@cryptotaxi247 / kubo / commits / 93f3117d7

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 -}