diff --git a/.gx/lastpubver b/.gx/lastpubver index b41fa5d..c8a88fc 100644 --- a/.gx/lastpubver +++ b/.gx/lastpubver @@ -1 +1 @@ -2.2.22: QmZix3EdeAdc4wnRksRXWEQ6kbqiFAP16h3Sq9JnEiP71N +2.3.0: QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz diff --git a/options/options.go b/options/options.go new file mode 100644 index 0000000..e0c9a52 --- /dev/null +++ b/options/options.go @@ -0,0 +1,50 @@ +package ropts + +// Option is a single routing option. +type Option func(opts *Options) error + +// Options is a set of routing options +type Options struct { + // Allow expired values. + Expired bool + Offline bool + // Other (ValueStore implementation specific) options. + Other map[interface{}]interface{} +} + +// Apply applies the given options to this Options +func (opts *Options) Apply(options ...Option) error { + for _, o := range options { + if err := o(opts); err != nil { + return err + } + } + return nil +} + +// ToOption converts this Options to a single Option. +func (opts *Options) ToOption() Option { + return func(nopts *Options) error { + *nopts = *opts + if opts.Other != nil { + nopts.Other = make(map[interface{}]interface{}, len(opts.Other)) + for k, v := range opts.Other { + nopts.Other[k] = v + } + } + return nil + } +} + +// Expired is an option that tells the routing system to return expired records +// when no newer records are known. +var Expired Option = func(opts *Options) error { + opts.Expired = true + return nil +} + +// Offline is an option that tells the routing system to operate offline (i.e., rely on cached/local data only). +var Offline Option = func(opts *Options) error { + opts.Offline = true + return nil +} diff --git a/package.json b/package.json index c3395f3..c33be9c 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,6 @@ "license": "MIT", "name": "go-libp2p-routing", "releaseCmd": "git commit -a -m \"gx publish $VERSION\"", - "version": "2.2.22" + "version": "2.3.0" } diff --git a/routing.go b/routing.go index 49f699c..33fe13b 100644 --- a/routing.go +++ b/routing.go @@ -5,6 +5,8 @@ import ( "context" "errors" + ropts "github.com/libp2p/go-libp2p-routing/options" + cid "github.com/ipfs/go-cid" ci "github.com/libp2p/go-libp2p-crypto" peer "github.com/libp2p/go-libp2p-peer" @@ -14,6 +16,10 @@ import ( // ErrNotFound is returned when a search fails to find anything var ErrNotFound = errors.New("routing: not found") +// ErrNotSupported is returned when a search or put fails because the key type +// isn't supported. +var ErrNotSupported = errors.New("routing: operation or key not supported") + // ContentRouting is a value provider layer of indirection. It is used to find // information about who has what content. type ContentRouting interface { @@ -36,26 +42,23 @@ type PeerRouting interface { FindPeer(context.Context, peer.ID) (pstore.PeerInfo, error) } +// ValueStore is a basic Put/Get interface. type ValueStore interface { - // Basic Put/Get // PutValue adds value corresponding to given Key. - PutValue(context.Context, string, []byte) error + PutValue(context.Context, string, []byte, ...ropts.Option) error // GetValue searches for the value corresponding to given Key. - GetValue(context.Context, string) ([]byte, error) + GetValue(context.Context, string, ...ropts.Option) ([]byte, error) - // GetValues searches for values corresponding to given Key. - // - // Passing a value of '0' for the count argument will cause the - // routing interface to return values only from cached or local storage - // and return an error if no cached value is found. + // TODO + // SearchValue searches for better and better values from this value + // store corresponding to the given Key. Implementations may halt the + // search after a period of time or may continue searching indefinitely. // - // Passing a value of '1' will return a local value if found, and query - // the network for the first value it finds otherwise. - // As a result, a value of '1' is mostly useful for cases where the record - // in question has only one valid value (such as public keys) - GetValues(c context.Context, k string, count int) ([]RecvdVal, error) + // Useful when you want a result *now* but still want to hear about + // better/newer results. + //SearchValue(context.Context, string, ...ropts.Option) (<-chan []byte, error) } // IpfsRouting is the combination of different routing types that ipfs @@ -73,35 +76,46 @@ type IpfsRouting interface { // TODO expose io.Closer or plain-old Close error } -// RecvdVal represents a dht value record that has been received from a given peer -// it is used to track peers with expired records in order to correct them. -type RecvdVal struct { - From peer.ID - Val []byte -} - +// PubKeyFetcher is an interfaces that should be implemented by value stores +// that can optimize retrieval of public keys. +// +// TODO(steb): Consider removing, see #22. type PubKeyFetcher interface { + // GetPublicKey returns the public key for the given peer. GetPublicKey(context.Context, peer.ID) (ci.PubKey, error) } // KeyForPublicKey returns the key used to retrieve public keys -// from the dht. +// from a value store. func KeyForPublicKey(id peer.ID) string { return "/pk/" + string(id) } -func GetPublicKey(r ValueStore, ctx context.Context, pkhash []byte) (ci.PubKey, error) { +// GetPublicKey retrieves the public key associated with the given peer ID from +// the value store. +// +// If the ValueStore is also a PubKeyFetcher, this method will call GetPublicKey +// (which may be better optimized) instead of GetValue. +func GetPublicKey(r ValueStore, ctx context.Context, p peer.ID) (ci.PubKey, error) { + k, err := p.ExtractPublicKey() + if err != nil { + // An error means that the peer ID is invalid. + return nil, err + } + if k != nil { + return k, nil + } + if dht, ok := r.(PubKeyFetcher); ok { // If we have a DHT as our routing system, use optimized fetcher - return dht.GetPublicKey(ctx, peer.ID(pkhash)) - } else { - key := "/pk/" + string(pkhash) - pkval, err := r.GetValue(ctx, key) - if err != nil { - return nil, err - } - - // get PublicKey from node.Data - return ci.UnmarshalPublicKey(pkval) + return dht.GetPublicKey(ctx, p) } + key := KeyForPublicKey(p) + pkval, err := r.GetValue(ctx, key) + if err != nil { + return nil, err + } + + // get PublicKey from node.Data + return ci.UnmarshalPublicKey(pkval) }