-
Notifications
You must be signed in to change notification settings - Fork 110
api, cmd, prod, pss, swarm: get publisher and query feed for chunk repair request #2175
Changes from 18 commits
8c4e6a7
7a213dc
7132166
f25b463
64741c4
7b6ef91
ce6e347
ed7e691
934ac93
81954b3
0842aff
2c55d03
78c188b
467473c
3608343
9747df5
7e1b979
52dac5b
64de461
be58121
75d5d64
4c3fc93
b893bc3
652bc35
2a2b9df
9a659f9
948a3ae
ebe8b6a
768c063
699a28f
16ea955
d4e6e0b
8feeed5
bdb8c02
0a2fca2
faf7158
3019ebc
b67a103
97dc5cd
0250a5f
6195cf7
931b76e
272df48
7fc3df5
a28f1ff
e6f0099
470ee96
5c2f9f0
61c33d9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,44 +18,97 @@ package prod | |
|
||
import ( | ||
"context" | ||
"encoding/hex" | ||
"errors" | ||
"fmt" | ||
|
||
"github.com/ethereum/go-ethereum/crypto" | ||
"github.com/ethersphere/swarm/chunk" | ||
"github.com/ethersphere/swarm/pss" | ||
"github.com/ethersphere/swarm/pss/trojan" | ||
"github.com/ethersphere/swarm/storage/feed" | ||
"github.com/ethersphere/swarm/storage/feed/lookup" | ||
) | ||
|
||
// RecoveryHook defines code to be executed upon trigger of failed to be retrieved chunks | ||
type RecoveryHook func(ctx context.Context, chunkAddress chunk.Address) error | ||
// ErrPublisher is returned when the publisher string cannot be decoded into bytes | ||
var ErrPublisher = errors.New("failed to decode publisher") | ||
|
||
// sender is the function call for sending trojan chunks | ||
// ErrPubKey is returned when the publisher bytes cannot be decompressed as a public key | ||
var ErrPubKey = errors.New("failed to decompress public key") | ||
|
||
// ErrFeedLookup is returned when the recovery feed cannot be successefully looked up | ||
var ErrFeedLookup = errors.New("failed to look up recovery feed") | ||
|
||
// ErrFeedContent is returned when there is a failure to retrieve content from the recovery feed | ||
var ErrFeedContent = errors.New("failed to get content for recovery feed") | ||
|
||
// RecoveryHook defines code to be executed upon failing to retrieve pinned chunks | ||
type RecoveryHook func(ctx context.Context, chunkAddress chunk.Address, publisher string) error | ||
|
||
// sender is the function type for sending trojan chunks | ||
type sender func(ctx context.Context, targets [][]byte, topic trojan.Topic, payload []byte) (*pss.Monitor, error) | ||
|
||
// NewRecoveryHook returns a new RecoveryHook with the sender function defined | ||
func NewRecoveryHook(send sender) RecoveryHook { | ||
return func(ctx context.Context, chunkAddress chunk.Address) error { | ||
targets, err := getPinners(chunkAddress) | ||
func NewRecoveryHook(send sender, handler feed.GenericHandler) RecoveryHook { | ||
return func(ctx context.Context, chunkAddress chunk.Address, publisher string) error { | ||
targets, err := getPinners(publisher, handler) | ||
if err != nil { | ||
return err | ||
} | ||
payload := chunkAddress | ||
topic := trojan.NewTopic("RECOVERY") | ||
|
||
// TODO: monitor return should | ||
if _, err := send(ctx, targets, topic, payload); err != nil { | ||
if _, err := send(ctx, targets, trojan.RecoveryTopic, payload); err != nil { | ||
return err | ||
} | ||
return nil | ||
} | ||
} | ||
|
||
// TODO: refactor this method to implement feed of target pinners | ||
// getPinners returns the specific target pinners for a corresponding chunk address | ||
func getPinners(chunkAddress chunk.Address) ([][]byte, error) { | ||
//this should get the feed and return correct target of pinners | ||
return [][]byte{ | ||
{57, 120}, | ||
{209, 156}, | ||
{156, 38}, | ||
{89, 19}, | ||
{22, 129}}, nil | ||
// getPinners returns the specific target pinners for a corresponding chunk | ||
func getPinners(publisher string, handler feed.GenericHandler) ([][]byte, error) { | ||
// get feed user from publisher | ||
publisherBytes, err := hex.DecodeString(publisher) | ||
if err != nil { | ||
return nil, ErrPublisher | ||
} | ||
pubKey, err := crypto.DecompressPubkey(publisherBytes) | ||
if err != nil { | ||
return nil, ErrPubKey | ||
} | ||
addr := crypto.PubkeyToAddress(*pubKey) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not entirely sure these are the steps to go from an (assumed) byte string as the publisher, to the actual as clarification: this code was adapted from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. looks good but this should defo be done only once instead of publisher, the derived ether address should be put in the context value There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. agreed, registered in #2181 |
||
|
||
// get feed topic from trojan recovery topic | ||
topic, err := feed.NewTopic(trojan.RecoveryTopicText, nil) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. trojan pkg should not know anything about recovery There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. resolved here |
||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// read feed | ||
fd := feed.Feed{ | ||
Topic: topic, | ||
User: addr, | ||
} | ||
|
||
query := feed.NewQueryLatest(&fd, lookup.NoClue) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this always There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as of now, we do not have any idea when the last update to the feed happened, so this is why but i have added a note to #2181, we can further optimize it this way i think. thanks |
||
// TODO: do we need this ctx.WithCancel? why? | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i think you dont need cancel here but timeout and of course extending the parent context inherited from netstore There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. addressed |
||
defer cancel() | ||
|
||
// TODO: not exactly sure what to do with the `feed.cacheEntry` value returned here | ||
// TODO: in fact, not even sure if we need to call `Lookup` first before calling `GetContent` | ||
_, err = handler.Lookup(ctx, query) | ||
// feed can still be queried even if there are no updates | ||
if err != nil && err.Error() != "no feed updates found" { | ||
zelig marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return nil, fmt.Errorf("%s : %s", ErrFeedLookup, err) | ||
} | ||
|
||
// TODO: how do we handle time-outs here? | ||
_, content, err := handler.GetContent(&fd) | ||
if err != nil { | ||
return nil, fmt.Errorf("%s : %s", ErrFeedContent, err) | ||
} | ||
|
||
// TODO: transform content into actual list of targets | ||
return [][]byte{content}, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -45,6 +45,12 @@ const MaxPayloadSize = 4030 | |
|
||
var hashFunc = storage.MakeHashFunc(storage.BMTHash) | ||
|
||
// RecoveryTopicText is the string used to construct the RecoveryTopic var | ||
const RecoveryTopicText = "RECOVERY" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we need a separate string here as a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should not be here. trojan pkg not supposed to know about recovery There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks, moved this to |
||
|
||
// RecoveryTopic is the topic used for repairing globally pinned chunks | ||
var RecoveryTopic = NewTopic(RecoveryTopicText) | ||
|
||
// ErrPayloadTooBig is returned when a given payload for a Message type is longer than the maximum amount allowed | ||
var ErrPayloadTooBig = fmt.Errorf("message payload size cannot be greater than %d bytes", MaxPayloadSize) | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,6 +31,13 @@ import ( | |
"github.com/ethersphere/swarm/storage/feed/lookup" | ||
) | ||
|
||
// GenericHandler is an interface which specifies funcs any feeds handler should use | ||
type GenericHandler interface { | ||
Lookup(ctx context.Context, query *Query) (*cacheEntry, error) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is the signature as originally defined in the it seems strange to me that the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good point |
||
GetContent(feed *Feed) (storage.Address, []byte, error) | ||
} | ||
|
||
// Handler is the struct to be used as the API for feeds | ||
type Handler struct { | ||
chunkStore *storage.NetStore | ||
HashSize int | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -99,7 +99,7 @@ type NetStore struct { | |
requestGroup singleflight.Group | ||
RemoteGet RemoteGetFunc | ||
logger log.Logger | ||
recoveryCallback func(ctx context.Context, chunkAddress chunk.Address) error // this is the function callback when a chunk failed to be retrieved | ||
recoveryCallback func(ctx context.Context, chunkAddress chunk.Address, publisher string) error // this is the callback to be executed when a chunk fails to be retrieved | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it makes sense to extract from context within the recovery function There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. removed this param, expecting code to be pushed to extract this data from |
||
} | ||
|
||
// NewNetStore creates a new NetStore using the provided chunk.Store and localID of the node. | ||
|
@@ -167,7 +167,7 @@ func (n *NetStore) Close() error { | |
} | ||
|
||
// WithRecoveryCallback allows injecting a callback func on the NetStore struct | ||
func (n *NetStore) WithRecoveryCallback(f func(ctx context.Context, chunkAddress chunk.Address) error) *NetStore { | ||
func (n *NetStore) WithRecoveryCallback(f func(ctx context.Context, chunkAddress chunk.Address, publisher string) error) *NetStore { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see previous comment There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. resolution here |
||
n.recoveryCallback = f | ||
return n | ||
} | ||
|
@@ -201,7 +201,8 @@ func (n *NetStore) Get(ctx context.Context, mode chunk.ModeGet, req *Request) (c | |
ch, err = n.RemoteFetch(ctx, req, fi) | ||
if err != nil { | ||
if n.recoveryCallback != nil { | ||
n.recoveryCallback(ctx, ref) | ||
// TODO: get actual publisher, dummy for now | ||
n.recoveryCallback(ctx, ref, "0226f213613e843a413ad35b40f193910d26eb35f00154afcde9ded57479a6224a") | ||
time.Sleep(500 * time.Millisecond) // TODO: view what the ideal timeout is | ||
return n.RemoteFetch(ctx, req, fi) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, we need the monitor here, I would block this until either parent context is done or monitor shows synced request
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should be done in #2171