From 2513a1de3e8174db5769c0b45c4363fa308ed7ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 10 Mar 2018 17:59:43 +0100 Subject: [PATCH 01/11] coreapi: move unixfs errors to the top MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Łukasz Magiera --- core/coreapi/interface/interface.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/coreapi/interface/interface.go b/core/coreapi/interface/interface.go index 75a168bf3e8..4d68b5f4be9 100644 --- a/core/coreapi/interface/interface.go +++ b/core/coreapi/interface/interface.go @@ -14,6 +14,9 @@ import ( ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) +var ErrIsDir = errors.New("object is a directory") +var ErrOffline = errors.New("can't resolve, ipfs node is offline") + // Path is a generic wrapper for paths used in the API. A path can be resolved // to a CID using one of Resolve functions in the API. type Path interface { @@ -384,6 +387,3 @@ type PinAPI interface { // Verify verifies the integrity of pinned objects Verify(context.Context) (<-chan PinStatus, error) } - -var ErrIsDir = errors.New("object is a directory") -var ErrOffline = errors.New("can't resolve, ipfs node is offline") From 934fccc087eec5e1ce8d739c95fd5016fa1d18e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 10 Mar 2018 18:02:43 +0100 Subject: [PATCH 02/11] coreapi: expose peer/addr types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Łukasz Magiera --- core/coreapi/interface/interface.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/coreapi/interface/interface.go b/core/coreapi/interface/interface.go index 4d68b5f4be9..fea1952b346 100644 --- a/core/coreapi/interface/interface.go +++ b/core/coreapi/interface/interface.go @@ -10,6 +10,8 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + ma "gx/ipfs/QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb/go-multiaddr" + peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) @@ -34,6 +36,8 @@ type Path interface { // if we didn't, godoc would generate nice links straight to go-ipld-format type Node ipld.Node type Link ipld.Link +type PeerID peer.ID +type Addr ma.Multiaddr type Reader interface { io.ReadSeeker From 3226ef35d89d6082a1db7f3d18517e3613aa259a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 10 Mar 2018 18:04:51 +0100 Subject: [PATCH 03/11] coreapi: pubsub interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Łukasz Magiera --- core/coreapi/interface/interface.go | 41 +++++++++++++++++ core/coreapi/interface/options/pubsub.go | 56 ++++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 core/coreapi/interface/options/pubsub.go diff --git a/core/coreapi/interface/interface.go b/core/coreapi/interface/interface.go index fea1952b346..3a6f1bdd9c4 100644 --- a/core/coreapi/interface/interface.go +++ b/core/coreapi/interface/interface.go @@ -92,6 +92,23 @@ type BadPinNode interface { Err() error } +// PubSubSubscription is an active PubSub subscription +type PubSubSubscription interface { + io.Closer + + // Chan return incoming message channel + Chan(context.Context) <-chan PubSubMessage +} + +// PubSubMessage is a single PubSub message +type PubSubMessage interface { + // From returns id of a peer from which the message has arrived + From() PeerID + + // Data returns the message body + Data() []byte +} + // CoreAPI defines an unified interface to IPFS for Go programs. type CoreAPI interface { // Unixfs returns an implementation of Unixfs API. @@ -391,3 +408,27 @@ type PinAPI interface { // Verify verifies the integrity of pinned objects Verify(context.Context) (<-chan PinStatus, error) } + +// PubSubAPI specifies the interface to PubSub +type PubSubAPI interface { + // Ls lists subscribed topics by name + Ls(context.Context) ([]string, error) + + // Peers list peers we are currently pubsubbing with + // TODO: WithTopic + Peers(context.Context, ...options.PubSubPeersOption) ([]PeerID, error) + + // WithTopic is an option for peers which specifies a topic filter for the + // function + WithTopic(topic string) options.PubSubPeersOption + + // Publish a message to a given pubsub topic + Publish(context.Context, string, []byte) error + + // Subscribe to messages on a given topic + Subscribe(context.Context, string) (PubSubSubscription, error) + + // WithDiscover is an option for Subscribe which specifies whether to try to + // discover other peers subscribed to the same topic + WithDiscover(discover bool) options.PubSubSubscribeOption +} diff --git a/core/coreapi/interface/options/pubsub.go b/core/coreapi/interface/options/pubsub.go new file mode 100644 index 00000000000..e276d7e4a19 --- /dev/null +++ b/core/coreapi/interface/options/pubsub.go @@ -0,0 +1,56 @@ +package options + +type PubSubPeersSettings struct { + Topic string +} + +type PubSubSubscribeSettings struct { + Discover bool +} + +type PubSubPeersOption func(*PubSubPeersSettings) error +type PubSubSubscribeOption func(*PubSubSubscribeSettings) error + +func PubSubPeersOptions(opts ...PubSubPeersOption) (*PubSubPeersSettings, error) { + options := &PubSubPeersSettings{ + Topic: "", + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + +func PubSubSubscribeOptions(opts ...PubSubSubscribeOption) (*PubSubSubscribeSettings, error) { + options := &PubSubSubscribeSettings{ + Discover: false, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + +type PubSubOptions struct{} + +func (api *PubSubOptions) WithTopic(topic string) PubSubPeersOption { + return func(settings *PubSubPeersSettings) error { + settings.Topic = topic + return nil + } +} + +func (api *PubSubOptions) WithDiscover(discover bool) PubSubSubscribeOption { + return func(settings *PubSubSubscribeSettings) error { + settings.Discover = discover + return nil + } +} From 978be1623493c88c9daabd853fcb2099fc955d7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 10 Mar 2018 18:09:01 +0100 Subject: [PATCH 04/11] coreapi: swarm interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Łukasz Magiera --- core/coreapi/interface/interface.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/core/coreapi/interface/interface.go b/core/coreapi/interface/interface.go index 3a6f1bdd9c4..27cc9793938 100644 --- a/core/coreapi/interface/interface.go +++ b/core/coreapi/interface/interface.go @@ -109,6 +109,22 @@ type PubSubMessage interface { Data() []byte } +// PeerInfo contains information about a peer +type PeerInfo interface { + // ID returns PeerID + ID() PeerID + + // Address returns the multiaddress via which we are connected with the peer + Address() Addr + + // Latency returns last known round trip time to the peer + Latency() time.Duration + + // Streams returns list of streams established with the peer + // TODO: should this return multicodecs? + Streams() []string +} + // CoreAPI defines an unified interface to IPFS for Go programs. type CoreAPI interface { // Unixfs returns an implementation of Unixfs API. @@ -432,3 +448,15 @@ type PubSubAPI interface { // discover other peers subscribed to the same topic WithDiscover(discover bool) options.PubSubSubscribeOption } + +// SwarmAPI specifies the interface to libp2p swarm +type SwarmAPI interface { + // Connect to a given address + Connect(context.Context, Addr) error + + // Disconnect from a given address + Disconnect(context.Context, Addr) error + + // Peers returns the list of peers we are connected to + Peers(context.Context) ([]PeerInfo, error) +} From ba46a3571711ff7f8adb0f62826448ebecaedcda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 10 Mar 2018 18:10:12 +0100 Subject: [PATCH 05/11] coreapi: dht interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Łukasz Magiera --- core/coreapi/interface/interface.go | 18 ++++++++++++++++ core/coreapi/interface/options/dht.go | 30 +++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 core/coreapi/interface/options/dht.go diff --git a/core/coreapi/interface/interface.go b/core/coreapi/interface/interface.go index 27cc9793938..e478179cfd9 100644 --- a/core/coreapi/interface/interface.go +++ b/core/coreapi/interface/interface.go @@ -425,6 +425,24 @@ type PinAPI interface { Verify(context.Context) (<-chan PinStatus, error) } +// DhtAPI specifies the interface to the DHT +type DhtAPI interface { + // FindPeer queries the DHT for all of the multiaddresses associated with a + // Peer ID + FindPeer(context.Context, PeerID) (<-chan Addr, error) + + // FindProviders finds peers in the DHT who can provide a specific value + // given a key. + FindProviders(context.Context, Path) (<-chan PeerID, error) //TODO: is path the right choice here? + + // Provide announces to the network that you are providing given values + Provide(context.Context, Path, ...options.DhtProvideOption) error + + // WithRecursive is an option for Provide which specifies whether to provide + // the given path recursively + WithRecursive(recursive bool) options.DhtProvideOption +} + // PubSubAPI specifies the interface to PubSub type PubSubAPI interface { // Ls lists subscribed topics by name diff --git a/core/coreapi/interface/options/dht.go b/core/coreapi/interface/options/dht.go new file mode 100644 index 00000000000..92fd14f4aa2 --- /dev/null +++ b/core/coreapi/interface/options/dht.go @@ -0,0 +1,30 @@ +package options + +type DhtProvideSettings struct { + Recursive bool +} + +type DhtProvideOption func(*DhtProvideSettings) error + +func DhtProvideOptions(opts ...DhtProvideOption) (*DhtProvideSettings, error) { + options := &DhtProvideSettings{ + Recursive: false, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + +type DhtOptions struct{} + +func (api *DhtOptions) WithRecursive(recursive bool) DhtProvideOption { + return func(settings *DhtProvideSettings) error { + settings.Recursive = recursive + return nil + } +} From 910535d96bdade51d2d7ee677a49cee0c9227056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 5 Mar 2018 14:55:37 +0100 Subject: [PATCH 06/11] coreapi: implement dht api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Łukasz Magiera --- core/coreapi/coreapi.go | 5 + core/coreapi/dht.go | 239 ++++++++++++++++++++++++++ core/coreapi/interface/interface.go | 9 +- core/coreapi/interface/options/dht.go | 26 +++ 4 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 core/coreapi/dht.go diff --git a/core/coreapi/coreapi.go b/core/coreapi/coreapi.go index c34bccb3637..e3d27441870 100644 --- a/core/coreapi/coreapi.go +++ b/core/coreapi/coreapi.go @@ -57,6 +57,11 @@ func (api *CoreAPI) Pin() coreiface.PinAPI { return &PinAPI{api, nil} } +// Dht returns the DhtAPI interface implementation backed by the go-ipfs node +func (api *CoreAPI) Dht() coreiface.DhtAPI { + return &DhtAPI{api, nil} +} + // ResolveNode resolves the path `p` using Unixfx resolver, gets and returns the // resolved Node. func (api *CoreAPI) ResolveNode(ctx context.Context, p coreiface.Path) (coreiface.Node, error) { diff --git a/core/coreapi/dht.go b/core/coreapi/dht.go new file mode 100644 index 00000000000..4694fbdd44a --- /dev/null +++ b/core/coreapi/dht.go @@ -0,0 +1,239 @@ +package coreapi + +import ( + "context" + "errors" + "fmt" + + coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" + caopts "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + dag "github.com/ipfs/go-ipfs/merkledag" + + routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" + notif "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing/notifications" + ipdht "gx/ipfs/QmVSep2WwKcXxMonPASsAJ3nZVjfVMKgMcaSigxKnUWpJv/go-libp2p-kad-dht" + pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" + peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" +) + +var ErrNotDHT = errors.New("routing service is not a DHT") + +type DhtAPI struct { + *CoreAPI + *caopts.DhtOptions +} + +func (api *DhtAPI) FindPeer(ctx context.Context, p coreiface.PeerID) (<-chan coreiface.Addr, error) { + dht, ok := api.node.Routing.(*ipdht.IpfsDHT) + if !ok { + return nil, ErrNotDHT + } + + outChan := make(chan coreiface.Addr) + events := make(chan *notif.QueryEvent) + ctx = notif.RegisterForQueryEvents(ctx, events) + + go func() { + defer close(outChan) + + sendAddrs := func(responses []*pstore.PeerInfo) error { + for _, response := range responses { + for _, addr := range response.Addrs { + select { + case outChan <- addr: + case <-ctx.Done(): + return ctx.Err() + } + } + } + return nil + } + + for event := range events { + if event.Type == notif.FinalPeer { + err := sendAddrs(event.Responses) + if err != nil { + return + } + } + } + }() + + go func() { + defer close(events) + pi, err := dht.FindPeer(ctx, peer.ID(p)) + if err != nil { + notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ + Type: notif.QueryError, + Extra: err.Error(), + }) + return + } + + notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ + Type: notif.FinalPeer, + Responses: []*pstore.PeerInfo{&pi}, + }) + }() + + return outChan, nil +} + +func (api *DhtAPI) FindProviders(ctx context.Context, p coreiface.Path, opts ...caopts.DhtFindProvidersOption) (<-chan coreiface.PeerID, error) { + settings, err := caopts.DhtFindProvidersOptions(opts...) + if err != nil { + return nil, err + } + + dht, ok := api.node.Routing.(*ipdht.IpfsDHT) + if !ok { + return nil, ErrNotDHT + } + + p, err = api.ResolvePath(ctx, p) + if err != nil { + return nil, err + } + + c := p.Cid() + + numProviders := settings.NumProviders + if numProviders < 1 { + return nil, fmt.Errorf("number of providers must be greater than 0") + } + + outChan := make(chan coreiface.PeerID) + events := make(chan *notif.QueryEvent) + ctx = notif.RegisterForQueryEvents(ctx, events) + + pchan := dht.FindProvidersAsync(ctx, c, numProviders) + go func() { + defer close(outChan) + + sendProviders := func(responses []*pstore.PeerInfo) error { + for _, response := range responses { + select { + case outChan <- coreiface.PeerID(response.ID): + case <-ctx.Done(): + return ctx.Err() + } + } + return nil + } + + for event := range events { + if event.Type == notif.Provider { + err := sendProviders(event.Responses) + if err != nil { + return + } + } + } + }() + + go func() { + defer close(events) + for p := range pchan { + np := p + notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ + Type: notif.Provider, + Responses: []*pstore.PeerInfo{&np}, + }) + } + }() + + return outChan, nil +} + +func (api *DhtAPI) Provide(ctx context.Context, path coreiface.Path, opts ...caopts.DhtProvideOption) error { + settings, err := caopts.DhtProvideOptions(opts...) + if err != nil { + return err + } + + if api.node.Routing == nil { + return errors.New("cannot provide in offline mode") + } + + if len(api.node.PeerHost.Network().Conns()) == 0 { + return errors.New("cannot provide, no connected peers") + } + + c := path.Cid() + + has, err := api.node.Blockstore.Has(c) + if err != nil { + return err + } + + if !has { + return fmt.Errorf("block %s not found locally, cannot provide", c) + } + + //TODO: either remove or use + //outChan := make(chan interface{}) + + //events := make(chan *notif.QueryEvent) + //ctx = notif.RegisterForQueryEvents(ctx, events) + + /*go func() { + defer close(outChan) + for range events { + select { + case <-ctx.Done(): + return + default: + } + } + }()*/ + + //defer close(events) + if settings.Recursive { + err = provideKeysRec(ctx, api.node.Routing, api.node.DAG, []*cid.Cid{c}) + } else { + err = provideKeys(ctx, api.node.Routing, []*cid.Cid{c}) + } + if err != nil { + return err + } + + return nil +} + +func provideKeys(ctx context.Context, r routing.IpfsRouting, cids []*cid.Cid) error { + for _, c := range cids { + err := r.Provide(ctx, c, true) + if err != nil { + return err + } + } + return nil +} + +func provideKeysRec(ctx context.Context, r routing.IpfsRouting, dserv ipld.DAGService, cids []*cid.Cid) error { + provided := cid.NewSet() + for _, c := range cids { + kset := cid.NewSet() + + err := dag.EnumerateChildrenAsync(ctx, dag.GetLinksDirect(dserv), c, kset.Visit) + if err != nil { + return err + } + + for _, k := range kset.Keys() { + if provided.Has(k) { + continue + } + + err = r.Provide(ctx, k, true) + if err != nil { + return err + } + provided.Add(k) + } + } + + return nil +} diff --git a/core/coreapi/interface/interface.go b/core/coreapi/interface/interface.go index e478179cfd9..487a761a8ae 100644 --- a/core/coreapi/interface/interface.go +++ b/core/coreapi/interface/interface.go @@ -146,6 +146,9 @@ type CoreAPI interface { // ObjectAPI returns an implementation of Object API Object() ObjectAPI + // Dht returns an implementation of Dht API + Dht() DhtAPI + // ResolvePath resolves the path using Unixfs resolver ResolvePath(context.Context, Path) (Path, error) @@ -433,7 +436,11 @@ type DhtAPI interface { // FindProviders finds peers in the DHT who can provide a specific value // given a key. - FindProviders(context.Context, Path) (<-chan PeerID, error) //TODO: is path the right choice here? + FindProviders(context.Context, Path, ...options.DhtFindProvidersOption) (<-chan PeerID, error) //TODO: is path the right choice here? + + // WithNumProviders is an option for FindProviders which specifies the + // number of peers to look for. Default is 20 + WithNumProviders(numProviders int) options.DhtFindProvidersOption // Provide announces to the network that you are providing given values Provide(context.Context, Path, ...options.DhtProvideOption) error diff --git a/core/coreapi/interface/options/dht.go b/core/coreapi/interface/options/dht.go index 92fd14f4aa2..3867e32c075 100644 --- a/core/coreapi/interface/options/dht.go +++ b/core/coreapi/interface/options/dht.go @@ -4,7 +4,12 @@ type DhtProvideSettings struct { Recursive bool } +type DhtFindProvidersSettings struct { + NumProviders int +} + type DhtProvideOption func(*DhtProvideSettings) error +type DhtFindProvidersOption func(*DhtFindProvidersSettings) error func DhtProvideOptions(opts ...DhtProvideOption) (*DhtProvideSettings, error) { options := &DhtProvideSettings{ @@ -20,6 +25,20 @@ func DhtProvideOptions(opts ...DhtProvideOption) (*DhtProvideSettings, error) { return options, nil } +func DhtFindProvidersOptions(opts ...DhtFindProvidersOption) (*DhtFindProvidersSettings, error) { + options := &DhtFindProvidersSettings{ + NumProviders: 20, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + type DhtOptions struct{} func (api *DhtOptions) WithRecursive(recursive bool) DhtProvideOption { @@ -28,3 +47,10 @@ func (api *DhtOptions) WithRecursive(recursive bool) DhtProvideOption { return nil } } + +func (api *DhtOptions) WithNumProviders(numProviders int) DhtFindProvidersOption { + return func(settings *DhtFindProvidersSettings) error { + settings.NumProviders = numProviders + return nil + } +} From 2d2a9791f1ffccf49a2f1d557259b5ea356381ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 5 Mar 2018 14:57:46 +0100 Subject: [PATCH 07/11] coreapi: minor doc fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Łukasz Magiera --- core/coreapi/coreapi.go | 12 +++++++----- core/coreapi/interface/interface.go | 14 ++++++++------ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/core/coreapi/coreapi.go b/core/coreapi/coreapi.go index e3d27441870..d421af64ec8 100644 --- a/core/coreapi/coreapi.go +++ b/core/coreapi/coreapi.go @@ -24,35 +24,37 @@ func NewCoreAPI(n *core.IpfsNode) coreiface.CoreAPI { return api } -// Unixfs returns the UnixfsAPI interface backed by the go-ipfs node +// Unixfs returns the UnixfsAPI interface implementation backed by the go-ipfs node func (api *CoreAPI) Unixfs() coreiface.UnixfsAPI { return (*UnixfsAPI)(api) } +// Block returns the BlockAPI interface implementation backed by the go-ipfs node func (api *CoreAPI) Block() coreiface.BlockAPI { return &BlockAPI{api, nil} } -// Dag returns the DagAPI interface backed by the go-ipfs node +// Dag returns the DagAPI interface implementation backed by the go-ipfs node func (api *CoreAPI) Dag() coreiface.DagAPI { return &DagAPI{api, nil} } -// Name returns the NameAPI interface backed by the go-ipfs node +// Name returns the NameAPI interface implementation backed by the go-ipfs node func (api *CoreAPI) Name() coreiface.NameAPI { return &NameAPI{api, nil} } -// Key returns the KeyAPI interface backed by the go-ipfs node +// Key returns the KeyAPI interface implementation backed by the go-ipfs node func (api *CoreAPI) Key() coreiface.KeyAPI { return &KeyAPI{api, nil} } -//Object returns the ObjectAPI interface backed by the go-ipfs node +// Object returns the ObjectAPI interface implementation backed by the go-ipfs node func (api *CoreAPI) Object() coreiface.ObjectAPI { return &ObjectAPI{api, nil} } +// Pin returns the PinAPI interface implementation backed by the go-ipfs node func (api *CoreAPI) Pin() coreiface.PinAPI { return &PinAPI{api, nil} } diff --git a/core/coreapi/interface/interface.go b/core/coreapi/interface/interface.go index 487a761a8ae..abb47291d7c 100644 --- a/core/coreapi/interface/interface.go +++ b/core/coreapi/interface/interface.go @@ -125,22 +125,24 @@ type PeerInfo interface { Streams() []string } -// CoreAPI defines an unified interface to IPFS for Go programs. +// CoreAPI defines an unified interface to IPFS for Go programs type CoreAPI interface { - // Unixfs returns an implementation of Unixfs API. + // Unixfs returns an implementation of Unixfs API Unixfs() UnixfsAPI - // Block returns an implementation of Block API. + // Block returns an implementation of Block API Block() BlockAPI - // Dag returns an implementation of Dag API. + // Dag returns an implementation of Dag API Dag() DagAPI - // Name returns an implementation of Name API. + // Name returns an implementation of Name API Name() NameAPI - // Key returns an implementation of Key API. + // Key returns an implementation of Key API Key() KeyAPI + + // Pin returns an implementation of Pin API Pin() PinAPI // ObjectAPI returns an implementation of Object API From f98929bd0c54dfa21c6af86e84ecf9655f42a306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 6 Mar 2018 00:13:30 +0100 Subject: [PATCH 08/11] coreapi: implement pubsub api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Łukasz Magiera --- core/coreapi/coreapi.go | 5 ++ core/coreapi/interface/interface.go | 11 +-- core/coreapi/pubsub.go | 107 ++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 core/coreapi/pubsub.go diff --git a/core/coreapi/coreapi.go b/core/coreapi/coreapi.go index d421af64ec8..98d1bd183b9 100644 --- a/core/coreapi/coreapi.go +++ b/core/coreapi/coreapi.go @@ -64,6 +64,11 @@ func (api *CoreAPI) Dht() coreiface.DhtAPI { return &DhtAPI{api, nil} } +// Dht returns the DhtAPI interface implementation backed by the go-ipfs node +func (api *CoreAPI) PubSub() coreiface.PubSubAPI { + return &PubSubAPI{api, nil} +} + // ResolveNode resolves the path `p` using Unixfx resolver, gets and returns the // resolved Node. func (api *CoreAPI) ResolveNode(ctx context.Context, p coreiface.Path) (coreiface.Node, error) { diff --git a/core/coreapi/interface/interface.go b/core/coreapi/interface/interface.go index abb47291d7c..2f962fefc3e 100644 --- a/core/coreapi/interface/interface.go +++ b/core/coreapi/interface/interface.go @@ -17,7 +17,7 @@ import ( ) var ErrIsDir = errors.New("object is a directory") -var ErrOffline = errors.New("can't resolve, ipfs node is offline") +var ErrOffline = errors.New("this action must be run in online mode, try running 'ipfs daemon' first") // Path is a generic wrapper for paths used in the API. A path can be resolved // to a CID using one of Resolve functions in the API. @@ -96,8 +96,8 @@ type BadPinNode interface { type PubSubSubscription interface { io.Closer - // Chan return incoming message channel - Chan(context.Context) <-chan PubSubMessage + // Next return the next incoming message + Next(context.Context) (PubSubMessage, error) } // PubSubMessage is a single PubSub message @@ -151,6 +151,9 @@ type CoreAPI interface { // Dht returns an implementation of Dht API Dht() DhtAPI + // PubSub returns an implementation of PubSub API + PubSub() PubSubAPI + // ResolvePath resolves the path using Unixfs resolver ResolvePath(context.Context, Path) (Path, error) @@ -469,7 +472,7 @@ type PubSubAPI interface { Publish(context.Context, string, []byte) error // Subscribe to messages on a given topic - Subscribe(context.Context, string) (PubSubSubscription, error) + Subscribe(context.Context, string, ...options.PubSubSubscribeOption) (PubSubSubscription, error) // WithDiscover is an option for Subscribe which specifies whether to try to // discover other peers subscribed to the same topic diff --git a/core/coreapi/pubsub.go b/core/coreapi/pubsub.go new file mode 100644 index 00000000000..9f08644075d --- /dev/null +++ b/core/coreapi/pubsub.go @@ -0,0 +1,107 @@ +package coreapi + +import ( + "context" + "errors" + + coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" + caopts "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + + floodsub "gx/ipfs/QmSFihvoND3eDaAYRCeLgLPt62yCPgMZs1NSZmKFEtJQQw/go-libp2p-floodsub" +) + +type PubSubAPI struct { + *CoreAPI + *caopts.PubSubOptions +} + +type pubSubSubscription struct { + subscription *floodsub.Subscription +} + +type pubSubMessage struct { + msg *floodsub.Message +} + +func (api *PubSubAPI) Ls(ctx context.Context) ([]string, error) { + if err := api.checkNode(); err != nil { + return nil, err + } + + return api.node.Floodsub.GetTopics(), nil +} + +func (api *PubSubAPI) Peers(ctx context.Context, opts ...caopts.PubSubPeersOption) ([]coreiface.PeerID, error) { + if err := api.checkNode(); err != nil { + return nil, err + } + + settings, err := caopts.PubSubPeersOptions(opts...) + if err != nil { + return nil, err + } + + peers := api.node.Floodsub.ListPeers(settings.Topic) + out := make([]coreiface.PeerID, len(peers)) + + for i, peer := range peers { + out[i] = coreiface.PeerID(peer) + } + + return out, nil +} + +func (api *PubSubAPI) Publish(ctx context.Context, topic string, data []byte) error { + if err := api.checkNode(); err != nil { + return err + } + + return api.node.Floodsub.Publish(topic, data) +} + +func (api *PubSubAPI) Subscribe(ctx context.Context, topic string, opts ...caopts.PubSubSubscribeOption) (coreiface.PubSubSubscription, error) { + if err := api.checkNode(); err != nil { + return nil, err + } + + sub, err := api.node.Floodsub.Subscribe(topic) + if err != nil { + return nil, err + } + + return &pubSubSubscription{sub}, nil +} + +func (api *PubSubAPI) checkNode() error { + if !api.node.OnlineMode() { + return coreiface.ErrOffline + } + + if api.node.Floodsub == nil { + return errors.New("experimental pubsub feature not enabled. Run daemon with --enable-pubsub-experiment to use.") + } + + return nil +} + +func (sub *pubSubSubscription) Close() error { + sub.subscription.Cancel() + return nil +} + +func (sub *pubSubSubscription) Next(ctx context.Context) (coreiface.PubSubMessage, error) { + msg, err := sub.subscription.Next(ctx) + if err != nil { + return nil, err + } + + return &pubSubMessage{msg}, nil +} + +func (msg *pubSubMessage) From() coreiface.PeerID { + return coreiface.PeerID(msg.msg.From) +} + +func (msg *pubSubMessage) Data() []byte { + return msg.msg.Data +} From e3867ffd474e78a65226d46f84c3e08a68ffd45b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 6 Mar 2018 02:54:09 +0100 Subject: [PATCH 09/11] coreapi: implement swarm api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Łukasz Magiera --- core/coreapi/coreapi.go | 7 +- core/coreapi/interface/interface.go | 7 +- core/coreapi/swarm.go | 147 ++++++++++++++++++++++++++++ 3 files changed, 158 insertions(+), 3 deletions(-) create mode 100644 core/coreapi/swarm.go diff --git a/core/coreapi/coreapi.go b/core/coreapi/coreapi.go index 98d1bd183b9..e664f5909bd 100644 --- a/core/coreapi/coreapi.go +++ b/core/coreapi/coreapi.go @@ -64,11 +64,16 @@ func (api *CoreAPI) Dht() coreiface.DhtAPI { return &DhtAPI{api, nil} } -// Dht returns the DhtAPI interface implementation backed by the go-ipfs node +// PubSub returns the DhtAPI interface implementation backed by the go-ipfs node func (api *CoreAPI) PubSub() coreiface.PubSubAPI { return &PubSubAPI{api, nil} } +// PubSub returns the DhtAPI interface implementation backed by the go-ipfs node +func (api *CoreAPI) Swarm() coreiface.SwarmAPI { + return &SwarmAPI{api} +} + // ResolveNode resolves the path `p` using Unixfx resolver, gets and returns the // resolved Node. func (api *CoreAPI) ResolveNode(ctx context.Context, p coreiface.Path) (coreiface.Node, error) { diff --git a/core/coreapi/interface/interface.go b/core/coreapi/interface/interface.go index 2f962fefc3e..ee24c16f1a1 100644 --- a/core/coreapi/interface/interface.go +++ b/core/coreapi/interface/interface.go @@ -118,11 +118,11 @@ type PeerInfo interface { Address() Addr // Latency returns last known round trip time to the peer - Latency() time.Duration + Latency(context.Context) (time.Duration, error) // Streams returns list of streams established with the peer // TODO: should this return multicodecs? - Streams() []string + Streams(context.Context) ([]string, error) } // CoreAPI defines an unified interface to IPFS for Go programs @@ -154,6 +154,9 @@ type CoreAPI interface { // PubSub returns an implementation of PubSub API PubSub() PubSubAPI + // Swarm returns an implementation of Swarm API + Swarm() SwarmAPI + // ResolvePath resolves the path using Unixfs resolver ResolvePath(context.Context, Path) (Path, error) diff --git a/core/coreapi/swarm.go b/core/coreapi/swarm.go new file mode 100644 index 00000000000..f107949a647 --- /dev/null +++ b/core/coreapi/swarm.go @@ -0,0 +1,147 @@ +package coreapi + +import ( + "context" + "errors" + "fmt" + "time" + + coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" + + iaddr "gx/ipfs/QmQViVWBHbU6HmYjXcdNq7tVASCNgdg64ZGcauuDkLCivW/go-ipfs-addr" + swarm "gx/ipfs/QmSwZMWwFZSUpe5muU2xgTUwppH24KfMwdPXiwbEp2c6G5/go-libp2p-swarm" + ma "gx/ipfs/QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb/go-multiaddr" + pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" + net "gx/ipfs/QmXfkENeeBvh3zYA51MaSdGUdBjhQ99cP5WQe8zgr6wchG/go-libp2p-net" + peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" +) + +type SwarmAPI struct { + *CoreAPI +} + +type connInfo struct { + api *CoreAPI + conn net.Conn + + addr ma.Multiaddr + peer peer.ID + muxer string +} + +func (api *SwarmAPI) Connect(ctx context.Context, addr coreiface.Addr) error { + if api.node.PeerHost == nil { + return coreiface.ErrOffline + } + + snet, ok := api.node.PeerHost.Network().(*swarm.Network) + if !ok { + return fmt.Errorf("peerhost network was not swarm") + } + + swrm := snet.Swarm() + + ia, err := iaddr.ParseMultiaddr(ma.Multiaddr(addr)) + if err != nil { + return err + } + + pi := pstore.PeerInfo{ + ID: ia.ID(), + Addrs: []ma.Multiaddr{ia.Transport()}, + } + + swrm.Backoff().Clear(pi.ID) + + return api.node.PeerHost.Connect(ctx, pi) +} + +func (api *SwarmAPI) Disconnect(ctx context.Context, addr coreiface.Addr) error { + if api.node.PeerHost == nil { + return coreiface.ErrOffline + } + + ia, err := iaddr.ParseMultiaddr(ma.Multiaddr(addr)) + if err != nil { + return err + } + + taddr := ia.Transport() + + found := false + conns := api.node.PeerHost.Network().ConnsToPeer(ia.ID()) + for _, conn := range conns { + if !conn.RemoteMultiaddr().Equal(taddr) { + continue + } + + if err := conn.Close(); err != nil { + return err + } + found = true + break + } + + if !found { + return errors.New("conn not found") + } + + return nil +} + +func (api *SwarmAPI) Peers(context.Context) ([]coreiface.PeerInfo, error) { + if api.node.PeerHost == nil { + return nil, coreiface.ErrOffline + } + + conns := api.node.PeerHost.Network().Conns() + + var out []coreiface.PeerInfo + for _, c := range conns { + pid := c.RemotePeer() + addr := c.RemoteMultiaddr() + + ci := &connInfo{ + api: api.CoreAPI, + conn: c, + + addr: addr, + peer: pid, + } + + swcon, ok := c.(*swarm.Conn) + if ok { + ci.muxer = fmt.Sprintf("%T", swcon.StreamConn().Conn()) + } + + out = append(out, ci) + } + + return out, nil +} + +func (ci *connInfo) ID() coreiface.PeerID { + return ci.ID() +} + +func (ci *connInfo) Address() coreiface.Addr { + return ci.addr +} + +func (ci *connInfo) Latency(context.Context) (time.Duration, error) { + return ci.api.node.Peerstore.LatencyEWMA(peer.ID(ci.ID())), nil +} + +func (ci *connInfo) Streams(context.Context) ([]string, error) { + streams, err := ci.conn.GetStreams() + if err != nil { + return nil, err + } + + out := make([]string, len(streams)) + for i, s := range streams { + out[i] = string(s.Protocol()) + } + + return out, nil +} From e5690b982e21bd9297a3277d3e128731cb51980e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 8 Mar 2018 16:59:02 +0100 Subject: [PATCH 10/11] coreapi: dht tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Łukasz Magiera --- core/coreapi/dht_test.go | 112 ++++++++++++++++++++++++++++ core/coreapi/interface/interface.go | 2 +- core/coreapi/name_test.go | 12 ++- core/coreapi/unixfs_test.go | 100 ++++++++++++++++++------- 4 files changed, 192 insertions(+), 34 deletions(-) create mode 100644 core/coreapi/dht_test.go diff --git a/core/coreapi/dht_test.go b/core/coreapi/dht_test.go new file mode 100644 index 00000000000..2919c5e9df5 --- /dev/null +++ b/core/coreapi/dht_test.go @@ -0,0 +1,112 @@ +package coreapi_test + +import ( + "context" + "io" + "io/ioutil" + "testing" + + coreapi "github.com/ipfs/go-ipfs/core/coreapi" + coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" + + blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" +) + +func TestDhtFindPeer(t *testing.T) { + ctx := context.Background() + nds, apis, err := makeAPISwarm(ctx, true, 3) + if err != nil { + t.Fatal(err) + } + + out, err := apis[2].Dht().FindPeer(ctx, coreiface.PeerID(nds[0].Identity)) + if err != nil { + t.Fatal(err) + } + + addr := <-out + + if addr.String() != "/ip4/127.0.0.1/tcp/4001" { + t.Errorf("got unexpected address from FindPeer: %s", addr.String()) + } + + out, err = apis[1].Dht().FindPeer(ctx, coreiface.PeerID(nds[2].Identity)) + if err != nil { + t.Fatal(err) + } + + addr = <-out + + if addr.String() != "/ip4/127.0.2.1/tcp/4001" { + t.Errorf("got unexpected address from FindPeer: %s", addr.String()) + } +} + +func TestDhtFindProviders(t *testing.T) { + ctx := context.Background() + nds, apis, err := makeAPISwarm(ctx, true, 3) + if err != nil { + t.Fatal(err) + } + + p, err := addTestObject(ctx, apis[0]) + if err != nil { + t.Fatal(err) + } + + out, err := apis[2].Dht().FindProviders(ctx, p, apis[2].Dht().WithNumProviders(1)) + if err != nil { + t.Fatal(err) + } + + provider := <-out + + if provider.String() != nds[0].Identity.String() { + t.Errorf("got wrong provider: %s != %s", provider.String(), nds[0].Identity.String()) + } +} + +func TestDhtProvide(t *testing.T) { + ctx := context.Background() + nds, apis, err := makeAPISwarm(ctx, true, 3) + if err != nil { + t.Fatal(err) + } + + // TODO: replace once there is local add on unixfs or somewhere + data, err := ioutil.ReadAll(&io.LimitedReader{R: rnd, N: 4092}) + if err != nil { + t.Fatal(err) + } + + b := blocks.NewBlock(data) + nds[0].Blockstore.Put(b) + p := coreapi.ParseCid(b.Cid()) + + out, err := apis[2].Dht().FindProviders(ctx, p, apis[2].Dht().WithNumProviders(1)) + if err != nil { + t.Fatal(err) + } + + provider := <-out + + if provider.String() != "" { + t.Errorf("got wrong provider: %s != %s", provider.String(), nds[0].Identity.String()) + } + + err = apis[0].Dht().Provide(ctx, p) + if err != nil { + t.Fatal(err) + } + + out, err = apis[2].Dht().FindProviders(ctx, p, apis[2].Dht().WithNumProviders(1)) + if err != nil { + t.Fatal(err) + } + + provider = <-out + + if provider.String() != nds[0].Identity.String() { + t.Errorf("got wrong provider: %s != %s", provider.String(), nds[0].Identity.String()) + } +} diff --git a/core/coreapi/interface/interface.go b/core/coreapi/interface/interface.go index ee24c16f1a1..5439bb89fed 100644 --- a/core/coreapi/interface/interface.go +++ b/core/coreapi/interface/interface.go @@ -36,7 +36,7 @@ type Path interface { // if we didn't, godoc would generate nice links straight to go-ipld-format type Node ipld.Node type Link ipld.Link -type PeerID peer.ID +type PeerID = peer.ID type Addr ma.Multiaddr type Reader interface { diff --git a/core/coreapi/name_test.go b/core/coreapi/name_test.go index 74fdacda41b..37010db59a1 100644 --- a/core/coreapi/name_test.go +++ b/core/coreapi/name_test.go @@ -20,11 +20,13 @@ func addTestObject(ctx context.Context, api coreiface.CoreAPI) (coreiface.Path, func TestBasicPublishResolve(t *testing.T) { ctx := context.Background() - n, api, err := makeAPIIdent(ctx, true) + nds, apis, err := makeAPISwarm(ctx, true, 2) if err != nil { t.Fatal(err) return } + n := nds[0] + api := apis[0] p, err := addTestObject(ctx, api) if err != nil { @@ -59,11 +61,12 @@ func TestBasicPublishResolve(t *testing.T) { func TestBasicPublishResolveKey(t *testing.T) { ctx := context.Background() - _, api, err := makeAPIIdent(ctx, true) + _, apis, err := makeAPISwarm(ctx, true, 2) if err != nil { t.Fatal(err) return } + api := apis[0] k, err := api.Key().Generate(ctx, "foo") if err != nil { @@ -106,12 +109,13 @@ func TestBasicPublishResolveTimeout(t *testing.T) { t.Skip("ValidTime doesn't appear to work at this time resolution") ctx := context.Background() - n, api, err := makeAPIIdent(ctx, true) + nds, apis, err := makeAPISwarm(ctx, true, 2) if err != nil { t.Fatal(err) return } - + n := nds[0] + api := apis[0] p, err := addTestObject(ctx, api) if err != nil { t.Fatal(err) diff --git a/core/coreapi/unixfs_test.go b/core/coreapi/unixfs_test.go index aa5903a7f15..d07ea0ff54a 100644 --- a/core/coreapi/unixfs_test.go +++ b/core/coreapi/unixfs_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/base64" + "fmt" "io" "math" "strings" @@ -13,6 +14,7 @@ import ( coreapi "github.com/ipfs/go-ipfs/core/coreapi" coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" coreunix "github.com/ipfs/go-ipfs/core/coreunix" + mock "github.com/ipfs/go-ipfs/core/mock" keystore "github.com/ipfs/go-ipfs/keystore" mdag "github.com/ipfs/go-ipfs/merkledag" repo "github.com/ipfs/go-ipfs/repo" @@ -21,6 +23,8 @@ import ( unixfs "github.com/ipfs/go-ipfs/unixfs" cbor "gx/ipfs/QmNRz7BDWfdFNVLt7AVvmRefkrURD25EeoipcXqo6yoXU1/go-ipld-cbor" + mocknet "gx/ipfs/QmNh1kGFFdsPu79KNSaL4NUKUPb4Eiz4KHdMtFY6664RDp/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) @@ -37,51 +41,89 @@ var emptyDir = coreapi.ResolvedPath("/ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbs // `echo -n | ipfs add` var emptyFile = coreapi.ResolvedPath("/ipfs/QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH", nil, nil) -func makeAPIIdent(ctx context.Context, fullIdentity bool) (*core.IpfsNode, coreiface.CoreAPI, error) { - var ident config.Identity - if fullIdentity { - sk, pk, err := ci.GenerateKeyPair(ci.RSA, 512) - if err != nil { - return nil, nil, err +func makeAPISwarm(ctx context.Context, fullIdentity bool, n int) ([]*core.IpfsNode, []coreiface.CoreAPI, error) { + mn := mocknet.New(ctx) + + nodes := make([]*core.IpfsNode, n) + apis := make([]coreiface.CoreAPI, n) + + for i := 0; i < n; i++ { + var ident config.Identity + if fullIdentity { + sk, pk, err := ci.GenerateKeyPair(ci.RSA, 512) + if err != nil { + return nil, nil, err + } + + id, err := peer.IDFromPublicKey(pk) + if err != nil { + return nil, nil, err + } + + kbytes, err := sk.Bytes() + if err != nil { + return nil, nil, err + } + + ident = config.Identity{ + PeerID: id.Pretty(), + PrivKey: base64.StdEncoding.EncodeToString(kbytes), + } + } else { + ident = config.Identity{ + PeerID: testPeerID, + } } - id, err := peer.IDFromPublicKey(pk) - if err != nil { - return nil, nil, err + c := config.Config{} + c.Addresses.Swarm = []string{fmt.Sprintf("/ip4/127.0.%d.1/tcp/4001", i)} + c.Identity = ident + + r := &repo.Mock{ + C: c, + D: ds2.ThreadSafeCloserMapDatastore(), + K: keystore.NewMemKeystore(), } - kbytes, err := sk.Bytes() + node, err := core.NewNode(ctx, &core.BuildCfg{ + Repo: r, + Host: mock.MockHostOption(mn), + Online: fullIdentity, + }) if err != nil { return nil, nil, err } + nodes[i] = node + apis[i] = coreapi.NewCoreAPI(node) + } - ident = config.Identity{ - PeerID: id.Pretty(), - PrivKey: base64.StdEncoding.EncodeToString(kbytes), - } - } else { - ident = config.Identity{ - PeerID: testPeerID, - } + err := mn.LinkAll() + if err != nil { + return nil, nil, err } - r := &repo.Mock{ - C: config.Config{ - Identity: ident, + bsinf := core.BootstrapConfigWithPeers( + []pstore.PeerInfo{ + nodes[0].Peerstore.PeerInfo(nodes[0].Identity), }, - D: ds2.ThreadSafeCloserMapDatastore(), - K: keystore.NewMemKeystore(), + ) + + for _, n := range nodes[1:] { + if err := n.Bootstrap(bsinf); err != nil { + return nil, nil, err + } } - node, err := core.NewNode(ctx, &core.BuildCfg{Repo: r}) + + return nodes, apis, nil +} + +func makeAPI(ctx context.Context) (*core.IpfsNode, coreiface.CoreAPI, error) { + nd, api, err := makeAPISwarm(ctx, false, 1) if err != nil { return nil, nil, err } - api := coreapi.NewCoreAPI(node) - return node, api, nil -} -func makeAPI(ctx context.Context) (*core.IpfsNode, coreiface.CoreAPI, error) { - return makeAPIIdent(ctx, false) + return nd[0], api[0], nil } func TestAdd(t *testing.T) { From be4c3554023423bd14701a0f38a69b897b8125f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 8 Mar 2018 17:10:16 +0100 Subject: [PATCH 11/11] coreapi: use type aliases for exposed types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Łukasz Magiera --- core/coreapi/interface/interface.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/coreapi/interface/interface.go b/core/coreapi/interface/interface.go index 5439bb89fed..b5ee09c10e7 100644 --- a/core/coreapi/interface/interface.go +++ b/core/coreapi/interface/interface.go @@ -34,10 +34,10 @@ type Path interface { // TODO: should we really copy these? // if we didn't, godoc would generate nice links straight to go-ipld-format -type Node ipld.Node -type Link ipld.Link +type Node = ipld.Node +type Link = ipld.Link type PeerID = peer.ID -type Addr ma.Multiaddr +type Addr = ma.Multiaddr type Reader interface { io.ReadSeeker