mirror of
https://github.com/ipfs/kubo.git
synced 2025-05-21 08:56:37 +08:00
314 lines
9.7 KiB
Go
314 lines
9.7 KiB
Go
package core
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"errors"
|
|
"fmt"
|
|
"net/http/httptest"
|
|
"path"
|
|
"testing"
|
|
"time"
|
|
|
|
context "context"
|
|
|
|
"github.com/ipfs/go-cid"
|
|
"github.com/ipfs/go-delegated-routing/client"
|
|
"github.com/ipfs/go-ipns"
|
|
"github.com/ipfs/kubo/core/node/libp2p"
|
|
"github.com/ipfs/kubo/repo"
|
|
"github.com/libp2p/go-libp2p/core/crypto"
|
|
peer "github.com/libp2p/go-libp2p/core/peer"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
datastore "github.com/ipfs/go-datastore"
|
|
syncds "github.com/ipfs/go-datastore/sync"
|
|
drs "github.com/ipfs/go-delegated-routing/server"
|
|
config "github.com/ipfs/kubo/config"
|
|
)
|
|
|
|
func TestInitialization(t *testing.T) {
|
|
ctx := context.Background()
|
|
id := testIdentity
|
|
|
|
good := []*config.Config{
|
|
{
|
|
Identity: id,
|
|
Addresses: config.Addresses{
|
|
Swarm: []string{"/ip4/0.0.0.0/tcp/4001", "/ip4/0.0.0.0/udp/4001/quic"},
|
|
API: []string{"/ip4/127.0.0.1/tcp/8000"},
|
|
},
|
|
},
|
|
|
|
{
|
|
Identity: id,
|
|
Addresses: config.Addresses{
|
|
Swarm: []string{"/ip4/0.0.0.0/tcp/4001", "/ip4/0.0.0.0/udp/4001/quic"},
|
|
API: []string{"/ip4/127.0.0.1/tcp/8000"},
|
|
},
|
|
},
|
|
}
|
|
|
|
bad := []*config.Config{
|
|
{},
|
|
}
|
|
|
|
for i, c := range good {
|
|
r := &repo.Mock{
|
|
C: *c,
|
|
D: syncds.MutexWrap(datastore.NewMapDatastore()),
|
|
}
|
|
n, err := NewNode(ctx, &BuildCfg{Repo: r})
|
|
if n == nil || err != nil {
|
|
t.Error("Should have constructed.", i, err)
|
|
}
|
|
}
|
|
|
|
for i, c := range bad {
|
|
r := &repo.Mock{
|
|
C: *c,
|
|
D: syncds.MutexWrap(datastore.NewMapDatastore()),
|
|
}
|
|
n, err := NewNode(ctx, &BuildCfg{Repo: r})
|
|
if n != nil || err == nil {
|
|
t.Error("Should have failed to construct.", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
var testIdentity = config.Identity{
|
|
PeerID: "QmNgdzLieYi8tgfo2WfTUzNVH5hQK9oAYGVf6dxN12NrHt",
|
|
PrivKey: "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",
|
|
}
|
|
|
|
var errNotSupported = errors.New("method not supported")
|
|
|
|
func TestDelegatedRoutingSingle(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
pId1, priv1, err := GeneratePeerID()
|
|
require.NoError(err)
|
|
|
|
pId2, _, err := GeneratePeerID()
|
|
require.NoError(err)
|
|
|
|
theID := path.Join("/ipns", string(pId1))
|
|
theErrorID := path.Join("/ipns", string(pId2))
|
|
|
|
d := &delegatedRoutingService{
|
|
goodPeerID: pId1,
|
|
badPeerID: pId2,
|
|
pk1: priv1,
|
|
}
|
|
|
|
url := StartRoutingServer(t, d)
|
|
n := GetNode(t, url)
|
|
|
|
ctx := context.Background()
|
|
|
|
v, err := n.Routing.GetValue(ctx, theID)
|
|
require.NoError(err)
|
|
require.NotNil(v)
|
|
require.Contains(string(v), "RECORD FROM SERVICE 0")
|
|
|
|
v, err = n.Routing.GetValue(ctx, theErrorID)
|
|
require.Nil(v)
|
|
require.Error(err)
|
|
|
|
err = n.Routing.PutValue(ctx, theID, v)
|
|
require.NoError(err)
|
|
|
|
err = n.Routing.PutValue(ctx, theErrorID, v)
|
|
require.Error(err)
|
|
}
|
|
|
|
func TestDelegatedRoutingMulti(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
pId1, priv1, err := GeneratePeerID()
|
|
require.NoError(err)
|
|
|
|
pId2, priv2, err := GeneratePeerID()
|
|
require.NoError(err)
|
|
|
|
theID1 := path.Join("/ipns", string(pId1))
|
|
theID2 := path.Join("/ipns", string(pId2))
|
|
|
|
d1 := &delegatedRoutingService{
|
|
goodPeerID: pId1,
|
|
badPeerID: pId2,
|
|
pk1: priv1,
|
|
serviceID: 1,
|
|
}
|
|
|
|
url1 := StartRoutingServer(t, d1)
|
|
|
|
d2 := &delegatedRoutingService{
|
|
goodPeerID: pId2,
|
|
badPeerID: pId1,
|
|
pk1: priv2,
|
|
serviceID: 2,
|
|
}
|
|
|
|
url2 := StartRoutingServer(t, d2)
|
|
|
|
n := GetNode(t, url1, url2)
|
|
|
|
ctx := context.Background()
|
|
|
|
v, err := n.Routing.GetValue(ctx, theID1)
|
|
require.NoError(err)
|
|
require.NotNil(v)
|
|
require.Contains(string(v), "RECORD FROM SERVICE 1")
|
|
|
|
v, err = n.Routing.GetValue(ctx, theID2)
|
|
require.NoError(err)
|
|
require.NotNil(v)
|
|
require.Contains(string(v), "RECORD FROM SERVICE 2")
|
|
|
|
err = n.Routing.PutValue(ctx, theID1, v)
|
|
require.Error(err)
|
|
|
|
err = n.Routing.PutValue(ctx, theID2, v)
|
|
require.Error(err)
|
|
}
|
|
|
|
func StartRoutingServer(t *testing.T, d drs.DelegatedRoutingService) string {
|
|
t.Helper()
|
|
|
|
f := drs.DelegatedRoutingAsyncHandler(d)
|
|
svr := httptest.NewServer(f)
|
|
t.Cleanup(func() {
|
|
svr.Close()
|
|
})
|
|
|
|
return svr.URL
|
|
}
|
|
|
|
func GetNode(t *testing.T, reframeURLs ...string) *IpfsNode {
|
|
t.Helper()
|
|
|
|
routers := make(map[string]config.Router)
|
|
for i, ru := range reframeURLs {
|
|
routers[fmt.Sprintf("reframe-%d", i)] = config.Router{
|
|
Type: string(config.RouterTypeReframe),
|
|
Parameters: map[string]string{
|
|
string(config.RouterParamEndpoint): ru,
|
|
},
|
|
}
|
|
}
|
|
|
|
cfg := config.Config{
|
|
Identity: testIdentity,
|
|
Addresses: config.Addresses{
|
|
Swarm: []string{"/ip4/0.0.0.0/tcp/0", "/ip4/0.0.0.0/udp/0/quic"},
|
|
API: []string{"/ip4/127.0.0.1/tcp/0"},
|
|
},
|
|
Routing: config.Routing{
|
|
Type: config.NewOptionalString("none"),
|
|
Routers: routers,
|
|
},
|
|
}
|
|
|
|
r := &repo.Mock{
|
|
C: cfg,
|
|
D: syncds.MutexWrap(datastore.NewMapDatastore()),
|
|
}
|
|
|
|
n, err := NewNode(context.Background(), &BuildCfg{Repo: r, Online: true, Routing: libp2p.NilRouterOption})
|
|
require.NoError(t, err)
|
|
|
|
return n
|
|
}
|
|
|
|
func GeneratePeerID() (peer.ID, crypto.PrivKey, error) {
|
|
priv, pk, err := crypto.GenerateEd25519Key(rand.Reader)
|
|
if err != nil {
|
|
return peer.ID(""), nil, err
|
|
}
|
|
|
|
pid, err := peer.IDFromPublicKey(pk)
|
|
return pid, priv, err
|
|
}
|
|
|
|
type delegatedRoutingService struct {
|
|
goodPeerID, badPeerID peer.ID
|
|
pk1 crypto.PrivKey
|
|
serviceID int
|
|
}
|
|
|
|
func (drs *delegatedRoutingService) FindProviders(ctx context.Context, key cid.Cid) (<-chan client.FindProvidersAsyncResult, error) {
|
|
return nil, errNotSupported
|
|
}
|
|
|
|
func (drs *delegatedRoutingService) GetIPNS(ctx context.Context, id []byte) (<-chan client.GetIPNSAsyncResult, error) {
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
ch := make(chan client.GetIPNSAsyncResult)
|
|
go func() {
|
|
defer close(ch)
|
|
defer cancel()
|
|
|
|
var out client.GetIPNSAsyncResult
|
|
switch peer.ID(id) {
|
|
case drs.goodPeerID:
|
|
ie, err := ipns.Create(drs.pk1, []byte(fmt.Sprintf("RECORD FROM SERVICE %d", drs.serviceID)), 0, time.Now().Add(10*time.Hour), 100*time.Hour)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
ieb, err := ie.Marshal()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
out = client.GetIPNSAsyncResult{
|
|
Record: ieb,
|
|
Err: nil,
|
|
}
|
|
case drs.badPeerID:
|
|
out = client.GetIPNSAsyncResult{
|
|
Record: nil,
|
|
Err: errors.New("THE ERROR"),
|
|
}
|
|
default:
|
|
return
|
|
}
|
|
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case ch <- out:
|
|
}
|
|
}()
|
|
|
|
return ch, nil
|
|
|
|
}
|
|
|
|
func (drs *delegatedRoutingService) PutIPNS(ctx context.Context, id []byte, record []byte) (<-chan client.PutIPNSAsyncResult, error) {
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
ch := make(chan client.PutIPNSAsyncResult)
|
|
go func() {
|
|
defer close(ch)
|
|
defer cancel()
|
|
|
|
var out client.PutIPNSAsyncResult
|
|
switch peer.ID(id) {
|
|
case drs.goodPeerID:
|
|
out = client.PutIPNSAsyncResult{}
|
|
case drs.badPeerID:
|
|
out = client.PutIPNSAsyncResult{
|
|
Err: fmt.Errorf("THE ERROR %d", drs.serviceID),
|
|
}
|
|
default:
|
|
return
|
|
}
|
|
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case ch <- out:
|
|
}
|
|
}()
|
|
|
|
return ch, nil
|
|
}
|