From e9e27cbbb0834c93d5b6df949382ac28d07e69ed Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Wed, 7 Oct 2020 13:59:32 +0200 Subject: [PATCH] feat: put gateway in middle of end-to-end test for lite mode --- cmd/lotus-gateway/api.go | 28 +++++++++++++++------------- cmd/lotus-gateway/api_test.go | 26 +++++++++++++------------- cmd/lotus-gateway/endtoend_test.go | 17 ++++++++++++++--- cmd/lotus-gateway/main.go | 2 +- node/test/builder.go | 4 ---- 5 files changed, 43 insertions(+), 34 deletions(-) diff --git a/cmd/lotus-gateway/api.go b/cmd/lotus-gateway/api.go index 0400a4a30..373c02eaa 100644 --- a/cmd/lotus-gateway/api.go +++ b/cmd/lotus-gateway/api.go @@ -23,7 +23,9 @@ var ( ErrLookbackTooLong = fmt.Errorf("lookbacks of more than %s are disallowed", LookbackCap) ) -type rpcAPI interface { +// gatewayDepsAPI defines the API methods that the GatewayAPI depends on +// (to make it easy to mock for tests) +type gatewayDepsAPI interface { ChainHead(ctx context.Context) (*types.TipSet, error) ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error) @@ -36,7 +38,7 @@ type rpcAPI interface { } type GatewayAPI struct { - rpc rpcAPI + api gatewayDepsAPI } func (a *GatewayAPI) checkTipsetKey(ctx context.Context, tsk types.TipSetKey) error { @@ -44,7 +46,7 @@ func (a *GatewayAPI) checkTipsetKey(ctx context.Context, tsk types.TipSetKey) er return nil } - ts, err := a.rpc.ChainGetTipSet(ctx, tsk) + ts, err := a.api.ChainGetTipSet(ctx, tsk) if err != nil { return err } @@ -82,15 +84,15 @@ func (a *GatewayAPI) checkTimestamp(at time.Time) error { func (a *GatewayAPI) ChainHead(ctx context.Context) (*types.TipSet, error) { // TODO: cache and invalidate cache when timestamp is up (or have internal ChainNotify) - return a.rpc.ChainHead(ctx) + return a.api.ChainHead(ctx) } func (a *GatewayAPI) ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) { - return a.rpc.ChainGetTipSet(ctx, tsk) + return a.api.ChainGetTipSet(ctx, tsk) } func (a *GatewayAPI) ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error) { - ts, err := a.rpc.ChainGetTipSet(ctx, tsk) + ts, err := a.api.ChainGetTipSet(ctx, tsk) if err != nil { return nil, err } @@ -105,7 +107,7 @@ func (a *GatewayAPI) ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpoc return nil, err } - return a.rpc.ChainGetTipSetByHeight(ctx, h, tsk) + return a.api.ChainGetTipSetByHeight(ctx, h, tsk) } func (a *GatewayAPI) GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) { @@ -113,12 +115,12 @@ func (a *GatewayAPI) GasEstimateMessageGas(ctx context.Context, msg *types.Messa return nil, err } - return a.rpc.GasEstimateMessageGas(ctx, msg, spec, tsk) + return a.api.GasEstimateMessageGas(ctx, msg, spec, tsk) } func (a *GatewayAPI) MpoolPush(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) { // TODO: additional anti-spam checks - return a.rpc.MpoolPushUntrusted(ctx, sm) + return a.api.MpoolPushUntrusted(ctx, sm) } func (a *GatewayAPI) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) { @@ -126,7 +128,7 @@ func (a *GatewayAPI) StateAccountKey(ctx context.Context, addr address.Address, return address.Undef, err } - return a.rpc.StateAccountKey(ctx, addr, tsk) + return a.api.StateAccountKey(ctx, addr, tsk) } func (a *GatewayAPI) StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) { @@ -134,7 +136,7 @@ func (a *GatewayAPI) StateGetActor(ctx context.Context, actor address.Address, t return nil, err } - return a.rpc.StateGetActor(ctx, actor, tsk) + return a.api.StateGetActor(ctx, actor, tsk) } func (a *GatewayAPI) StateLookupID(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) { @@ -142,11 +144,11 @@ func (a *GatewayAPI) StateLookupID(ctx context.Context, addr address.Address, ts return address.Undef, err } - return a.rpc.StateLookupID(ctx, addr, tsk) + return a.api.StateLookupID(ctx, addr, tsk) } func (a *GatewayAPI) StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64) (*api.MsgLookup, error) { - return a.rpc.StateWaitMsgLimited(ctx, msg, confidence, stateWaitLookbackLimit) + return a.api.StateWaitMsgLimited(ctx, msg, confidence, stateWaitLookbackLimit) } var _ api.GatewayAPI = (*GatewayAPI)(nil) diff --git a/cmd/lotus-gateway/api_test.go b/cmd/lotus-gateway/api_test.go index ab42c9310..2df2c9ba4 100644 --- a/cmd/lotus-gateway/api_test.go +++ b/cmd/lotus-gateway/api_test.go @@ -87,8 +87,8 @@ func TestGatewayAPIChainGetTipSetByHeight(t *testing.T) { for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { - mock := &mockRPCAPI{} - a := &GatewayAPI{rpc: mock} + mock := &mockGatewayDepsAPI{} + a := &GatewayAPI{api: mock} // Create tipsets from genesis up to tskh and return the highest ts := mock.createTipSets(tt.args.tskh, tt.args.genesisTS) @@ -104,19 +104,19 @@ func TestGatewayAPIChainGetTipSetByHeight(t *testing.T) { } } -type mockRPCAPI struct { +type mockGatewayDepsAPI struct { lk sync.RWMutex tipsets []*types.TipSet } -func (m *mockRPCAPI) ChainHead(ctx context.Context) (*types.TipSet, error) { +func (m *mockGatewayDepsAPI) 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) { +func (m *mockGatewayDepsAPI) ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) { m.lk.RLock() defer m.lk.RUnlock() @@ -130,7 +130,7 @@ func (m *mockRPCAPI) ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (* } // createTipSets creates tipsets from genesis up to tskh and returns the highest -func (m *mockRPCAPI) createTipSets(h abi.ChainEpoch, genesisTimestamp uint64) *types.TipSet { +func (m *mockGatewayDepsAPI) createTipSets(h abi.ChainEpoch, genesisTimestamp uint64) *types.TipSet { m.lk.Lock() defer m.lk.Unlock() @@ -151,33 +151,33 @@ func (m *mockRPCAPI) createTipSets(h abi.ChainEpoch, genesisTimestamp uint64) *t return m.tipsets[len(m.tipsets)-1] } -func (m *mockRPCAPI) ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error) { +func (m *mockGatewayDepsAPI) 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) { +func (m *mockGatewayDepsAPI) 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) { +func (m *mockGatewayDepsAPI) 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) { +func (m *mockGatewayDepsAPI) 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) { +func (m *mockGatewayDepsAPI) 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) { +func (m *mockGatewayDepsAPI) 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) { +func (m *mockGatewayDepsAPI) StateWaitMsgLimited(ctx context.Context, msg cid.Cid, confidence uint64, h abi.ChainEpoch) (*api.MsgLookup, error) { panic("implement me") } diff --git a/cmd/lotus-gateway/endtoend_test.go b/cmd/lotus-gateway/endtoend_test.go index e92c3248b..317e676d7 100644 --- a/cmd/lotus-gateway/endtoend_test.go +++ b/cmd/lotus-gateway/endtoend_test.go @@ -37,6 +37,8 @@ func init() { policy.SetMinVerifiedDealSize(abi.NewStoragePower(256)) } +// TestEndToEnd tests that API calls can be made on a lite node that is +// connected through a gateway to a full API node func TestEndToEnd(t *testing.T) { _ = os.Setenv("BELLMAN_NO_GPU", "1") @@ -66,10 +68,12 @@ func TestEndToEnd(t *testing.T) { err = sendFunds(ctx, t, lite, liteWalletAddr, fullWalletAddr, types.NewInt(100)) require.NoError(t, err) + // Sign some data with the lite node wallet address data := []byte("hello") sig, err := lite.WalletSign(ctx, liteWalletAddr, data) require.NoError(t, err) + // Verify the signature ok, err := lite.WalletVerify(ctx, liteWalletAddr, data, sig) require.NoError(t, err) require.True(t, ok) @@ -101,6 +105,10 @@ func sendFunds(ctx context.Context, t *testing.T, fromNode test.TestNode, fromAd func startNodes(ctx context.Context, t *testing.T, blocktime time.Duration) (test.TestNode, test.TestNode, jsonrpc.ClientCloser) { var closer jsonrpc.ClientCloser + // Create one miner and two full nodes. + // - Put a gateway server in front of full node 1 + // - Start full node 2 in lite mode + // - Connect lite node -> gateway server -> full node opts := append( // Full node test.OneFull, @@ -108,17 +116,20 @@ func startNodes(ctx context.Context, t *testing.T, blocktime time.Duration) (tes func(nodes []test.TestNode) node.Option { fullNode := nodes[0] - addr, err := builder.WSMultiAddrToString(fullNode.ListenAddr) + // Create a gateway server in front of the full node + _, addr, err := builder.CreateRPCServer(&GatewayAPI{api: fullNode}) require.NoError(t, err) - // Create a gateway API that connects to the full node + // Create a gateway client API that connects to the gateway server var gapi api.GatewayAPI gapi, closer, err = client.NewGatewayRPC(ctx, addr, nil) require.NoError(t, err) + + // Override this node with lite-mode options return node.LiteModeOverrides(gapi) }, ) - n, sn := builder.RPCMockSbBuilderWithOpts(t, opts, test.OneMiner) + n, sn := builder.RPCMockSbBuilder(t, opts, test.OneMiner) full := n[0] lite := n[1] diff --git a/cmd/lotus-gateway/main.go b/cmd/lotus-gateway/main.go index 3c8eb4019..c19599084 100644 --- a/cmd/lotus-gateway/main.go +++ b/cmd/lotus-gateway/main.go @@ -76,7 +76,7 @@ var runCmd = &cli.Command{ log.Info("Setting up API endpoint at " + address) rpcServer := jsonrpc.NewServer() - rpcServer.Register("Filecoin", &GatewayAPI{rpc: api}) + rpcServer.Register("Filecoin", &GatewayAPI{api: api}) mux.Handle("/rpc/v0", rpcServer) mux.PathPrefix("/").Handler(http.DefaultServeMux) diff --git a/node/test/builder.go b/node/test/builder.go index f38201697..ebf96e98f 100644 --- a/node/test/builder.go +++ b/node/test/builder.go @@ -157,10 +157,6 @@ func RPCMockSbBuilder(t *testing.T, fullOpts []test.OptionGenerator, storage []t 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)