1package discovery 2 3import ( 4 "context" 5 "github.com/libp2p/go-libp2p-core/discovery" 6 "time" 7 8 cid "github.com/ipfs/go-cid" 9 10 "github.com/libp2p/go-libp2p-core/peer" 11 "github.com/libp2p/go-libp2p-core/routing" 12 13 mh "github.com/multiformats/go-multihash" 14) 15 16// RoutingDiscovery is an implementation of discovery using ContentRouting. 17// Namespaces are translated to Cids using the SHA256 hash. 18type RoutingDiscovery struct { 19 routing.ContentRouting 20} 21 22func NewRoutingDiscovery(router routing.ContentRouting) *RoutingDiscovery { 23 return &RoutingDiscovery{router} 24} 25 26func (d *RoutingDiscovery) Advertise(ctx context.Context, ns string, opts ...Option) (time.Duration, error) { 27 var options Options 28 err := options.Apply(opts...) 29 if err != nil { 30 return 0, err 31 } 32 33 ttl := options.Ttl 34 if ttl == 0 || ttl > 3*time.Hour { 35 // the DHT provider record validity is 24hrs, but it is recommnded to republish at least every 6hrs 36 // we go one step further and republish every 3hrs 37 ttl = 3 * time.Hour 38 } 39 40 cid, err := nsToCid(ns) 41 if err != nil { 42 return 0, err 43 } 44 45 // this context requires a timeout; it determines how long the DHT looks for 46 // closest peers to the key/CID before it goes on to provide the record to them. 47 // Not setting a timeout here will make the DHT wander forever. 48 pctx, cancel := context.WithTimeout(ctx, 60*time.Second) 49 defer cancel() 50 51 err = d.Provide(pctx, cid, true) 52 if err != nil { 53 return 0, err 54 } 55 56 return ttl, nil 57} 58 59func (d *RoutingDiscovery) FindPeers(ctx context.Context, ns string, opts ...Option) (<-chan peer.AddrInfo, error) { 60 var options Options 61 err := options.Apply(opts...) 62 if err != nil { 63 return nil, err 64 } 65 66 limit := options.Limit 67 if limit == 0 { 68 limit = 100 // that's just arbitrary, but FindProvidersAsync needs a count 69 } 70 71 cid, err := nsToCid(ns) 72 if err != nil { 73 return nil, err 74 } 75 76 return d.FindProvidersAsync(ctx, cid, limit), nil 77} 78 79func nsToCid(ns string) (cid.Cid, error) { 80 h, err := mh.Sum([]byte(ns), mh.SHA2_256, -1) 81 if err != nil { 82 return cid.Undef, err 83 } 84 85 return cid.NewCidV1(cid.Raw, h), nil 86} 87 88func NewDiscoveryRouting(disc discovery.Discovery, opts ...discovery.Option) *DiscoveryRouting { 89 return &DiscoveryRouting{disc, opts} 90} 91 92type DiscoveryRouting struct { 93 discovery.Discovery 94 opts []discovery.Option 95} 96 97func (r *DiscoveryRouting) Provide(ctx context.Context, c cid.Cid, bcast bool) error { 98 if !bcast { 99 return nil 100 } 101 102 _, err := r.Advertise(ctx, cidToNs(c), r.opts...) 103 return err 104} 105 106func (r *DiscoveryRouting) FindProvidersAsync(ctx context.Context, c cid.Cid, limit int) <-chan peer.AddrInfo { 107 ch, _ := r.FindPeers(ctx, cidToNs(c), append([]discovery.Option{discovery.Limit(limit)}, r.opts...)...) 108 return ch 109} 110 111func cidToNs(c cid.Cid) string { 112 return "/provider/" + c.String() 113} 114