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, ¬if.QueryEvent{
70
+ Type: notif.QueryError,
71
+ Extra: err.Error(),
72
+ })
73
+ return
74
+ }
75
+
76
+ notif.PublishQueryEvent(ctx, ¬if.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, ¬if.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
+}