feat: add end-to-end test for lite mode

This commit is contained in:
Dirk McCormick 2020-10-07 11:26:15 +02:00
parent 7b1bec91ed
commit ef73b964fb
17 changed files with 510 additions and 99 deletions

View File

@ -37,7 +37,7 @@ func TestCCUpgrade(t *testing.T, b APIBuilder, blocktime time.Duration) {
func testCCUpgrade(t *testing.T, b APIBuilder, blocktime time.Duration, upgradeHeight abi.ChainEpoch) {
ctx := context.Background()
n, sn := b(t, 1, OneMiner, node.Override(new(stmgr.UpgradeSchedule), stmgr.UpgradeSchedule{{
n, sn := b(t, OneFull, OneMiner, node.Override(new(stmgr.UpgradeSchedule), stmgr.UpgradeSchedule{{
Network: build.ActorUpgradeNetworkVersion,
Height: upgradeHeight,
Migration: stmgr.UpgradeActorsV2,

View File

@ -48,7 +48,7 @@ func TestDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration, carExport
_ = os.Setenv("BELLMAN_NO_GPU", "1")
ctx := context.Background()
n, sn := b(t, 1, OneMiner)
n, sn := b(t, OneFull, OneMiner)
client := n[0].FullNode.(*impl.FullNodeAPI)
miner := sn[0]
@ -85,7 +85,7 @@ func TestDoubleDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration) {
_ = os.Setenv("BELLMAN_NO_GPU", "1")
ctx := context.Background()
n, sn := b(t, 1, OneMiner)
n, sn := b(t, OneFull, OneMiner)
client := n[0].FullNode.(*impl.FullNodeAPI)
miner := sn[0]
@ -149,7 +149,7 @@ func TestFastRetrievalDealFlow(t *testing.T, b APIBuilder, blocktime time.Durati
_ = os.Setenv("BELLMAN_NO_GPU", "1")
ctx := context.Background()
n, sn := b(t, 1, OneMiner)
n, sn := b(t, OneFull, OneMiner)
client := n[0].FullNode.(*impl.FullNodeAPI)
miner := sn[0]
@ -204,7 +204,7 @@ func TestSenondDealRetrieval(t *testing.T, b APIBuilder, blocktime time.Duration
_ = os.Setenv("BELLMAN_NO_GPU", "1")
ctx := context.Background()
n, sn := b(t, 1, OneMiner)
n, sn := b(t, OneFull, OneMiner)
client := n[0].FullNode.(*impl.FullNodeAPI)
miner := sn[0]

View File

@ -25,7 +25,7 @@ var log = logging.Logger("apitest")
func (ts *testSuite) testMining(t *testing.T) {
ctx := context.Background()
apis, sn := ts.makeNodes(t, 1, OneMiner)
apis, sn := ts.makeNodes(t, OneFull, OneMiner)
api := apis[0]
newHeads, err := api.ChainNotify(ctx)
@ -54,7 +54,7 @@ func (ts *testSuite) testMiningReal(t *testing.T) {
}()
ctx := context.Background()
apis, sn := ts.makeNodes(t, 1, OneMiner)
apis, sn := ts.makeNodes(t, OneFull, OneMiner)
api := apis[0]
newHeads, err := api.ChainNotify(ctx)
@ -93,7 +93,7 @@ func TestDealMining(t *testing.T, b APIBuilder, blocktime time.Duration, carExpo
// test making a deal with a fresh miner, and see if it starts to mine
ctx := context.Background()
n, sn := b(t, 1, []StorageMiner{
n, sn := b(t, OneFull, []StorageMiner{
{Full: 0, Preseal: PresealGenesis},
{Full: 0, Preseal: 0}, // TODO: Add support for miners on non-first full node
})

View File

@ -33,7 +33,7 @@ func TestPaymentChannels(t *testing.T, b APIBuilder, blocktime time.Duration) {
_ = os.Setenv("BELLMAN_NO_GPU", "1")
ctx := context.Background()
n, sn := b(t, 2, OneMiner)
n, sn := b(t, TwoFull, OneMiner)
paymentCreator := n[0]
paymentReceiver := n[1]

View File

@ -40,12 +40,14 @@ type StorageMiner struct {
Preseal int
}
type OptionGenerator func([]TestNode) node.Option
// APIBuilder is a function which is invoked in test suite to provide
// test nodes and networks
//
// storage array defines storage nodes, numbers in the array specify full node
// index the storage node 'belongs' to
type APIBuilder func(t *testing.T, nFull int, storage []StorageMiner, opts ...node.Option) ([]TestNode, []TestStorageNode)
type APIBuilder func(t *testing.T, full []OptionGenerator, storage []StorageMiner, opts ...node.Option) ([]TestNode, []TestStorageNode)
type testSuite struct {
makeNodes APIBuilder
}
@ -63,13 +65,25 @@ func TestApis(t *testing.T, b APIBuilder) {
t.Run("testMiningReal", ts.testMiningReal)
}
func DefaultFullOpts(nFull int) []OptionGenerator {
full := make([]OptionGenerator, nFull)
for i := range full {
full[i] = func(nodes []TestNode) node.Option {
return node.Options()
}
}
return full
}
var OneMiner = []StorageMiner{{Full: 0, Preseal: PresealGenesis}}
var OneFull = DefaultFullOpts(1)
var TwoFull = DefaultFullOpts(2)
func (ts *testSuite) testVersion(t *testing.T) {
build.RunningNodeType = build.NodeFull
ctx := context.Background()
apis, _ := ts.makeNodes(t, 1, OneMiner)
apis, _ := ts.makeNodes(t, OneFull, OneMiner)
api := apis[0]
v, err := api.Version(ctx)
@ -81,7 +95,7 @@ func (ts *testSuite) testVersion(t *testing.T) {
func (ts *testSuite) testID(t *testing.T) {
ctx := context.Background()
apis, _ := ts.makeNodes(t, 1, OneMiner)
apis, _ := ts.makeNodes(t, OneFull, OneMiner)
api := apis[0]
id, err := api.ID(ctx)
@ -93,7 +107,7 @@ func (ts *testSuite) testID(t *testing.T) {
func (ts *testSuite) testConnectTwo(t *testing.T) {
ctx := context.Background()
apis, _ := ts.makeNodes(t, 2, OneMiner)
apis, _ := ts.makeNodes(t, TwoFull, OneMiner)
p, err := apis[0].NetPeers(ctx)
if err != nil {

View File

@ -34,7 +34,7 @@ func init() {
func TestPledgeSector(t *testing.T, b APIBuilder, blocktime time.Duration, nSectors int) {
ctx := context.Background()
n, sn := b(t, 1, OneMiner)
n, sn := b(t, OneFull, OneMiner)
client := n[0].FullNode.(*impl.FullNodeAPI)
miner := sn[0]
@ -133,7 +133,7 @@ func testWindowPostUpgrade(t *testing.T, b APIBuilder, blocktime time.Duration,
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
n, sn := b(t, 1, OneMiner, node.Override(new(stmgr.UpgradeSchedule), stmgr.UpgradeSchedule{{
n, sn := b(t, OneFull, OneMiner, node.Override(new(stmgr.UpgradeSchedule), stmgr.UpgradeSchedule{{
Network: build.ActorUpgradeNetworkVersion,
Height: upgradeHeight,
Migration: stmgr.UpgradeActorsV2,

View File

@ -6,7 +6,6 @@ import (
"sync"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/chain/messagepool"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/wallet"
"github.com/filecoin-project/lotus/node/modules/dtypes"
@ -21,7 +20,7 @@ const dsKeyActorNonce = "ActorNextNonce"
var log = logging.Logger("messagesigner")
type mpoolAPI interface {
type MpoolNonceAPI interface {
GetNonce(address.Address) (uint64, error)
}
@ -30,15 +29,11 @@ type mpoolAPI interface {
type MessageSigner struct {
wallet *wallet.Wallet
lk sync.Mutex
mpool mpoolAPI
mpool MpoolNonceAPI
ds datastore.Batching
}
func NewMessageSigner(wallet *wallet.Wallet, mpool *messagepool.MessagePool, ds dtypes.MetadataDS) *MessageSigner {
return newMessageSigner(wallet, mpool, ds)
}
func newMessageSigner(wallet *wallet.Wallet, mpool mpoolAPI, ds dtypes.MetadataDS) *MessageSigner {
func NewMessageSigner(wallet *wallet.Wallet, mpool MpoolNonceAPI, ds dtypes.MetadataDS) *MessageSigner {
ds = namespace.Wrap(ds, datastore.NewKey("/message-signer/"))
return &MessageSigner{
wallet: wallet,

View File

@ -177,7 +177,7 @@ func TestMessageSignerSignMessage(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
mpool := newMockMpool()
ds := ds_sync.MutexWrap(datastore.NewMapDatastore())
ms := newMessageSigner(w, mpool, ds)
ms := NewMessageSigner(w, mpool, ds)
for _, m := range tt.msgs {
if len(m.mpoolNonce) == 1 {

View File

@ -390,7 +390,7 @@ func checkVoucherOutput(t *testing.T, list string, vouchers []voucherSpec) {
}
func startTwoNodesOneMiner(ctx context.Context, t *testing.T, blocktime time.Duration) ([]test.TestNode, []address.Address) {
n, sn := builder.RPCMockSbBuilder(t, 2, test.OneMiner)
n, sn := builder.RPCMockSbBuilder(t, test.TwoFull, test.OneMiner)
paymentCreator := n[0]
paymentReceiver := n[1]

View File

@ -0,0 +1,183 @@
package main
import (
"context"
"sync"
"testing"
"time"
"github.com/filecoin-project/lotus/build"
"github.com/stretchr/testify/require"
"github.com/filecoin-project/lotus/chain/types/mock"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/types"
"github.com/ipfs/go-cid"
)
func TestGatewayAPIChainGetTipSetByHeight(t *testing.T) {
ctx := context.Background()
lookbackTimestamp := uint64(time.Now().Unix()) - uint64(LookbackCap.Seconds())
type args struct {
h abi.ChainEpoch
tskh abi.ChainEpoch
genesisTS uint64
}
tests := []struct {
name string
args args
expErr bool
}{{
name: "basic",
args: args{
h: abi.ChainEpoch(1),
tskh: abi.ChainEpoch(5),
},
}, {
name: "genesis",
args: args{
h: abi.ChainEpoch(0),
tskh: abi.ChainEpoch(5),
},
}, {
name: "same epoch as tipset",
args: args{
h: abi.ChainEpoch(5),
tskh: abi.ChainEpoch(5),
},
}, {
name: "tipset too old",
args: args{
// Tipset height is 5, genesis is at LookbackCap - 10 epochs.
// So resulting tipset height will be 5 epochs earlier than LookbackCap.
h: abi.ChainEpoch(1),
tskh: abi.ChainEpoch(5),
genesisTS: lookbackTimestamp - build.BlockDelaySecs*10,
},
expErr: true,
}, {
name: "lookup height too old",
args: args{
// Tipset height is 5, lookup height is 1, genesis is at LookbackCap - 3 epochs.
// So
// - lookup height will be 2 epochs earlier than LookbackCap.
// - tipset height will be 2 epochs later than LookbackCap.
h: abi.ChainEpoch(1),
tskh: abi.ChainEpoch(5),
genesisTS: lookbackTimestamp - build.BlockDelaySecs*3,
},
expErr: true,
}, {
name: "tipset and lookup height within acceptable range",
args: args{
// Tipset height is 5, lookup height is 1, genesis is at LookbackCap.
// So
// - lookup height will be 1 epoch later than LookbackCap.
// - tipset height will be 5 epochs later than LookbackCap.
h: abi.ChainEpoch(1),
tskh: abi.ChainEpoch(5),
genesisTS: lookbackTimestamp,
},
}}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
mock := &mockRPCAPI{}
a := &GatewayAPI{rpc: mock}
// Create tipsets from genesis up to tskh and return the highest
ts := mock.createTipSets(tt.args.tskh, tt.args.genesisTS)
got, err := a.ChainGetTipSetByHeight(ctx, tt.args.h, ts.Key())
if tt.expErr {
require.Error(t, err)
} else {
require.NoError(t, err)
require.Equal(t, tt.args.h, got.Height())
}
})
}
}
type mockRPCAPI struct {
lk sync.RWMutex
tipsets []*types.TipSet
}
func (m *mockRPCAPI) ChainHead(ctx context.Context) (*types.TipSet, error) {
m.lk.RLock()
defer m.lk.RUnlock()
return m.tipsets[len(m.tipsets)-1], nil
}
func (m *mockRPCAPI) ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) {
m.lk.RLock()
defer m.lk.RUnlock()
for _, ts := range m.tipsets {
if ts.Key() == tsk {
return ts, nil
}
}
return nil, nil
}
// createTipSets creates tipsets from genesis up to tskh and returns the highest
func (m *mockRPCAPI) createTipSets(h abi.ChainEpoch, genesisTimestamp uint64) *types.TipSet {
m.lk.Lock()
defer m.lk.Unlock()
targeth := h + 1 // add one for genesis block
if genesisTimestamp == 0 {
genesisTimestamp = uint64(time.Now().Unix()) - build.BlockDelaySecs*uint64(targeth)
}
var currts *types.TipSet
for currh := abi.ChainEpoch(0); currh < targeth; currh++ {
blks := mock.MkBlock(currts, 1, 1)
if currh == 0 {
blks.Timestamp = genesisTimestamp
}
currts = mock.TipSet(blks)
m.tipsets = append(m.tipsets, currts)
}
return m.tipsets[len(m.tipsets)-1]
}
func (m *mockRPCAPI) ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error) {
m.lk.Lock()
defer m.lk.Unlock()
return m.tipsets[h], nil
}
func (m *mockRPCAPI) GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) {
panic("implement me")
}
func (m *mockRPCAPI) MpoolPushUntrusted(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) {
panic("implement me")
}
func (m *mockRPCAPI) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) {
panic("implement me")
}
func (m *mockRPCAPI) StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) {
panic("implement me")
}
func (m *mockRPCAPI) StateLookupID(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) {
panic("implement me")
}
func (m *mockRPCAPI) StateWaitMsgLimited(ctx context.Context, msg cid.Cid, confidence uint64, h abi.ChainEpoch) (*api.MsgLookup, error) {
panic("implement me")
}

View File

@ -0,0 +1,140 @@
package main
import (
"context"
"fmt"
"os"
"testing"
"time"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/node"
"github.com/filecoin-project/lotus/api/client"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/lotus/chain/wallet"
"github.com/stretchr/testify/require"
"github.com/filecoin-project/go-state-types/abi"
builder "github.com/filecoin-project/lotus/node/test"
"github.com/filecoin-project/lotus/api/test"
"github.com/filecoin-project/lotus/chain/actors/policy"
"github.com/filecoin-project/lotus/chain/types"
)
func init() {
policy.SetSupportedProofTypes(abi.RegisteredSealProof_StackedDrg2KiBV1)
policy.SetConsensusMinerMinPower(abi.NewStoragePower(2048))
policy.SetMinVerifiedDealSize(abi.NewStoragePower(256))
}
func TestEndToEnd(t *testing.T) {
_ = os.Setenv("BELLMAN_NO_GPU", "1")
blocktime := 5 * time.Millisecond
ctx := context.Background()
full, lite, closer := startNodes(ctx, t, blocktime)
defer closer()
// The full node starts with a wallet
fullWalletAddr, err := full.WalletDefaultAddress(ctx)
require.NoError(t, err)
// Check the full node's wallet balance from the lite node
balance, err := lite.WalletBalance(ctx, fullWalletAddr)
require.NoError(t, err)
fmt.Println(balance)
// Create a wallet on the lite node
liteWalletAddr, err := lite.WalletNew(ctx, wallet.ActSigType("secp256k1"))
require.NoError(t, err)
// Send some funds from the full node to the lite node
err = sendFunds(ctx, t, full, fullWalletAddr, liteWalletAddr, types.NewInt(1e18))
require.NoError(t, err)
// Send some funds from the lite node back to the full node
err = sendFunds(ctx, t, lite, liteWalletAddr, fullWalletAddr, types.NewInt(100))
require.NoError(t, err)
data := []byte("hello")
sig, err := lite.WalletSign(ctx, liteWalletAddr, data)
require.NoError(t, err)
ok, err := lite.WalletVerify(ctx, liteWalletAddr, data, sig)
require.NoError(t, err)
require.True(t, ok)
}
func sendFunds(ctx context.Context, t *testing.T, fromNode test.TestNode, fromAddr address.Address, toAddr address.Address, amt types.BigInt) error {
msg := &types.Message{
From: fromAddr,
To: toAddr,
Value: amt,
}
sm, err := fromNode.MpoolPushMessage(ctx, msg, nil)
if err != nil {
return err
}
res, err := fromNode.StateWaitMsg(ctx, sm.Cid(), 1)
if err != nil {
return err
}
if res.Receipt.ExitCode != 0 {
return xerrors.Errorf("send funds failed with exit code %d", res.Receipt.ExitCode)
}
return nil
}
func startNodes(ctx context.Context, t *testing.T, blocktime time.Duration) (test.TestNode, test.TestNode, jsonrpc.ClientCloser) {
var closer jsonrpc.ClientCloser
opts := append(
// Full node
test.OneFull,
// Lite node
func(nodes []test.TestNode) node.Option {
fullNode := nodes[0]
addr, err := builder.WSMultiAddrToString(fullNode.ListenAddr)
require.NoError(t, err)
// Create a gateway API that connects to the full node
var gapi api.GatewayAPI
gapi, closer, err = client.NewGatewayRPC(ctx, addr, nil)
require.NoError(t, err)
return node.LiteModeOverrides(gapi)
},
)
n, sn := builder.RPCMockSbBuilderWithOpts(t, opts, test.OneMiner)
full := n[0]
lite := n[1]
miner := sn[0]
// Get the listener address for the full node
fullAddr, err := full.NetAddrsListen(ctx)
require.NoError(t, err)
// Connect the miner and the full node
err = miner.NetConnect(ctx, fullAddr)
require.NoError(t, err)
// Start mining blocks
bm := test.NewBlockMiner(ctx, t, miner, blocktime)
bm.MineBlocks()
return full, lite, closer
}

View File

@ -64,8 +64,8 @@ func TestMinerAllInfo(t *testing.T) {
require.NoError(t, infoAllCmd.Action(cctx))
}
bp := func(t *testing.T, nFull int, storage []test.StorageMiner, opts ...node.Option) ([]test.TestNode, []test.TestStorageNode) {
n, sn = builder.Builder(t, nFull, storage, opts...)
bp := func(t *testing.T, fullOpts []test.OptionGenerator, storage []test.StorageMiner, opts ...node.Option) ([]test.TestNode, []test.TestStorageNode) {
n, sn = builder.Builder(t, fullOpts, storage, opts...)
t.Run("pre-info-all", run)

View File

@ -39,7 +39,6 @@ import (
"github.com/filecoin-project/lotus/lib/ulimit"
"github.com/filecoin-project/lotus/metrics"
"github.com/filecoin-project/lotus/node"
"github.com/filecoin-project/lotus/node/impl/full"
"github.com/filecoin-project/lotus/node/modules"
"github.com/filecoin-project/lotus/node/modules/dtypes"
"github.com/filecoin-project/lotus/node/modules/testing"
@ -255,20 +254,7 @@ var DaemonCmd = &cli.Command{
}
defer closer()
liteMode = node.Options(
node.Override(new(api.GatewayAPI), gapi),
node.Override(new(full.ChainModuleAPI), node.From(new(api.GatewayAPI))),
node.Override(new(full.GasModuleAPI), node.From(new(api.GatewayAPI))),
node.Override(new(full.MpoolModuleAPI), node.From(new(api.GatewayAPI))),
node.Override(new(full.StateModuleAPI), node.From(new(api.GatewayAPI))),
node.Override(new(stmgr.StateManagerAPI), modules.NewRPCStateManager),
node.Unset(node.RunHelloKey),
node.Unset(node.RunChainExchangeKey),
node.Unset(node.RunPeerMgrKey),
node.Unset(node.HandleIncomingBlocksKey),
node.Unset(node.HandleIncomingMessagesKey),
)
liteMode = node.LiteModeOverrides(gapi)
}
var api api.FullNode

View File

@ -264,6 +264,7 @@ func Online() Option {
Override(new(*stmgr.StateManager), stmgr.NewStateManagerWithUpgradeSchedule),
Override(new(stmgr.StateManagerAPI), From(new(*stmgr.StateManager))),
Override(new(*wallet.Wallet), wallet.NewWallet),
Override(new(messagesigner.MpoolNonceAPI), From(new(*messagepool.MessagePool))),
Override(new(*messagesigner.MessageSigner), messagesigner.NewMessageSigner),
Override(new(full.ChainModuleAPI), From(new(full.ChainModule))),
@ -401,6 +402,23 @@ func StorageMiner(out *api.StorageMiner) Option {
)
}
func LiteModeOverrides(gapi api.GatewayAPI) Option {
return Options(
Override(new(messagesigner.MpoolNonceAPI), From(new(modules.MpoolNonceAPI))),
Override(new(api.GatewayAPI), gapi),
Override(new(full.ChainModuleAPI), From(new(api.GatewayAPI))),
Override(new(full.GasModuleAPI), From(new(api.GatewayAPI))),
Override(new(full.MpoolModuleAPI), From(new(api.GatewayAPI))),
Override(new(full.StateModuleAPI), From(new(api.GatewayAPI))),
Override(new(stmgr.StateManagerAPI), modules.NewRPCStateManager),
Unset(RunHelloKey),
Unset(RunChainExchangeKey),
Unset(RunPeerMgrKey),
Unset(HandleIncomingBlocksKey),
Unset(HandleIncomingMessagesKey),
)
}
// Config sets up constructors based on the provided Config
func ConfigCommon(cfg *config.Common) Option {
return Options(

View File

@ -180,7 +180,7 @@ func (a *MpoolAPI) MpoolPushMessage(ctx context.Context, msg *types.Message, spe
// Sign and push the message
return a.MessageSigner.SignMessage(ctx, msg, func(smsg *types.SignedMessage) error {
if _, err := a.Mpool.Push(smsg); err != nil {
if _, err := a.MpoolModuleAPI.MpoolPush(ctx, smsg); err != nil {
return xerrors.Errorf("mpool push: failed to push message: %w", err)
}
return nil

View File

@ -0,0 +1,33 @@
package modules
import (
"context"
"go.uber.org/fx"
"github.com/filecoin-project/lotus/node/impl/full"
"github.com/filecoin-project/lotus/chain/messagesigner"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/go-address"
)
// MpoolNonceAPI substitutes the mpool nonce with an implementation that
// doesn't rely on the mpool - it just gets the nonce from actor state
type MpoolNonceAPI struct {
fx.In
StateAPI full.StateAPI
}
// GetNonce gets the nonce from actor state
func (a *MpoolNonceAPI) GetNonce(addr address.Address) (uint64, error) {
act, err := a.StateAPI.StateGetActor(context.Background(), addr, types.EmptyTSK)
if err != nil {
return 0, err
}
return act.Nonce, nil
}
var _ messagesigner.MpoolNonceAPI = (*MpoolNonceAPI)(nil)

View File

@ -7,10 +7,13 @@ import (
"io/ioutil"
"net"
"net/http/httptest"
"strings"
"sync"
"testing"
"time"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/go-state-types/abi"
@ -117,6 +120,7 @@ func CreateTestStorageNode(ctx context.Context, t *testing.T, waddr address.Addr
if err != nil {
t.Fatalf("failed to construct node: %v", err)
}
t.Cleanup(func() { _ = stop(context.Background()) })
/*// Bootstrap with full node
@ -137,13 +141,33 @@ func CreateTestStorageNode(ctx context.Context, t *testing.T, waddr address.Addr
return test.TestStorageNode{StorageMiner: minerapi, MineOne: mineOne}
}
func Builder(t *testing.T, nFull int, storage []test.StorageMiner, opts ...node.Option) ([]test.TestNode, []test.TestStorageNode) {
func Builder(t *testing.T, fullOpts []test.OptionGenerator, storage []test.StorageMiner, opts ...node.Option) ([]test.TestNode, []test.TestStorageNode) {
return mockBuilderOpts(t, fullOpts, storage, opts, false)
}
func MockSbBuilder(t *testing.T, fullOpts []test.OptionGenerator, storage []test.StorageMiner, opts ...node.Option) ([]test.TestNode, []test.TestStorageNode) {
return mockSbBuilderOpts(t, fullOpts, storage, opts, false)
}
func RPCBuilder(t *testing.T, fullOpts []test.OptionGenerator, storage []test.StorageMiner, opts ...node.Option) ([]test.TestNode, []test.TestStorageNode) {
return mockBuilderOpts(t, fullOpts, storage, opts, true)
}
func RPCMockSbBuilder(t *testing.T, fullOpts []test.OptionGenerator, storage []test.StorageMiner) ([]test.TestNode, []test.TestStorageNode) {
return mockSbBuilderOpts(t, fullOpts, storage, []node.Option{}, true)
}
func RPCMockSbBuilderWithOpts(t *testing.T, fullOpts []test.OptionGenerator, storage []test.StorageMiner) ([]test.TestNode, []test.TestStorageNode) {
return mockSbBuilderOpts(t, fullOpts, storage, []node.Option{}, true)
}
func mockBuilderOpts(t *testing.T, fullOpts []test.OptionGenerator, storage []test.StorageMiner, opts []node.Option, rpc bool) ([]test.TestNode, []test.TestStorageNode) {
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
mn := mocknet.New(ctx)
fulls := make([]test.TestNode, nFull)
fulls := make([]test.TestNode, len(fullOpts))
storers := make([]test.TestStorageNode, len(storage))
pk, _, err := crypto.GenerateEd25519Key(rand.Reader)
@ -208,7 +232,7 @@ func Builder(t *testing.T, nFull int, storage []test.StorageMiner, opts ...node.
// END PRESEAL SECTION
for i := 0; i < nFull; i++ {
for i := 0; i < len(fullOpts); i++ {
var genesis node.Option
if i == 0 {
genesis = node.Override(new(modules.Genesis), testing2.MakeGenesisMem(&genbuf, *templ))
@ -224,12 +248,18 @@ func Builder(t *testing.T, nFull int, storage []test.StorageMiner, opts ...node.
node.Test(),
genesis,
fullOpts[i](fulls),
node.Options(opts...),
)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = stop(context.Background()) })
if rpc {
fulls[i] = fullRpc(t, fulls[i])
}
}
for i, def := range storage {
@ -261,6 +291,9 @@ func Builder(t *testing.T, nFull int, storage []test.StorageMiner, opts ...node.
psd := presealDirs[i]
*/
if rpc {
storers[i] = storerRpc(t, storers[i])
}
}
if err := mn.LinkAll(); err != nil {
@ -286,11 +319,13 @@ func Builder(t *testing.T, nFull int, storage []test.StorageMiner, opts ...node.
return fulls, storers
}
func MockSbBuilder(t *testing.T, nFull int, storage []test.StorageMiner, options ...node.Option) ([]test.TestNode, []test.TestStorageNode) {
ctx := context.Background()
func mockSbBuilderOpts(t *testing.T, fullOpts []test.OptionGenerator, storage []test.StorageMiner, options []node.Option, rpc bool) ([]test.TestNode, []test.TestStorageNode) {
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
mn := mocknet.New(ctx)
fulls := make([]test.TestNode, nFull)
fulls := make([]test.TestNode, len(fullOpts))
storers := make([]test.TestStorageNode, len(storage))
var genbuf bytes.Buffer
@ -354,7 +389,7 @@ func MockSbBuilder(t *testing.T, nFull int, storage []test.StorageMiner, options
// END PRESEAL SECTION
for i := 0; i < nFull; i++ {
for i := 0; i < len(fullOpts); i++ {
var genesis node.Option
if i == 0 {
genesis = node.Override(new(modules.Genesis), testing2.MakeGenesisMem(&genbuf, *templ))
@ -374,12 +409,18 @@ func MockSbBuilder(t *testing.T, nFull int, storage []test.StorageMiner, options
node.Override(new(ffiwrapper.Verifier), mock.MockVerifier),
genesis,
fullOpts[i](fulls),
node.Options(options...),
)
if err != nil {
t.Fatalf("%+v", err)
}
t.Cleanup(func() { _ = stop(context.Background()) })
if rpc {
fulls[i] = fullRpc(t, fulls[i])
}
}
for i, def := range storage {
@ -414,6 +455,10 @@ func MockSbBuilder(t *testing.T, nFull int, storage []test.StorageMiner, options
node.Override(new(ffiwrapper.Verifier), mock.MockVerifier),
node.Unset(new(*sectorstorage.Manager)),
))
if rpc {
storers[i] = storerRpc(t, storers[i])
}
}
if err := mn.LinkAll(); err != nil {
@ -438,69 +483,66 @@ func MockSbBuilder(t *testing.T, nFull int, storage []test.StorageMiner, options
return fulls, storers
}
func RPCBuilder(t *testing.T, nFull int, storage []test.StorageMiner, opts ...node.Option) ([]test.TestNode, []test.TestStorageNode) {
return rpcWithBuilder(t, Builder, nFull, storage, opts...)
func fullRpc(t *testing.T, nd test.TestNode) test.TestNode {
ma, listenAddr, err := CreateRPCServer(nd)
require.NoError(t, err)
var full test.TestNode
full.FullNode, _, err = client.NewFullNodeRPC(context.Background(), listenAddr, nil)
require.NoError(t, err)
full.ListenAddr = ma
return full
}
func RPCMockSbBuilder(t *testing.T, nFull int, storage []test.StorageMiner, opts ...node.Option) ([]test.TestNode, []test.TestStorageNode) {
return rpcWithBuilder(t, MockSbBuilder, nFull, storage, opts...)
func storerRpc(t *testing.T, nd test.TestStorageNode) test.TestStorageNode {
ma, listenAddr, err := CreateRPCServer(nd)
require.NoError(t, err)
var storer test.TestStorageNode
storer.StorageMiner, _, err = client.NewStorageMinerRPC(context.Background(), listenAddr, nil)
require.NoError(t, err)
storer.ListenAddr = ma
storer.MineOne = nd.MineOne
return storer
}
func rpcWithBuilder(t *testing.T, b test.APIBuilder, nFull int, storage []test.StorageMiner, opts ...node.Option) ([]test.TestNode, []test.TestStorageNode) {
fullApis, storaApis := b(t, nFull, storage, opts...)
fulls := make([]test.TestNode, nFull)
storers := make([]test.TestStorageNode, len(storage))
for i, a := range fullApis {
func CreateRPCServer(handler interface{}) (multiaddr.Multiaddr, string, error) {
rpcServer := jsonrpc.NewServer()
rpcServer.Register("Filecoin", a)
rpcServer.Register("Filecoin", handler)
testServ := httptest.NewServer(rpcServer) // todo: close
addr := testServ.Listener.Addr()
listenAddr := "ws://" + addr.String()
var err error
fulls[i].FullNode, _, err = client.NewFullNodeRPC(context.Background(), listenAddr, nil)
ma, err := parseWSMultiAddr(addr)
if err != nil {
t.Fatal(err)
return nil, "", err
}
ma, err := parseWSSMultiAddr(addr)
if err != nil {
t.Fatal(err)
}
fulls[i].ListenAddr = ma
return ma, listenAddr, err
}
for i, a := range storaApis {
rpcServer := jsonrpc.NewServer()
rpcServer.Register("Filecoin", a)
testServ := httptest.NewServer(rpcServer) // todo: close
addr := testServ.Listener.Addr()
listenAddr := "ws://" + addr.String()
var err error
storers[i].StorageMiner, _, err = client.NewStorageMinerRPC(context.Background(), listenAddr, nil)
if err != nil {
t.Fatal(err)
}
ma, err := parseWSSMultiAddr(addr)
if err != nil {
t.Fatal(err)
}
storers[i].ListenAddr = ma
storers[i].MineOne = a.MineOne
}
return fulls, storers
}
func parseWSSMultiAddr(addr net.Addr) (multiaddr.Multiaddr, error) {
func parseWSMultiAddr(addr net.Addr) (multiaddr.Multiaddr, error) {
host, port, err := net.SplitHostPort(addr.String())
if err != nil {
return nil, err
}
ma, err := multiaddr.NewMultiaddr("/ip4/" + host + "/" + addr.Network() + "/" + port + "/wss")
ma, err := multiaddr.NewMultiaddr("/ip4/" + host + "/" + addr.Network() + "/" + port + "/ws")
if err != nil {
return nil, err
}
return ma, nil
}
func WSMultiAddrToString(addr multiaddr.Multiaddr) (string, error) {
parts := strings.Split(addr.String(), "/")
if len(parts) != 6 || parts[0] != "" {
return "", xerrors.Errorf("Malformed ws multiaddr %s", addr)
}
host := parts[2]
port := parts[4]
proto := parts[5]
return proto + "://" + host + ":" + port + "/rpc/v0", nil
}