remove supernode routing
It was never fully implemented and isn't used. fixes #3950 (not removing routing/mock because that *is* in use). License: MIT Signed-off-by: Steven Allen <steven@stebalien.com>
Steven Allen committed
Oct 12, 2017 at 19:39 UTC
93f3117d7da0fd16616253eeacb81e3cb85ec8af
10 files changed
+2
-1133
cmd/ipfs/daemon.go
+2
-17
@@ -16,12 +16,10 @@ import (
16
commands "github.com/ipfs/go-ipfs/core/commands"
17
corehttp "github.com/ipfs/go-ipfs/core/corehttp"
18
corerepo "github.com/ipfs/go-ipfs/core/corerepo"
19
- "github.com/ipfs/go-ipfs/core/corerouting"
19
nodeMount "github.com/ipfs/go-ipfs/fuse/node"
20
fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
21
migrate "github.com/ipfs/go-ipfs/repo/fsrepo/migrations"
22
24
- pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore"
23
mprome "gx/ipfs/QmSk46nSD78YiuNojYMS8NW6hSCjH95JajqqzzoychZgef/go-metrics-prometheus"
24
"gx/ipfs/QmX3QZ5jHEPidwUrymXV1iSCSUhdGxj15sm2gP4jKMef7B/client_golang/prometheus"
25
"gx/ipfs/QmX3U3YXCQ6UYBxq2LVWF8dARS1hPUTEYLrSx654Qyxyw6/go-multiaddr-net"
@@ -304,21 +302,8 @@ func daemonFunc(req cmds.Request, res cmds.Response) {
302
}
303
switch routingOption {
304
case routingOptionSupernodeKwd:
307
- servers, err := cfg.SupernodeRouting.ServerIPFSAddrs()
308
- if err != nil {
309
- res.SetError(err, cmds.ErrNormal)
310
- repo.Close() // because ownership hasn't been transferred to the node
311
- return
312
- }
313
- var infos []pstore.PeerInfo
314
- for _, addr := range servers {
315
- infos = append(infos, pstore.PeerInfo{
316
- ID: addr.ID(),
317
- Addrs: []ma.Multiaddr{addr.Transport()},
318
- })
319
- }
320
-
321
- ncfg.Routing = corerouting.SupernodeClient(infos...)
305
+ res.SetError(errors.New("supernode routing was never fully implemented and has been removed"), cmds.ErrNormal)
306
+ return
307
case routingOptionDHTClientKwd:
308
ncfg.Routing = core.DHTClientOption
309
case routingOptionDHTKwd:
core/corerouting/core.go
deleted
-52
@@ -1,52 +0,0 @@
1
-package corerouting
2
-
3
-import (
4
- "errors"
5
-
6
- context "context"
7
- core "github.com/ipfs/go-ipfs/core"
8
- repo "github.com/ipfs/go-ipfs/repo"
9
- supernode "github.com/ipfs/go-ipfs/routing/supernode"
10
- gcproxy "github.com/ipfs/go-ipfs/routing/supernode/proxy"
11
- routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing"
12
- pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore"
13
- ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore"
14
- "gx/ipfs/QmaSxYRuMq4pkpBBG2CYaRrPx2z7NmMVEs34b9g61biQA6/go-libp2p-host"
15
-)
16
-
17
-// NB: DHT option is included in the core to avoid 1) because it's a sane
18
-// default and 2) to avoid a circular dependency (it needs to be referenced in
19
-// the core if it's going to be the default)
20
-
21
-var errServersMissing = errors.New("supernode routing client requires at least 1 server peer")
22
-
23
-// SupernodeServer returns a configuration for a routing server that stores
24
-// routing records to the provided datastore. Only routing records are store in
25
-// the datastore.
26
-func SupernodeServer(recordSource ds.Datastore) core.RoutingOption {
27
- return func(ctx context.Context, ph host.Host, dstore repo.Datastore) (routing.IpfsRouting, error) {
28
- server, err := supernode.NewServer(recordSource, ph.Peerstore(), ph.ID())
29
- if err != nil {
30
- return nil, err
31
- }
32
- proxy := &gcproxy.Loopback{
33
- Handler: server,
34
- Local: ph.ID(),
35
- }
36
- ph.SetStreamHandler(gcproxy.ProtocolSNR, proxy.HandleStream)
37
- return supernode.NewClient(proxy, ph, ph.Peerstore(), ph.ID())
38
- }
39
-}
40
-
41
-// TODO doc
42
-func SupernodeClient(remotes ...pstore.PeerInfo) core.RoutingOption {
43
- return func(ctx context.Context, ph host.Host, dstore repo.Datastore) (routing.IpfsRouting, error) {
44
- if len(remotes) < 1 {
45
- return nil, errServersMissing
46
- }
47
-
48
- proxy := gcproxy.Standard(ph, remotes)
49
- ph.SetStreamHandler(gcproxy.ProtocolSNR, proxy.HandleStream)
50
- return supernode.NewClient(proxy, ph, ph.Peerstore(), ph.ID())
51
- }
52
-}
routing/supernode/client.go
deleted
-164
@@ -1,164 +0,0 @@
1
-package supernode
2
-
3
-import (
4
- "bytes"
5
- "context"
6
- "errors"
7
- "time"
8
-
9
- proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy"
10
-
11
- cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid"
12
- routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing"
13
- pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore"
14
- logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log"
15
- loggables "gx/ipfs/QmT4PgCNdv73hnFAqzHqwW44q7M9PWpykSswHDxndquZbc/go-libp2p-loggables"
16
- dhtpb "gx/ipfs/QmT7PnPxYkeKPCG8pAnucfcjrXc15Q7FgvFv7YC24EPrw8/go-libp2p-kad-dht/pb"
17
- peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer"
18
- proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto"
19
- "gx/ipfs/QmaSxYRuMq4pkpBBG2CYaRrPx2z7NmMVEs34b9g61biQA6/go-libp2p-host"
20
- pb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb"
21
-)
22
-
23
-var log = logging.Logger("supernode")
24
-
25
-type Client struct {
26
- peerhost host.Host
27
- peerstore pstore.Peerstore
28
- proxy proxy.Proxy
29
- local peer.ID
30
-}
31
-
32
-// TODO take in datastore/cache
33
-func NewClient(px proxy.Proxy, h host.Host, ps pstore.Peerstore, local peer.ID) (*Client, error) {
34
- return &Client{
35
- proxy: px,
36
- local: local,
37
- peerstore: ps,
38
- peerhost: h,
39
- }, nil
40
-}
41
-
42
-func (c *Client) FindProvidersAsync(ctx context.Context, k *cid.Cid, max int) <-chan pstore.PeerInfo {
43
- logging.ContextWithLoggable(ctx, loggables.Uuid("findProviders"))
44
- defer log.EventBegin(ctx, "findProviders", k).Done()
45
- ch := make(chan pstore.PeerInfo)
46
- go func() {
47
- defer close(ch)
48
- request := dhtpb.NewMessage(dhtpb.Message_GET_PROVIDERS, k.KeyString(), 0)
49
- response, err := c.proxy.SendRequest(ctx, request)
50
- if err != nil {
51
- log.Debug(err)
52
- return
53
- }
54
- for _, p := range dhtpb.PBPeersToPeerInfos(response.GetProviderPeers()) {
55
- select {
56
- case <-ctx.Done():
57
- log.Debug(ctx.Err())
58
- return
59
- case ch <- *p:
60
- }
61
- }
62
- }()
63
- return ch
64
-}
65
-
66
-func (c *Client) PutValue(ctx context.Context, k string, v []byte) error {
67
- defer log.EventBegin(ctx, "putValue").Done()
68
- r, err := makeRecord(c.peerstore, c.local, k, v)
69
- if err != nil {
70
- return err
71
- }
72
- pmes := dhtpb.NewMessage(dhtpb.Message_PUT_VALUE, string(k), 0)
73
- pmes.Record = r
74
- return c.proxy.SendMessage(ctx, pmes) // wrap to hide the remote
75
-}
76
-
77
-func (c *Client) GetValue(ctx context.Context, k string) ([]byte, error) {
78
- defer log.EventBegin(ctx, "getValue").Done()
79
- msg := dhtpb.NewMessage(dhtpb.Message_GET_VALUE, string(k), 0)
80
- response, err := c.proxy.SendRequest(ctx, msg) // TODO wrap to hide the remote
81
- if err != nil {
82
- return nil, err
83
- }
84
- return response.Record.GetValue(), nil
85
-}
86
-
87
-func (c *Client) GetValues(ctx context.Context, k string, _ int) ([]routing.RecvdVal, error) {
88
- defer log.EventBegin(ctx, "getValue").Done()
89
- msg := dhtpb.NewMessage(dhtpb.Message_GET_VALUE, string(k), 0)
90
- response, err := c.proxy.SendRequest(ctx, msg) // TODO wrap to hide the remote
91
- if err != nil {
92
- return nil, err
93
- }
94
-
95
- return []routing.RecvdVal{
96
- {
97
- Val: response.Record.GetValue(),
98
- From: c.local,
99
- },
100
- }, nil
101
-}
102
-
103
-// Provide adds the given key 'k' to the content routing system. If 'brd' is
104
-// true, it announces that content to the network. For the supernode client,
105
-// setting 'brd' to false makes this call a no-op
106
-func (c *Client) Provide(ctx context.Context, k *cid.Cid, brd bool) error {
107
- if !brd {
108
- return nil
109
- }
110
- defer log.EventBegin(ctx, "provide", k).Done()
111
- msg := dhtpb.NewMessage(dhtpb.Message_ADD_PROVIDER, k.KeyString(), 0)
112
- // FIXME how is connectedness defined for the local node
113
- pri := []dhtpb.PeerRoutingInfo{
114
- {
115
- PeerInfo: pstore.PeerInfo{
116
- ID: c.local,
117
- Addrs: c.peerhost.Addrs(),
118
- },
119
- },
120
- }
121
- msg.ProviderPeers = dhtpb.PeerRoutingInfosToPBPeers(pri)
122
- return c.proxy.SendMessage(ctx, msg) // TODO wrap to hide remote
123
-}
124
-
125
-func (c *Client) FindPeer(ctx context.Context, id peer.ID) (pstore.PeerInfo, error) {
126
- defer log.EventBegin(ctx, "findPeer", id).Done()
127
- request := dhtpb.NewMessage(dhtpb.Message_FIND_NODE, string(id), 0)
128
- response, err := c.proxy.SendRequest(ctx, request) // hide remote
129
- if err != nil {
130
- return pstore.PeerInfo{}, err
131
- }
132
- for _, p := range dhtpb.PBPeersToPeerInfos(response.GetCloserPeers()) {
133
- if p.ID == id {
134
- return *p, nil
135
- }
136
- }
137
- return pstore.PeerInfo{}, errors.New("could not find peer")
138
-}
139
-
140
-// creates and signs a record for the given key/value pair
141
-func makeRecord(ps pstore.Peerstore, p peer.ID, k string, v []byte) (*pb.Record, error) {
142
- blob := bytes.Join([][]byte{[]byte(k), v, []byte(p)}, []byte{})
143
- sig, err := ps.PrivKey(p).Sign(blob)
144
- if err != nil {
145
- return nil, err
146
- }
147
- return &pb.Record{
148
- Key: proto.String(string(k)),
149
- Value: v,
150
- Author: proto.String(string(p)),
151
- Signature: sig,
152
- }, nil
153
-}
154
-
155
-func (c *Client) Ping(ctx context.Context, id peer.ID) (time.Duration, error) {
156
- defer log.EventBegin(ctx, "ping", id).Done()
157
- return time.Nanosecond, errors.New("supernode routing does not support the ping method")
158
-}
159
-
160
-func (c *Client) Bootstrap(ctx context.Context) error {
161
- return c.proxy.Bootstrap(ctx)
162
-}
163
-
164
-var _ routing.IpfsRouting = &Client{}
routing/supernode/proxy/loopback.go
deleted
-59
@@ -1,59 +0,0 @@
1
-package proxy
2
-
3
-import (
4
- context "context"
5
- inet "gx/ipfs/QmNa31VPzC561NWwRsJLE7nGYZYuuD2QfpK2b1q9BK54J1/go-libp2p-net"
6
- dhtpb "gx/ipfs/QmT7PnPxYkeKPCG8pAnucfcjrXc15Q7FgvFv7YC24EPrw8/go-libp2p-kad-dht/pb"
7
- peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer"
8
- ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io"
9
-)
10
-
11
-// RequestHandler handles routing requests locally
12
-type RequestHandler interface {
13
- HandleRequest(ctx context.Context, p peer.ID, m *dhtpb.Message) *dhtpb.Message
14
-}
15
-
16
-// Loopback forwards requests to a local handler
17
-type Loopback struct {
18
- Handler RequestHandler
19
- Local peer.ID
20
-}
21
-
22
-func (_ *Loopback) Bootstrap(ctx context.Context) error {
23
- return nil
24
-}
25
-
26
-// SendMessage intercepts local requests, forwarding them to a local handler
27
-func (lb *Loopback) SendMessage(ctx context.Context, m *dhtpb.Message) error {
28
- response := lb.Handler.HandleRequest(ctx, lb.Local, m)
29
- if response != nil {
30
- log.Warning("loopback handler returned unexpected message")
31
- }
32
- return nil
33
-}
34
-
35
-// SendRequest intercepts local requests, forwarding them to a local handler
36
-func (lb *Loopback) SendRequest(ctx context.Context, m *dhtpb.Message) (*dhtpb.Message, error) {
37
- return lb.Handler.HandleRequest(ctx, lb.Local, m), nil
38
-}
39
-
40
-func (lb *Loopback) HandleStream(s inet.Stream) {
41
- defer s.Close()
42
- pbr := ggio.NewDelimitedReader(s, inet.MessageSizeMax)
43
- var incoming dhtpb.Message
44
- if err := pbr.ReadMsg(&incoming); err != nil {
45
- s.Reset()
46
- log.Debug(err)
47
- return
48
- }
49
- ctx := context.TODO()
50
- outgoing := lb.Handler.HandleRequest(ctx, s.Conn().RemotePeer(), &incoming)
51
-
52
- pbw := ggio.NewDelimitedWriter(s)
53
-
54
- if err := pbw.WriteMsg(outgoing); err != nil {
55
- s.Reset()
56
- log.Debug(err)
57
- return
58
- }
59
-}
routing/supernode/proxy/standard.go
deleted
-174
@@ -1,174 +0,0 @@
1
-package proxy
2
-
3
-import (
4
- "context"
5
- "errors"
6
-
7
- inet "gx/ipfs/QmNa31VPzC561NWwRsJLE7nGYZYuuD2QfpK2b1q9BK54J1/go-libp2p-net"
8
- pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore"
9
- kbucket "gx/ipfs/QmSAFA8v42u4gpJNy1tb7vW3JiiXiaYDC2b845c2RnNSJL/go-libp2p-kbucket"
10
- logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log"
11
- loggables "gx/ipfs/QmT4PgCNdv73hnFAqzHqwW44q7M9PWpykSswHDxndquZbc/go-libp2p-loggables"
12
- dhtpb "gx/ipfs/QmT7PnPxYkeKPCG8pAnucfcjrXc15Q7FgvFv7YC24EPrw8/go-libp2p-kad-dht/pb"
13
- peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer"
14
- ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io"
15
- host "gx/ipfs/QmaSxYRuMq4pkpBBG2CYaRrPx2z7NmMVEs34b9g61biQA6/go-libp2p-host"
16
-)
17
-
18
-const ProtocolSNR = "/ipfs/supernoderouting"
19
-
20
-var log = logging.Logger("supernode/proxy")
21
-
22
-type Proxy interface {
23
- Bootstrap(context.Context) error
24
- HandleStream(inet.Stream)
25
- SendMessage(ctx context.Context, m *dhtpb.Message) error
26
- SendRequest(ctx context.Context, m *dhtpb.Message) (*dhtpb.Message, error)
27
-}
28
-
29
-type standard struct {
30
- Host host.Host
31
-
32
- remoteInfos []pstore.PeerInfo // addr required for bootstrapping
33
- remoteIDs []peer.ID // []ID is required for each req. here, cached for performance.
34
-}
35
-
36
-func Standard(h host.Host, remotes []pstore.PeerInfo) Proxy {
37
- var ids []peer.ID
38
- for _, remote := range remotes {
39
- ids = append(ids, remote.ID)
40
- }
41
- return &standard{h, remotes, ids}
42
-}
43
-
44
-func (px *standard) Bootstrap(ctx context.Context) error {
45
- var cxns []pstore.PeerInfo
46
- for _, info := range px.remoteInfos {
47
- if err := px.Host.Connect(ctx, info); err != nil {
48
- continue
49
- }
50
- cxns = append(cxns, info)
51
- }
52
- if len(cxns) == 0 {
53
- log.Error("unable to bootstrap to any supernode routers")
54
- } else {
55
- log.Infof("bootstrapped to %d supernode routers: %s", len(cxns), cxns)
56
- }
57
- return nil
58
-}
59
-
60
-func (p *standard) HandleStream(s inet.Stream) {
61
- // TODO(brian): Should clients be able to satisfy requests?
62
- log.Error("supernode client received (dropped) a routing message from", s.Conn().RemotePeer())
63
- s.Reset()
64
-}
65
-
66
-const replicationFactor = 2
67
-
68
-// SendMessage sends message to each remote sequentially (randomized order),
69
-// stopping after the first successful response. If all fail, returns the last
70
-// error.
71
-func (px *standard) SendMessage(ctx context.Context, m *dhtpb.Message) error {
72
- var err error
73
- var numSuccesses int
74
- for _, remote := range sortedByKey(px.remoteIDs, m.GetKey()) {
75
- if err = px.sendMessage(ctx, m, remote); err != nil { // careful don't re-declare err!
76
- continue
77
- }
78
- numSuccesses++
79
- switch m.GetType() {
80
- case dhtpb.Message_ADD_PROVIDER, dhtpb.Message_PUT_VALUE:
81
- if numSuccesses < replicationFactor {
82
- continue
83
- }
84
- }
85
- return nil // success
86
- }
87
- return err // NB: returns the last error
88
-}
89
-
90
-func (px *standard) sendMessage(ctx context.Context, m *dhtpb.Message, remote peer.ID) (err error) {
91
- e := log.EventBegin(ctx, "sendRoutingMessage", px.Host.ID(), remote, m)
92
- defer func() {
93
- if err != nil {
94
- e.SetError(err)
95
- }
96
- e.Done()
97
- }()
98
- if err = px.Host.Connect(ctx, pstore.PeerInfo{ID: remote}); err != nil {
99
- return err
100
- }
101
- s, err := px.Host.NewStream(ctx, remote, ProtocolSNR)
102
- if err != nil {
103
- return err
104
- }
105
- pbw := ggio.NewDelimitedWriter(s)
106
-
107
- err = pbw.WriteMsg(m)
108
- if err == nil {
109
- s.Close()
110
- } else {
111
- s.Reset()
112
- }
113
- return err
114
-}
115
-
116
-// SendRequest sends the request to each remote sequentially (randomized order),
117
-// stopping after the first successful response. If all fail, returns the last
118
-// error.
119
-func (px *standard) SendRequest(ctx context.Context, m *dhtpb.Message) (*dhtpb.Message, error) {
120
- var err error
121
- for _, remote := range sortedByKey(px.remoteIDs, m.GetKey()) {
122
- var reply *dhtpb.Message
123
- reply, err = px.sendRequest(ctx, m, remote) // careful don't redeclare err!
124
- if err != nil {
125
- continue
126
- }
127
- return reply, nil // success
128
- }
129
- return nil, err // NB: returns the last error
130
-}
131
-
132
-func (px *standard) sendRequest(ctx context.Context, m *dhtpb.Message, remote peer.ID) (*dhtpb.Message, error) {
133
- e := log.EventBegin(ctx, "sendRoutingRequest", px.Host.ID(), remote, logging.Pair("request", m))
134
- defer e.Done()
135
- if err := px.Host.Connect(ctx, pstore.PeerInfo{ID: remote}); err != nil {
136
- e.SetError(err)
137
- return nil, err
138
- }
139
- s, err := px.Host.NewStream(ctx, remote, ProtocolSNR)
140
- if err != nil {
141
- e.SetError(err)
142
- return nil, err
143
- }
144
- defer s.Close()
145
- r := ggio.NewDelimitedReader(s, inet.MessageSizeMax)
146
- w := ggio.NewDelimitedWriter(s)
147
- if err = w.WriteMsg(m); err != nil {
148
- s.Reset()
149
- e.SetError(err)
150
- return nil, err
151
- }
152
-
153
- response := &dhtpb.Message{}
154
- if err = r.ReadMsg(response); err != nil {
155
- s.Reset()
156
- e.SetError(err)
157
- return nil, err
158
- }
159
- // need ctx expiration?
160
- if response == nil {
161
- s.Reset()
162
- err := errors.New("no response to request")
163
- e.SetError(err)
164
- return nil, err
165
- }
166
- e.Append(logging.Pair("response", response))
167
- e.Append(logging.Pair("uuid", loggables.Uuid("foo")))
168
- return response, nil
169
-}
170
-
171
-func sortedByKey(peers []peer.ID, skey string) []peer.ID {
172
- target := kbucket.ConvertKey(skey)
173
- return kbucket.SortClosestPeers(peers, target)
174
-}
routing/supernode/server.go
deleted
-202
@@ -1,202 +0,0 @@
1
-package supernode
2
-
3
-import (
4
- "context"
5
- "errors"
6
- "fmt"
7
-
8
- proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy"
9
- dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help"
10
-
11
- pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore"
12
- dhtpb "gx/ipfs/QmT7PnPxYkeKPCG8pAnucfcjrXc15Q7FgvFv7YC24EPrw8/go-libp2p-kad-dht/pb"
13
- datastore "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore"
14
- peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer"
15
- proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto"
16
- pb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb"
17
-)
18
-
19
-// Server handles routing queries using a database backend
20
-type Server struct {
21
- local peer.ID
22
- routingBackend datastore.Datastore
23
- peerstore pstore.Peerstore
24
- *proxy.Loopback // so server can be injected into client
25
-}
26
-
27
-// NewServer creates a new Supernode routing Server
28
-func NewServer(ds datastore.Datastore, ps pstore.Peerstore, local peer.ID) (*Server, error) {
29
- s := &Server{local, ds, ps, nil}
30
- s.Loopback = &proxy.Loopback{
31
- Handler: s,
32
- Local: local,
33
- }
34
- return s, nil
35
-}
36
-
37
-func (_ *Server) Bootstrap(ctx context.Context) error {
38
- return nil
39
-}
40
-
41
-// HandleLocalRequest implements the proxy.RequestHandler interface. This is
42
-// where requests are received from the outside world.
43
-func (s *Server) HandleRequest(ctx context.Context, p peer.ID, req *dhtpb.Message) *dhtpb.Message {
44
- _, response := s.handleMessage(ctx, p, req) // ignore response peer. it's local.
45
- return response
46
-}
47
-
48
-func (s *Server) handleMessage(
49
- ctx context.Context, p peer.ID, req *dhtpb.Message) (peer.ID, *dhtpb.Message) {
50
-
51
- defer log.EventBegin(ctx, "routingMessageReceived", req, p).Done()
52
-
53
- var response = dhtpb.NewMessage(req.GetType(), req.GetKey(), req.GetClusterLevel())
54
- switch req.GetType() {
55
-
56
- case dhtpb.Message_GET_VALUE:
57
- rawRecord, err := getRoutingRecord(s.routingBackend, req.GetKey())
58
- if err != nil {
59
- return "", nil
60
- }
61
- response.Record = rawRecord
62
- return p, response
63
-
64
- case dhtpb.Message_PUT_VALUE:
65
- // FIXME: verify complains that the peer's ID is not present in the
66
- // peerstore. Mocknet problem?
67
- // if err := verify(s.peerstore, req.GetRecord()); err != nil {
68
- // log.Event(ctx, "validationFailed", req, p)
69
- // return "", nil
70
- // }
71
- putRoutingRecord(s.routingBackend, req.GetKey(), req.GetRecord())
72
- return p, req
73
-
74
- case dhtpb.Message_FIND_NODE:
75
- p := s.peerstore.PeerInfo(peer.ID(req.GetKey()))
76
- pri := []dhtpb.PeerRoutingInfo{
77
- {
78
- PeerInfo: p,
79
- // Connectedness: TODO
80
- },
81
- }
82
- response.CloserPeers = dhtpb.PeerRoutingInfosToPBPeers(pri)
83
- return p.ID, response
84
-
85
- case dhtpb.Message_ADD_PROVIDER:
86
- for _, provider := range req.GetProviderPeers() {
87
- providerID := peer.ID(provider.GetId())
88
- if providerID == p {
89
- store := []*dhtpb.Message_Peer{provider}
90
- storeProvidersToPeerstore(s.peerstore, p, store)
91
- if err := putRoutingProviders(s.routingBackend, req.GetKey(), store); err != nil {
92
- return "", nil
93
- }
94
- } else {
95
- log.Event(ctx, "addProviderBadRequest", p, req)
96
- }
97
- }
98
- return "", nil
99
-
100
- case dhtpb.Message_GET_PROVIDERS:
101
- providers, err := getRoutingProviders(s.routingBackend, req.GetKey())
102
- if err != nil {
103
- return "", nil
104
- }
105
- response.ProviderPeers = providers
106
- return p, response
107
-
108
- case dhtpb.Message_PING:
109
- return p, req
110
- default:
111
- }
112
- return "", nil
113
-}
114
-
115
-var _ proxy.RequestHandler = &Server{}
116
-var _ proxy.Proxy = &Server{}
117
-
118
-func getRoutingRecord(ds datastore.Datastore, k string) (*pb.Record, error) {
119
- dskey := dshelp.NewKeyFromBinary([]byte(k))
120
- val, err := ds.Get(dskey)
121
- if err != nil {
122
- return nil, err
123
- }
124
- recordBytes, ok := val.([]byte)
125
- if !ok {
126
- return nil, fmt.Errorf("datastore had non byte-slice value for %v", dskey)
127
- }
128
- var record pb.Record
129
- if err := proto.Unmarshal(recordBytes, &record); err != nil {
130
- return nil, errors.New("failed to unmarshal dht record from datastore")
131
- }
132
- return &record, nil
133
-}
134
-
135
-func putRoutingRecord(ds datastore.Datastore, k string, value *pb.Record) error {
136
- data, err := proto.Marshal(value)
137
- if err != nil {
138
- return err
139
- }
140
- dskey := dshelp.NewKeyFromBinary([]byte(k))
141
- // TODO namespace
142
- return ds.Put(dskey, data)
143
-}
144
-
145
-func putRoutingProviders(ds datastore.Datastore, k string, newRecords []*dhtpb.Message_Peer) error {
146
- log.Event(context.Background(), "putRoutingProviders")
147
- oldRecords, err := getRoutingProviders(ds, k)
148
- if err != nil {
149
- return err
150
- }
151
- mergedRecords := make(map[string]*dhtpb.Message_Peer)
152
- for _, provider := range oldRecords {
153
- mergedRecords[provider.GetId()] = provider // add original records
154
- }
155
- for _, provider := range newRecords {
156
- mergedRecords[provider.GetId()] = provider // overwrite old record if new exists
157
- }
158
- var protomsg dhtpb.Message
159
- protomsg.ProviderPeers = make([]*dhtpb.Message_Peer, 0, len(mergedRecords))
160
- for _, provider := range mergedRecords {
161
- protomsg.ProviderPeers = append(protomsg.ProviderPeers, provider)
162
- }
163
- data, err := proto.Marshal(&protomsg)
164
- if err != nil {
165
- return err
166
- }
167
- return ds.Put(providerKey(k), data)
168
-}
169
-
170
-func storeProvidersToPeerstore(ps pstore.Peerstore, p peer.ID, providers []*dhtpb.Message_Peer) {
171
- for _, provider := range providers {
172
- providerID := peer.ID(provider.GetId())
173
- if providerID != p {
174
- log.Errorf("provider message came from third-party %s", p)
175
- continue
176
- }
177
- for _, maddr := range provider.Addresses() {
178
- // as a router, we want to store addresses for peers who have provided
179
- ps.AddAddr(p, maddr, pstore.AddressTTL)
180
- }
181
- }
182
-}
183
-
184
-func getRoutingProviders(ds datastore.Datastore, k string) ([]*dhtpb.Message_Peer, error) {
185
- e := log.EventBegin(context.Background(), "getProviders")
186
- defer e.Done()
187
- var providers []*dhtpb.Message_Peer
188
- if v, err := ds.Get(providerKey(k)); err == nil {
189
- if data, ok := v.([]byte); ok {
190
- var msg dhtpb.Message
191
- if err := proto.Unmarshal(data, &msg); err != nil {
192
- return nil, err
193
- }
194
- providers = append(providers, msg.GetProviderPeers()...)
195
- }
196
- }
197
- return providers, nil
198
-}
199
-
200
-func providerKey(k string) datastore.Key {
201
- return datastore.KeyWithNamespaces([]string{"routing", "providers", k})
202
-}
routing/supernode/server_test.go
deleted
-39
@@ -1,39 +0,0 @@
1
-package supernode
2
-
3
-import (
4
- "testing"
5
-
6
- dhtpb "gx/ipfs/QmT7PnPxYkeKPCG8pAnucfcjrXc15Q7FgvFv7YC24EPrw8/go-libp2p-kad-dht/pb"
7
- datastore "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore"
8
-)
9
-
10
-func TestPutProviderDoesntResultInDuplicates(t *testing.T) {
11
- routingBackend := datastore.NewMapDatastore()
12
- k := "foo"
13
- put := []*dhtpb.Message_Peer{
14
- convPeer("bob", "127.0.0.1/tcp/4001"),
15
- convPeer("alice", "10.0.0.10/tcp/4001"),
16
- }
17
- if err := putRoutingProviders(routingBackend, k, put); err != nil {
18
- t.Fatal(err)
19
- }
20
- if err := putRoutingProviders(routingBackend, k, put); err != nil {
21
- t.Fatal(err)
22
- }
23
-
24
- got, err := getRoutingProviders(routingBackend, k)
25
- if err != nil {
26
- t.Fatal(err)
27
- }
28
- if len(got) != 2 {
29
- t.Fatal("should be 2 values, but there are", len(got))
30
- }
31
-}
32
-
33
-func convPeer(name string, addrs ...string) *dhtpb.Message_Peer {
34
- var rawAddrs [][]byte
35
- for _, addr := range addrs {
36
- rawAddrs = append(rawAddrs, []byte(addr))
37
- }
38
- return &dhtpb.Message_Peer{Id: &name, Addrs: rawAddrs}
39
-}
test/integration/grandcentral_test.go
deleted
-180
@@ -1,180 +0,0 @@
1
-package integrationtest
2
-
3
-import (
4
- "bytes"
5
- "errors"
6
- "fmt"
7
- "io"
8
- "math"
9
- "testing"
10
-
11
- context "context"
12
-
13
- core "github.com/ipfs/go-ipfs/core"
14
- "github.com/ipfs/go-ipfs/core/corerouting"
15
- "github.com/ipfs/go-ipfs/core/coreunix"
16
- mock "github.com/ipfs/go-ipfs/core/mock"
17
- ds2 "github.com/ipfs/go-ipfs/thirdparty/datastore2"
18
- "github.com/ipfs/go-ipfs/thirdparty/unit"
19
- pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore"
20
- testutil "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil"
21
- mocknet "gx/ipfs/QmRQ76P5dgvxTujhfPsCRAG83rC15jgb1G9bKLuomuC6dQ/go-libp2p/p2p/net/mock"
22
-)
23
-
24
-func TestSupernodeBootstrappedAddCat(t *testing.T) {
25
- // create 8 supernode-routing bootstrap nodes
26
- // create 2 supernode-routing clients both bootstrapped to the bootstrap nodes
27
- // let the bootstrap nodes share a single datastore
28
- // add a large file on one node then cat the file from the other
29
- conf := testutil.LatencyConfig{
30
- NetworkLatency: 0,
31
- RoutingLatency: 0,
32
- BlockstoreLatency: 0,
33
- }
34
- if err := RunSupernodeBootstrappedAddCat(RandomBytes(100*unit.MB), conf); err != nil {
35
- t.Fatal(err)
36
- }
37
-}
38
-
39
-func RunSupernodeBootstrappedAddCat(data []byte, conf testutil.LatencyConfig) error {
40
- ctx, cancel := context.WithCancel(context.Background())
41
- defer cancel()
42
-
43
- servers, clients, err := InitializeSupernodeNetwork(ctx, 8, 2, conf)
44
- if err != nil {
45
- return err
46
- }
47
- for _, n := range append(servers, clients...) {
48
- defer n.Close()
49
- }
50
-
51
- adder := clients[0]
52
- catter := clients[1]
53
-
54
- log.Info("adder is", adder.Identity)
55
- log.Info("catter is", catter.Identity)
56
-
57
- keyAdded, err := coreunix.Add(adder, bytes.NewReader(data))
58
- if err != nil {
59
- return err
60
- }
61
-
62
- readerCatted, err := coreunix.Cat(ctx, catter, keyAdded)
63
- if err != nil {
64
- return err
65
- }
66
-
67
- // verify
68
- bufout := new(bytes.Buffer)
69
- io.Copy(bufout, readerCatted)
70
- if 0 != bytes.Compare(bufout.Bytes(), data) {
71
- return errors.New("catted data does not match added data")
72
- }
73
- cancel()
74
- return nil
75
-}
76
-
77
-func InitializeSupernodeNetwork(
78
- ctx context.Context,
79
- numServers, numClients int,
80
- conf testutil.LatencyConfig) ([]*core.IpfsNode, []*core.IpfsNode, error) {
81
-
82
- // create network
83
- mn := mocknet.New(ctx)
84
-
85
- mn.SetLinkDefaults(mocknet.LinkOptions{
86
- Latency: conf.NetworkLatency,
87
- Bandwidth: math.MaxInt32,
88
- })
89
-
90
- routingDatastore := ds2.ThreadSafeCloserMapDatastore()
91
- var servers []*core.IpfsNode
92
- for i := 0; i < numServers; i++ {
93
- bootstrap, err := core.NewNode(ctx, &core.BuildCfg{
94
- Online: true,
95
- Host: mock.MockHostOption(mn),
96
- Routing: corerouting.SupernodeServer(routingDatastore),
97
- })
98
- if err != nil {
99
- return nil, nil, err
100
- }
101
- servers = append(servers, bootstrap)
102
- }
103
-
104
- var bootstrapInfos []pstore.PeerInfo
105
- for _, n := range servers {
106
- info := n.Peerstore.PeerInfo(n.PeerHost.ID())
107
- bootstrapInfos = append(bootstrapInfos, info)
108
- }
109
-
110
- var clients []*core.IpfsNode
111
- for i := 0; i < numClients; i++ {
112
- n, err := core.NewNode(ctx, &core.BuildCfg{
113
- Online: true,
114
- Host: mock.MockHostOption(mn),
115
- Routing: corerouting.SupernodeClient(bootstrapInfos...),
116
- })
117
- if err != nil {
118
- return nil, nil, err
119
- }
120
- clients = append(clients, n)
121
- }
122
- mn.LinkAll()
123
-
124
- bcfg := core.BootstrapConfigWithPeers(bootstrapInfos)
125
- for _, n := range clients {
126
- if err := n.Bootstrap(bcfg); err != nil {
127
- return nil, nil, err
128
- }
129
- }
130
- return servers, clients, nil
131
-}
132
-
133
-func TestSupernodePutRecordGetRecord(t *testing.T) {
134
- // create 8 supernode-routing bootstrap nodes
135
- // create 2 supernode-routing clients both bootstrapped to the bootstrap nodes
136
- // let the bootstrap nodes share a single datastore
137
- // add a large file on one node then cat the file from the other
138
- conf := testutil.LatencyConfig{
139
- NetworkLatency: 0,
140
- RoutingLatency: 0,
141
- BlockstoreLatency: 0,
142
- }
143
- if err := RunSupernodePutRecordGetRecord(conf); err != nil {
144
- t.Fatal(err)
145
- }
146
-}
147
-
148
-func RunSupernodePutRecordGetRecord(conf testutil.LatencyConfig) error {
149
- ctx, cancel := context.WithCancel(context.Background())
150
- defer cancel()
151
-
152
- servers, clients, err := InitializeSupernodeNetwork(ctx, 2, 2, conf)
153
- if err != nil {
154
- return err
155
- }
156
- for _, n := range append(servers, clients...) {
157
- defer n.Close()
158
- }
159
-
160
- putter := clients[0]
161
- getter := clients[1]
162
-
163
- k := "key"
164
- note := []byte("a note from putter")
165
-
166
- if err := putter.Routing.PutValue(ctx, k, note); err != nil {
167
- return fmt.Errorf("failed to put value: %s", err)
168
- }
169
-
170
- received, err := getter.Routing.GetValue(ctx, k)
171
- if err != nil {
172
- return fmt.Errorf("failed to get value: %s", err)
173
- }
174
-
175
- if 0 != bytes.Compare(note, received) {
176
- return errors.New("record doesn't match")
177
- }
178
- cancel()
179
- return nil
180
-}
test/supernode_client/.gitignore
deleted
-1
@@ -1 +0,0 @@
1
-.ipfs/
test/supernode_client/main.go
deleted
-245
@@ -1,245 +0,0 @@
1
-package main
2
-
3
-import (
4
- "bytes"
5
- "flag"
6
- "fmt"
7
- "io"
8
- "io/ioutil"
9
- "log"
10
- "math"
11
- "os"
12
- gopath "path"
13
- "time"
14
-
15
- random "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-random"
16
- commands "github.com/ipfs/go-ipfs/commands"
17
- core "github.com/ipfs/go-ipfs/core"
18
- corehttp "github.com/ipfs/go-ipfs/core/corehttp"
19
- corerouting "github.com/ipfs/go-ipfs/core/corerouting"
20
- "github.com/ipfs/go-ipfs/core/coreunix"
21
- "github.com/ipfs/go-ipfs/repo"
22
- config "github.com/ipfs/go-ipfs/repo/config"
23
- fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
24
- ds2 "github.com/ipfs/go-ipfs/thirdparty/datastore2"
25
- "github.com/ipfs/go-ipfs/thirdparty/ipfsaddr"
26
- unit "github.com/ipfs/go-ipfs/thirdparty/unit"
27
-
28
- context "context"
29
- pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore"
30
- logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log"
31
- ma "gx/ipfs/QmXY77cVe7rVRQXZZQRioukUM7aRW3BTcAgJe12MCtb3Ji/go-multiaddr"
32
-)
33
-
34
-var elog = logging.Logger("gc-client")
35
-
36
-var (
37
- cat = flag.Bool("cat", false, "else add")
38
- seed = flag.Int64("seed", 1, "")
39
- nBitsForKeypair = flag.Int("b", 1024, "number of bits for keypair (if repo is uninitialized)")
40
-)
41
-
42
-func main() {
43
- flag.Parse()
44
- if err := run(); err != nil {
45
- fmt.Fprintf(os.Stderr, "error: %s\n", err)
46
- os.Exit(1)
47
- }
48
-}
49
-
50
-func run() error {
51
- servers := config.DefaultSNRServers
52
- fmt.Println("using gcr remotes:")
53
- for _, p := range servers {
54
- fmt.Println("\t", p)
55
- }
56
-
57
- ctx, cancel := context.WithCancel(context.Background())
58
- defer cancel()
59
-
60
- cwd, err := os.Getwd()
61
- if err != nil {
62
- return err
63
- }
64
- repoPath := gopath.Join(cwd, config.DefaultPathName)
65
- _ = ensureRepoInitialized(repoPath)
66
-
67
- repo, err := fsrepo.Open(repoPath)
68
- if err != nil { // owned by node
69
- return err
70
- }
71
- cfg, err := repo.Config()
72
- if err != nil {
73
- return err
74
- }
75
-
76
- cfg.Bootstrap = servers
77
- if err := repo.SetConfig(cfg); err != nil {
78
- return err
79
- }
80
-
81
- var addrs []ipfsaddr.IPFSAddr
82
- for _, info := range servers {
83
- addr, err := ipfsaddr.ParseString(info)
84
- if err != nil {
85
- return err
86
- }
87
- addrs = append(addrs, addr)
88
- }
89
-
90
- var infos []pstore.PeerInfo
91
- for _, addr := range addrs {
92
- infos = append(infos, pstore.PeerInfo{
93
- ID: addr.ID(),
94
- Addrs: []ma.Multiaddr{addr.Transport()},
95
- })
96
- }
97
-
98
- node, err := core.NewNode(ctx, &core.BuildCfg{
99
- Online: true,
100
- Repo: repo,
101
- Routing: corerouting.SupernodeClient(infos...),
102
- })
103
- if err != nil {
104
- return err
105
- }
106
- defer node.Close()
107
-
108
- opts := []corehttp.ServeOption{
109
- corehttp.CommandsOption(cmdCtx(node, repoPath)),
110
- corehttp.GatewayOption(false),
111
- }
112
-
113
- if *cat {
114
- if err := runFileCattingWorker(ctx, node); err != nil {
115
- return err
116
- }
117
- } else {
118
- if err := runFileAddingWorker(node); err != nil {
119
- return err
120
- }
121
- }
122
- return corehttp.ListenAndServe(node, cfg.Addresses.API, opts...)
123
-}
124
-
125
-func ensureRepoInitialized(path string) error {
126
- if !fsrepo.IsInitialized(path) {
127
- conf, err := config.Init(ioutil.Discard, *nBitsForKeypair)
128
- if err != nil {
129
- return err
130
- }
131
- if err := fsrepo.Init(path, conf); err != nil {
132
- return err
133
- }
134
- }
135
- return nil
136
-}
137
-
138
-func sizeOfIthFile(i int64) int64 {
139
- return (1 << uint64(i)) * unit.KB
140
-}
141
-
142
-func runFileAddingWorker(n *core.IpfsNode) error {
143
- errs := make(chan error)
144
- go func() {
145
- var i int64
146
- for i = 1; i < math.MaxInt32; i++ {
147
- piper, pipew := io.Pipe()
148
- go func() {
149
- defer pipew.Close()
150
- if err := random.WritePseudoRandomBytes(sizeOfIthFile(i), pipew, *seed); err != nil {
151
- errs <- err
152
- }
153
- }()
154
- k, err := coreunix.Add(n, piper)
155
- if err != nil {
156
- errs <- err
157
- }
158
- log.Println("added file", "seed", *seed, "#", i, "key", k, "size", unit.Information(sizeOfIthFile(i)))
159
- time.Sleep(1 * time.Second)
160
- }
161
- }()
162
-
163
- var i int64
164
- for i = 0; i < math.MaxInt32; i++ {
165
- err := <-errs
166
- if err != nil {
167
- log.Fatal(err)
168
- }
169
- }
170
-
171
- return nil
172
-}
173
-
174
-func runFileCattingWorker(ctx context.Context, n *core.IpfsNode) error {
175
- conf, err := config.Init(ioutil.Discard, *nBitsForKeypair)
176
- if err != nil {
177
- return err
178
- }
179
-
180
- r := &repo.Mock{
181
- D: ds2.ThreadSafeCloserMapDatastore(),
182
- C: *conf,
183
- }
184
- dummy, err := core.NewNode(ctx, &core.BuildCfg{
185
- Repo: r,
186
- })
187
- if err != nil {
188
- return err
189
- }
190
-
191
- errs := make(chan error)
192
-
193
- go func() {
194
- defer dummy.Close()
195
- var i int64 = 1
196
- for {
197
- buf := new(bytes.Buffer)
198
- if err := random.WritePseudoRandomBytes(sizeOfIthFile(i), buf, *seed); err != nil {
199
- errs <- err
200
- }
201
- // add to a dummy node to discover the key
202
- k, err := coreunix.Add(dummy, bytes.NewReader(buf.Bytes()))
203
- if err != nil {
204
- errs <- err
205
- }
206
- e := elog.EventBegin(ctx, "cat", logging.LoggableF(func() map[string]interface{} {
207
- return map[string]interface{}{
208
- "key": k,
209
- "localPeer": n.Identity,
210
- }
211
- }))
212
- if r, err := coreunix.Cat(ctx, n, k); err != nil {
213
- e.Done()
214
- log.Printf("failed to cat file. seed: %d #%d key: %s err: %s", *seed, i, k, err)
215
- } else {
216
- log.Println("found file", "seed", *seed, "#", i, "key", k, "size", unit.Information(sizeOfIthFile(i)))
217
- io.Copy(ioutil.Discard, r)
218
- e.Done()
219
- log.Println("catted file", "seed", *seed, "#", i, "key", k, "size", unit.Information(sizeOfIthFile(i)))
220
- i++
221
- }
222
- time.Sleep(time.Second)
223
- }
224
- }()
225
-
226
- err = <-errs
227
- if err != nil {
228
- log.Fatal(err)
229
- }
230
-
231
- return nil
232
-}
233
-
234
-func cmdCtx(node *core.IpfsNode, repoPath string) commands.Context {
235
- return commands.Context{
236
- Online: true,
237
- ConfigRoot: repoPath,
238
- LoadConfig: func(path string) (*config.Config, error) {
239
- return node.Repo.Config()
240
- },
241
- ConstructNode: func() (*core.IpfsNode, error) {
242
- return node, nil
243
- },
244
- }
245
-}