@cryptotaxi247 / kubo / commits / 36168542c

coreapi: implement dht api

License: MIT Signed-off-by: Łukasz Magiera <magik6k@gmail.com>

Łukasz Magiera committed Mar 10, 2018 at 19:12 UTC 36168542c9dbf4cdaab43a76b278b762a3c4d9c9
5 files changed +279 -1
core/coreapi/coreapi.go
+5
@@ -62,3 +62,8 @@ func (api *CoreAPI) Object() coreiface.ObjectAPI {
62 func (api *CoreAPI) Pin() coreiface.PinAPI {
63 return (*PinAPI)(api)
64 }
65 +
66 +// Dht returns the DhtAPI interface implementation backed by the go-ipfs node
67 +func (api *CoreAPI) Dht() coreiface.DhtAPI {
68 + return &DhtAPI{api, nil}
69 +}
core/coreapi/dht.go new
+240
@@ -0,0 +1,240 @@
1 +package coreapi
2 +
3 +import (
4 + "context"
5 + "errors"
6 + "fmt"
7 +
8 + coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
9 + caopts "github.com/ipfs/go-ipfs/core/coreapi/interface/options"
10 + dag "github.com/ipfs/go-ipfs/merkledag"
11 +
12 + routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing"
13 + notif "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing/notifications"
14 + ipdht "gx/ipfs/QmVSep2WwKcXxMonPASsAJ3nZVjfVMKgMcaSigxKnUWpJv/go-libp2p-kad-dht"
15 + ma "gx/ipfs/QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb/go-multiaddr"
16 + pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore"
17 + peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer"
18 + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid"
19 + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format"
20 +)
21 +
22 +var ErrNotDHT = errors.New("routing service is not a DHT")
23 +
24 +type DhtAPI struct {
25 + *CoreAPI
26 + *caopts.DhtOptions
27 +}
28 +
29 +func (api *DhtAPI) FindPeer(ctx context.Context, p peer.ID) (<-chan ma.Multiaddr, error) {
30 + dht, ok := api.node.Routing.(*ipdht.IpfsDHT)
31 + if !ok {
32 + return nil, ErrNotDHT
33 + }
34 +
35 + outChan := make(chan ma.Multiaddr)
36 + events := make(chan *notif.QueryEvent)
37 + ctx = notif.RegisterForQueryEvents(ctx, events)
38 +
39 + go func() {
40 + defer close(outChan)
41 +
42 + sendAddrs := func(responses []*pstore.PeerInfo) error {
43 + for _, response := range responses {
44 + for _, addr := range response.Addrs {
45 + select {
46 + case outChan <- addr:
47 + case <-ctx.Done():
48 + return ctx.Err()
49 + }
50 + }
51 + }
52 + return nil
53 + }
54 +
55 + for event := range events {
56 + if event.Type == notif.FinalPeer {
57 + err := sendAddrs(event.Responses)
58 + if err != nil {
59 + return
60 + }
61 + }
62 + }
63 + }()
64 +
65 + go func() {
66 + defer close(events)
67 + pi, err := dht.FindPeer(ctx, peer.ID(p))
68 + if err != nil {
69 + notif.PublishQueryEvent(ctx, &notif.QueryEvent{
70 + Type: notif.QueryError,
71 + Extra: err.Error(),
72 + })
73 + return
74 + }
75 +
76 + notif.PublishQueryEvent(ctx, &notif.QueryEvent{
77 + Type: notif.FinalPeer,
78 + Responses: []*pstore.PeerInfo{&pi},
79 + })
80 + }()
81 +
82 + return outChan, nil
83 +}
84 +
85 +func (api *DhtAPI) FindProviders(ctx context.Context, p coreiface.Path, opts ...caopts.DhtFindProvidersOption) (<-chan peer.ID, error) {
86 + settings, err := caopts.DhtFindProvidersOptions(opts...)
87 + if err != nil {
88 + return nil, err
89 + }
90 +
91 + dht, ok := api.node.Routing.(*ipdht.IpfsDHT)
92 + if !ok {
93 + return nil, ErrNotDHT
94 + }
95 +
96 + p, err = api.ResolvePath(ctx, p)
97 + if err != nil {
98 + return nil, err
99 + }
100 +
101 + c := p.Cid()
102 +
103 + numProviders := settings.NumProviders
104 + if numProviders < 1 {
105 + return nil, fmt.Errorf("number of providers must be greater than 0")
106 + }
107 +
108 + outChan := make(chan peer.ID)
109 + events := make(chan *notif.QueryEvent)
110 + ctx = notif.RegisterForQueryEvents(ctx, events)
111 +
112 + pchan := dht.FindProvidersAsync(ctx, c, numProviders)
113 + go func() {
114 + defer close(outChan)
115 +
116 + sendProviders := func(responses []*pstore.PeerInfo) error {
117 + for _, response := range responses {
118 + select {
119 + case outChan <- response.ID:
120 + case <-ctx.Done():
121 + return ctx.Err()
122 + }
123 + }
124 + return nil
125 + }
126 +
127 + for event := range events {
128 + if event.Type == notif.Provider {
129 + err := sendProviders(event.Responses)
130 + if err != nil {
131 + return
132 + }
133 + }
134 + }
135 + }()
136 +
137 + go func() {
138 + defer close(events)
139 + for p := range pchan {
140 + np := p
141 + notif.PublishQueryEvent(ctx, &notif.QueryEvent{
142 + Type: notif.Provider,
143 + Responses: []*pstore.PeerInfo{&np},
144 + })
145 + }
146 + }()
147 +
148 + return outChan, nil
149 +}
150 +
151 +func (api *DhtAPI) Provide(ctx context.Context, path coreiface.Path, opts ...caopts.DhtProvideOption) error {
152 + settings, err := caopts.DhtProvideOptions(opts...)
153 + if err != nil {
154 + return err
155 + }
156 +
157 + if api.node.Routing == nil {
158 + return errors.New("cannot provide in offline mode")
159 + }
160 +
161 + if len(api.node.PeerHost.Network().Conns()) == 0 {
162 + return errors.New("cannot provide, no connected peers")
163 + }
164 +
165 + c := path.Cid()
166 +
167 + has, err := api.node.Blockstore.Has(c)
168 + if err != nil {
169 + return err
170 + }
171 +
172 + if !has {
173 + return fmt.Errorf("block %s not found locally, cannot provide", c)
174 + }
175 +
176 + //TODO: either remove or use
177 + //outChan := make(chan interface{})
178 +
179 + //events := make(chan *notif.QueryEvent)
180 + //ctx = notif.RegisterForQueryEvents(ctx, events)
181 +
182 + /*go func() {
183 + defer close(outChan)
184 + for range events {
185 + select {
186 + case <-ctx.Done():
187 + return
188 + default:
189 + }
190 + }
191 + }()*/
192 +
193 + //defer close(events)
194 + if settings.Recursive {
195 + err = provideKeysRec(ctx, api.node.Routing, api.node.DAG, []*cid.Cid{c})
196 + } else {
197 + err = provideKeys(ctx, api.node.Routing, []*cid.Cid{c})
198 + }
199 + if err != nil {
200 + return err
201 + }
202 +
203 + return nil
204 +}
205 +
206 +func provideKeys(ctx context.Context, r routing.IpfsRouting, cids []*cid.Cid) error {
207 + for _, c := range cids {
208 + err := r.Provide(ctx, c, true)
209 + if err != nil {
210 + return err
211 + }
212 + }
213 + return nil
214 +}
215 +
216 +func provideKeysRec(ctx context.Context, r routing.IpfsRouting, dserv ipld.DAGService, cids []*cid.Cid) error {
217 + provided := cid.NewSet()
218 + for _, c := range cids {
219 + kset := cid.NewSet()
220 +
221 + err := dag.EnumerateChildrenAsync(ctx, dag.GetLinksDirect(dserv), c, kset.Visit)
222 + if err != nil {
223 + return err
224 + }
225 +
226 + for _, k := range kset.Keys() {
227 + if provided.Has(k) {
228 + continue
229 + }
230 +
231 + err = r.Provide(ctx, k, true)
232 + if err != nil {
233 + return err
234 + }
235 + provided.Add(k)
236 + }
237 + }
238 +
239 + return nil
240 +}
core/coreapi/interface/coreapi.go
+3
@@ -31,6 +31,9 @@ type CoreAPI interface {
31 // ObjectAPI returns an implementation of Object API
32 Object() ObjectAPI
33
34 + // Dht returns an implementation of Dht API
35 + Dht() DhtAPI
36 +
37 // ResolvePath resolves the path using Unixfs resolver
38 ResolvePath(context.Context, Path) (ResolvedPath, error)
39
core/coreapi/interface/dht.go
+5 -1
@@ -17,7 +17,11 @@ type DhtAPI interface {
17
18 // FindProviders finds peers in the DHT who can provide a specific value
19 // given a key.
20 - FindProviders(context.Context, Path) (<-chan peer.ID, error) //TODO: is path the right choice here?
20 + FindProviders(context.Context, Path, ...options.DhtFindProvidersOption) (<-chan peer.ID, error) //TODO: is path the right choice here?
21 +
22 + // WithNumProviders is an option for FindProviders which specifies the
23 + // number of peers to look for. Default is 20
24 + WithNumProviders(numProviders int) options.DhtFindProvidersOption
25
26 // Provide announces to the network that you are providing given values
27 Provide(context.Context, Path, ...options.DhtProvideOption) error
core/coreapi/interface/options/dht.go
+26
@@ -4,7 +4,12 @@ type DhtProvideSettings struct {
4 Recursive bool
5 }
6
7 +type DhtFindProvidersSettings struct {
8 + NumProviders int
9 +}
10 +
11 type DhtProvideOption func(*DhtProvideSettings) error
12 +type DhtFindProvidersOption func(*DhtFindProvidersSettings) error
13
14 func DhtProvideOptions(opts ...DhtProvideOption) (*DhtProvideSettings, error) {
15 options := &DhtProvideSettings{
@@ -20,6 +25,20 @@ func DhtProvideOptions(opts ...DhtProvideOption) (*DhtProvideSettings, error) {
25 return options, nil
26 }
27
28 +func DhtFindProvidersOptions(opts ...DhtFindProvidersOption) (*DhtFindProvidersSettings, error) {
29 + options := &DhtFindProvidersSettings{
30 + NumProviders: 20,
31 + }
32 +
33 + for _, opt := range opts {
34 + err := opt(options)
35 + if err != nil {
36 + return nil, err
37 + }
38 + }
39 + return options, nil
40 +}
41 +
42 type DhtOptions struct{}
43
44 func (api *DhtOptions) WithRecursive(recursive bool) DhtProvideOption {
@@ -28,3 +47,10 @@ func (api *DhtOptions) WithRecursive(recursive bool) DhtProvideOption {
47 return nil
48 }
49 }
50 +
51 +func (api *DhtOptions) WithNumProviders(numProviders int) DhtFindProvidersOption {
52 + return func(settings *DhtFindProvidersSettings) error {
53 + settings.NumProviders = numProviders
54 + return nil
55 + }
56 +}