From e19cd9ed01fbd5c633ac6542e8be6d1ffe2c77c4 Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Tue, 29 Sep 2020 17:25:45 +0200 Subject: [PATCH 01/18] feat: lotus-lite - replace wallet StateManager with thin client to gateway --- api/api_gateway.go | 17 +++++++++++++++++ api/apistruct/struct.go | 32 ++++++++++++++++++++++++++++++++ api/client/client.go | 14 ++++++++++++++ chain/stmgr/stmgr.go | 7 +++++++ cli/cmd.go | 9 +++++++++ cmd/lotus-gateway/api.go | 33 ++++++++++++++++++++++----------- cmd/lotus/daemon.go | 28 ++++++++++++++++++++++++++++ node/builder.go | 1 + node/impl/full/wallet.go | 8 ++++---- node/modules/statemanager.go | 29 +++++++++++++++++++++++++++++ 10 files changed, 163 insertions(+), 15 deletions(-) create mode 100644 api/api_gateway.go create mode 100644 node/modules/statemanager.go diff --git a/api/api_gateway.go b/api/api_gateway.go new file mode 100644 index 000000000..6a220e9ea --- /dev/null +++ b/api/api_gateway.go @@ -0,0 +1,17 @@ +package api + +import ( + "context" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/lotus/chain/types" + "github.com/ipfs/go-cid" +) + +type GatewayAPI interface { + StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) + ChainHead(ctx context.Context) (*types.TipSet, error) + ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) + MpoolPush(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) + StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) +} diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index 1a3ddda58..c71b6b560 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -361,6 +361,17 @@ type WorkerStruct struct { } } +type GatewayStruct struct { + Internal struct { + // TODO: does the gateway need perms? + StateGetActor func(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) + ChainHead func(ctx context.Context) (*types.TipSet, error) + ChainGetTipSet func(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) + MpoolPush func(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) + StateAccountKey func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) + } +} + // CommonStruct func (c *CommonStruct) AuthVerify(ctx context.Context, token string) ([]auth.Permission, error) { @@ -1372,7 +1383,28 @@ func (w *WorkerStruct) Closing(ctx context.Context) (<-chan struct{}, error) { return w.Internal.Closing(ctx) } +func (g GatewayStruct) StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) { + return g.Internal.StateGetActor(ctx, actor, ts) +} + +func (g GatewayStruct) ChainHead(ctx context.Context) (*types.TipSet, error) { + return g.Internal.ChainHead(ctx) +} + +func (g GatewayStruct) ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) { + return g.Internal.ChainGetTipSet(ctx, tsk) +} + +func (g GatewayStruct) MpoolPush(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) { + return g.Internal.MpoolPush(ctx, sm) +} + +func (g GatewayStruct) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) { + return g.Internal.StateAccountKey(ctx, addr, tsk) +} + var _ api.Common = &CommonStruct{} var _ api.FullNode = &FullNodeStruct{} var _ api.StorageMiner = &StorageMinerStruct{} var _ api.WorkerAPI = &WorkerStruct{} +var _ api.GatewayAPI = &GatewayStruct{} diff --git a/api/client/client.go b/api/client/client.go index cd915acf0..390ce93d7 100644 --- a/api/client/client.go +++ b/api/client/client.go @@ -82,3 +82,17 @@ func NewWorkerRPC(ctx context.Context, addr string, requestHeader http.Header) ( return &res, closer, err } + +// NewGatewayRPC creates a new http jsonrpc client for a gateway node. +func NewGatewayRPC(ctx context.Context, addr string, requestHeader http.Header, opts ...jsonrpc.Option) (api.GatewayAPI, jsonrpc.ClientCloser, error) { + var res apistruct.GatewayStruct + closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin", + []interface{}{ + &res.Internal, + }, + requestHeader, + opts..., + ) + + return &res, closer, err +} diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index ba3dcd1d8..476de1927 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -40,6 +40,11 @@ import ( var log = logging.Logger("statemgr") +type StateManagerAPI interface { + LoadActorTsk(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*types.Actor, error) + ResolveToKeyAddress(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) +} + type versionSpec struct { networkVersion network.Version atOrBelow abi.ChainEpoch @@ -1393,3 +1398,5 @@ func (sm *StateManager) GetMarketState(ctx context.Context, ts *types.TipSet) (m } return actState, nil } + +var _ StateManagerAPI = (*StateManager)(nil) diff --git a/cli/cmd.go b/cli/cmd.go index edcb69adc..e6475934b 100644 --- a/cli/cmd.go +++ b/cli/cmd.go @@ -289,6 +289,15 @@ func GetWorkerAPI(ctx *cli.Context) (api.WorkerAPI, jsonrpc.ClientCloser, error) return client.NewWorkerRPC(ctx.Context, addr, headers) } +func GetGatewayAPI(ctx *cli.Context) (api.GatewayAPI, jsonrpc.ClientCloser, error) { + addr, headers, err := GetRawAPI(ctx, repo.FullNode) + if err != nil { + return nil, nil, err + } + + return client.NewGatewayRPC(ctx.Context, addr, headers) +} + func DaemonContext(cctx *cli.Context) context.Context { if mtCtx, ok := cctx.App.Metadata[metadataTraceContext]; ok { return mtCtx.(context.Context) diff --git a/cmd/lotus-gateway/api.go b/cmd/lotus-gateway/api.go index 0a6365dbd..edf6525b1 100644 --- a/cmd/lotus-gateway/api.go +++ b/cmd/lotus-gateway/api.go @@ -49,17 +49,6 @@ func (a *GatewayAPI) checkTipset(ctx context.Context, ts types.TipSetKey) error return nil } -func (a *GatewayAPI) StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) { - ctx, span := trace.StartSpan(ctx, "StateGetActor") - defer span.End() - - if err := a.checkTipset(ctx, ts); err != nil { - return nil, fmt.Errorf("bad tipset: %w", err) - } - - return a.api.StateGetActor(ctx, actor, ts) -} - func (a *GatewayAPI) ChainHead(ctx context.Context) (*types.TipSet, error) { ctx, span := trace.StartSpan(ctx, "ChainHead") defer span.End() @@ -88,3 +77,25 @@ func (a *GatewayAPI) MpoolPush(ctx context.Context, sm *types.SignedMessage) (ci return a.api.MpoolPushUntrusted(ctx, sm) } + +func (a *GatewayAPI) StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) { + ctx, span := trace.StartSpan(ctx, "StateGetActor") + defer span.End() + + if err := a.checkTipset(ctx, ts); err != nil { + return nil, fmt.Errorf("bad tipset: %w", err) + } + + return a.api.StateGetActor(ctx, actor, ts) +} + +func (a *GatewayAPI) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) { + ctx, span := trace.StartSpan(ctx, "StateAccountKey") + defer span.End() + + if err := a.checkTipset(ctx, tsk); err != nil { + return address.Undef, fmt.Errorf("bad tipset: %w", err) + } + + return a.api.StateAccountKey(ctx, addr, tsk) +} diff --git a/cmd/lotus/daemon.go b/cmd/lotus/daemon.go index a0f754a60..d57dddb79 100644 --- a/cmd/lotus/daemon.go +++ b/cmd/lotus/daemon.go @@ -15,6 +15,8 @@ import ( "runtime/pprof" "strings" + "go.uber.org/fx" + "github.com/filecoin-project/lotus/chain/types" paramfetch "github.com/filecoin-project/go-paramfetch" @@ -114,6 +116,10 @@ var DaemonCmd = &cli.Command{ Name: "halt-after-import", Usage: "halt the process after importing chain from file", }, + &cli.BoolFlag{ + Name: "lite", + Usage: "start lotus in lite mode", + }, &cli.StringFlag{ Name: "pprof", Usage: "specify name of file for writing cpu profile to", @@ -240,6 +246,27 @@ var DaemonCmd = &cli.Command{ shutdownChan := make(chan struct{}) + // If the daemon is started in "lite mode", replace the StateManager + // with a thin client to a gateway server + liteMode := node.Options() + if cctx.Bool("lite") { + gapi, closer, err := lcli.GetGatewayAPI(cctx) + if err != nil { + return err + } + + createRPCStateMgr := func(lc fx.Lifecycle) *modules.RPCStateManager { + lc.Append(fx.Hook{ + OnStop: func(ctx context.Context) error { + closer() + return nil + }, + }) + return modules.NewRPCStateManager(gapi) + } + liteMode = node.Override(new(stmgr.StateManagerAPI), createRPCStateMgr) + } + var api api.FullNode stop, err := node.New(ctx, @@ -251,6 +278,7 @@ var DaemonCmd = &cli.Command{ node.Repo(r), genesis, + liteMode, node.ApplyIf(func(s *node.Settings) bool { return cctx.IsSet("api") }, node.Override(node.SetApiEndpointKey, func(lr repo.LockedRepo) error { diff --git a/node/builder.go b/node/builder.go index 0d48ec130..50a20d4bb 100644 --- a/node/builder.go +++ b/node/builder.go @@ -261,6 +261,7 @@ func Online() Option { Override(new(*store.ChainStore), modules.ChainStore), Override(new(stmgr.UpgradeSchedule), stmgr.DefaultUpgradeSchedule()), Override(new(*stmgr.StateManager), stmgr.NewStateManagerWithUpgradeSchedule), + Override(new(stmgr.StateManagerAPI), From(new(*stmgr.StateManager))), Override(new(*wallet.Wallet), wallet.NewWallet), Override(new(*messagesigner.MessageSigner), messagesigner.NewMessageSigner), diff --git a/node/impl/full/wallet.go b/node/impl/full/wallet.go index b2ecdebbd..33f924846 100644 --- a/node/impl/full/wallet.go +++ b/node/impl/full/wallet.go @@ -19,8 +19,8 @@ import ( type WalletAPI struct { fx.In - StateManager *stmgr.StateManager - Wallet *wallet.Wallet + stmgr.StateManagerAPI + Wallet *wallet.Wallet } func (a *WalletAPI) WalletNew(ctx context.Context, typ crypto.SigType) (address.Address, error) { @@ -36,7 +36,7 @@ func (a *WalletAPI) WalletList(ctx context.Context) ([]address.Address, error) { } func (a *WalletAPI) WalletBalance(ctx context.Context, addr address.Address) (types.BigInt, error) { - act, err := a.StateManager.LoadActorTsk(ctx, addr, types.EmptyTSK) + act, err := a.StateManagerAPI.LoadActorTsk(ctx, addr, types.EmptyTSK) if xerrors.Is(err, types.ErrActorNotFound) { return big.Zero(), nil } else if err != nil { @@ -46,7 +46,7 @@ func (a *WalletAPI) WalletBalance(ctx context.Context, addr address.Address) (ty } func (a *WalletAPI) WalletSign(ctx context.Context, k address.Address, msg []byte) (*crypto.Signature, error) { - keyAddr, err := a.StateManager.ResolveToKeyAddress(ctx, k, nil) + keyAddr, err := a.StateManagerAPI.ResolveToKeyAddress(ctx, k, nil) if err != nil { return nil, xerrors.Errorf("failed to resolve ID address: %w", keyAddr) } diff --git a/node/modules/statemanager.go b/node/modules/statemanager.go new file mode 100644 index 000000000..b673f7268 --- /dev/null +++ b/node/modules/statemanager.go @@ -0,0 +1,29 @@ +package modules + +import ( + "context" + + "github.com/filecoin-project/lotus/api" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/lotus/chain/stmgr" + "github.com/filecoin-project/lotus/chain/types" +) + +type RPCStateManager struct { + gapi api.GatewayAPI +} + +func NewRPCStateManager(api api.GatewayAPI) *RPCStateManager { + return &RPCStateManager{gapi: api} +} + +func (s *RPCStateManager) LoadActorTsk(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*types.Actor, error) { + return s.gapi.StateGetActor(ctx, addr, tsk) +} + +func (s *RPCStateManager) ResolveToKeyAddress(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) { + return s.gapi.StateAccountKey(ctx, addr, ts.Key()) +} + +var _ stmgr.StateManagerAPI = (*RPCStateManager)(nil) From 1ffdd7d5b3a83ab313066714f1e305cbe967931a Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Wed, 30 Sep 2020 10:43:59 +0200 Subject: [PATCH 02/18] fix: gateway - remove tracing (JsonRPC automatically traces all calls) --- cmd/lotus-gateway/api.go | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/cmd/lotus-gateway/api.go b/cmd/lotus-gateway/api.go index edf6525b1..22b30544e 100644 --- a/cmd/lotus-gateway/api.go +++ b/cmd/lotus-gateway/api.go @@ -9,8 +9,6 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/types" "github.com/ipfs/go-cid" - - "go.opencensus.io/trace" ) const LookbackCap = time.Hour @@ -50,17 +48,12 @@ func (a *GatewayAPI) checkTipset(ctx context.Context, ts types.TipSetKey) error } func (a *GatewayAPI) ChainHead(ctx context.Context) (*types.TipSet, error) { - ctx, span := trace.StartSpan(ctx, "ChainHead") - defer span.End() // TODO: cache and invalidate cache when timestamp is up (or have internal ChainNotify) return a.api.ChainHead(ctx) } func (a *GatewayAPI) ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) { - ctx, span := trace.StartSpan(ctx, "ChainGetTipSet") - defer span.End() - if err := a.checkTipset(ctx, tsk); err != nil { return nil, fmt.Errorf("bad tipset: %w", err) } @@ -70,18 +63,12 @@ func (a *GatewayAPI) ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (* } func (a *GatewayAPI) MpoolPush(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) { - ctx, span := trace.StartSpan(ctx, "MpoolPush") - defer span.End() - // TODO: additional anti-spam checks return a.api.MpoolPushUntrusted(ctx, sm) } func (a *GatewayAPI) StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) { - ctx, span := trace.StartSpan(ctx, "StateGetActor") - defer span.End() - if err := a.checkTipset(ctx, ts); err != nil { return nil, fmt.Errorf("bad tipset: %w", err) } @@ -90,9 +77,6 @@ func (a *GatewayAPI) StateGetActor(ctx context.Context, actor address.Address, t } func (a *GatewayAPI) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) { - ctx, span := trace.StartSpan(ctx, "StateAccountKey") - defer span.End() - if err := a.checkTipset(ctx, tsk); err != nil { return address.Undef, fmt.Errorf("bad tipset: %w", err) } From eec13ff8dc3144606b1a43e21d62f885ef45d8ce Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Wed, 30 Sep 2020 16:35:06 +0200 Subject: [PATCH 03/18] refactor: daemon - simplify gateway cleanup --- cmd/lotus/daemon.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/cmd/lotus/daemon.go b/cmd/lotus/daemon.go index d57dddb79..54dcfe54c 100644 --- a/cmd/lotus/daemon.go +++ b/cmd/lotus/daemon.go @@ -15,8 +15,6 @@ import ( "runtime/pprof" "strings" - "go.uber.org/fx" - "github.com/filecoin-project/lotus/chain/types" paramfetch "github.com/filecoin-project/go-paramfetch" @@ -249,22 +247,24 @@ var DaemonCmd = &cli.Command{ // If the daemon is started in "lite mode", replace the StateManager // with a thin client to a gateway server liteMode := node.Options() - if cctx.Bool("lite") { + isLite := cctx.Bool("lite") + if isLite { gapi, closer, err := lcli.GetGatewayAPI(cctx) if err != nil { return err } - createRPCStateMgr := func(lc fx.Lifecycle) *modules.RPCStateManager { - lc.Append(fx.Hook{ - OnStop: func(ctx context.Context) error { - closer() - return nil - }, - }) - return modules.NewRPCStateManager(gapi) - } - liteMode = node.Override(new(stmgr.StateManagerAPI), createRPCStateMgr) + defer closer() + + liteMode = node.Options( + node.Override(new(api.GatewayAPI), gapi), + 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), + ) } var api api.FullNode From 00a14de0948ce078437338f2356c04159efd1087 Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Wed, 30 Sep 2020 16:36:16 +0200 Subject: [PATCH 04/18] feat: gateway - StateLookupID --- api/api_gateway.go | 3 ++- api/apistruct/struct.go | 12 ++++++++---- chain/stmgr/stmgr.go | 1 + cmd/lotus-gateway/api.go | 26 ++++++++++++++++++-------- node/impl/full/multisig.go | 9 +++++---- node/impl/full/state.go | 6 +----- node/modules/statemanager.go | 4 ++++ 7 files changed, 39 insertions(+), 22 deletions(-) diff --git a/api/api_gateway.go b/api/api_gateway.go index 6a220e9ea..398a7518f 100644 --- a/api/api_gateway.go +++ b/api/api_gateway.go @@ -9,9 +9,10 @@ import ( ) type GatewayAPI interface { - StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) ChainHead(ctx context.Context) (*types.TipSet, error) ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) MpoolPush(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) + StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) + StateLookupID(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) } diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index c71b6b560..268fc4460 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -1383,10 +1383,6 @@ func (w *WorkerStruct) Closing(ctx context.Context) (<-chan struct{}, error) { return w.Internal.Closing(ctx) } -func (g GatewayStruct) StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) { - return g.Internal.StateGetActor(ctx, actor, ts) -} - func (g GatewayStruct) ChainHead(ctx context.Context) (*types.TipSet, error) { return g.Internal.ChainHead(ctx) } @@ -1403,6 +1399,14 @@ func (g GatewayStruct) StateAccountKey(ctx context.Context, addr address.Address return g.Internal.StateAccountKey(ctx, addr, tsk) } +func (g GatewayStruct) StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) { + return g.Internal.StateGetActor(ctx, actor, ts) +} + +func (g GatewayStruct) StateLookupID(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) { + panic("implement me") +} + var _ api.Common = &CommonStruct{} var _ api.FullNode = &FullNodeStruct{} var _ api.StorageMiner = &StorageMinerStruct{} diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index 476de1927..d3129ea20 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -42,6 +42,7 @@ var log = logging.Logger("statemgr") type StateManagerAPI interface { LoadActorTsk(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*types.Actor, error) + LookupID(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) ResolveToKeyAddress(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) } diff --git a/cmd/lotus-gateway/api.go b/cmd/lotus-gateway/api.go index 22b30544e..642ed4d86 100644 --- a/cmd/lotus-gateway/api.go +++ b/cmd/lotus-gateway/api.go @@ -68,14 +68,6 @@ func (a *GatewayAPI) MpoolPush(ctx context.Context, sm *types.SignedMessage) (ci return a.api.MpoolPushUntrusted(ctx, sm) } -func (a *GatewayAPI) StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) { - if err := a.checkTipset(ctx, ts); err != nil { - return nil, fmt.Errorf("bad tipset: %w", err) - } - - return a.api.StateGetActor(ctx, actor, ts) -} - func (a *GatewayAPI) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) { if err := a.checkTipset(ctx, tsk); err != nil { return address.Undef, fmt.Errorf("bad tipset: %w", err) @@ -83,3 +75,21 @@ func (a *GatewayAPI) StateAccountKey(ctx context.Context, addr address.Address, return a.api.StateAccountKey(ctx, addr, tsk) } + +func (a *GatewayAPI) StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) { + if err := a.checkTipset(ctx, tsk); err != nil { + return nil, fmt.Errorf("bad tipset: %w", err) + } + + return a.api.StateGetActor(ctx, actor, tsk) +} + +func (a *GatewayAPI) StateLookupID(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) { + if err := a.checkTipset(ctx, tsk); err != nil { + return address.Undef, fmt.Errorf("bad tipset: %w", err) + } + + return a.api.StateLookupID(ctx, addr, tsk) +} + +var _ api.GatewayAPI = &GatewayAPI{} diff --git a/node/impl/full/multisig.go b/node/impl/full/multisig.go index 715689edc..489658412 100644 --- a/node/impl/full/multisig.go +++ b/node/impl/full/multisig.go @@ -3,6 +3,8 @@ package full import ( "context" + "github.com/filecoin-project/lotus/chain/stmgr" + "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-address" @@ -23,9 +25,8 @@ import ( type MsigAPI struct { fx.In - WalletAPI WalletAPI - StateAPI StateAPI - MpoolAPI MpoolAPI + StateManagerAPI stmgr.StateManagerAPI + MpoolAPI MpoolAPI } func (a *MsigAPI) messageBuilder(ctx context.Context, from address.Address) (multisig.MessageBuilder, error) { @@ -152,7 +153,7 @@ func (a *MsigAPI) msigApproveOrCancel(ctx context.Context, operation api.MsigPro } if proposer.Protocol() != address.ID { - proposerID, err := a.StateAPI.StateLookupID(ctx, proposer, types.EmptyTSK) + proposerID, err := a.StateManagerAPI.LookupID(ctx, proposer, nil) if err != nil { return cid.Undef, err } diff --git a/node/impl/full/state.go b/node/impl/full/state.go index 56dfc0a14..37af5a9a4 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -382,12 +382,8 @@ func (a *StateAPI) StateLookupID(ctx context.Context, addr address.Address, tsk if err != nil { return address.Undef, xerrors.Errorf("loading tipset %s: %w", tsk, err) } - state, err := a.stateForTs(ctx, ts) - if err != nil { - return address.Undef, err - } - return state.LookupID(addr) + return a.StateManager.LookupID(ctx, addr, ts) } func (a *StateAPI) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) { diff --git a/node/modules/statemanager.go b/node/modules/statemanager.go index b673f7268..0ed054d45 100644 --- a/node/modules/statemanager.go +++ b/node/modules/statemanager.go @@ -22,6 +22,10 @@ func (s *RPCStateManager) LoadActorTsk(ctx context.Context, addr address.Address return s.gapi.StateGetActor(ctx, addr, tsk) } +func (s *RPCStateManager) LookupID(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) { + return s.gapi.StateLookupID(ctx, addr, ts.Key()) +} + func (s *RPCStateManager) ResolveToKeyAddress(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) { return s.gapi.StateAccountKey(ctx, addr, ts.Key()) } From 8fa4c0a970e58bd9cebbd4ba2614b033701b75c5 Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Wed, 30 Sep 2020 17:59:18 +0200 Subject: [PATCH 05/18] feat: gateway - MpoolPush --- cmd/lotus/daemon.go | 3 +++ node/builder.go | 3 +++ node/impl/full/mpool.go | 7 ++++++- node/modules/pushmessage.go | 25 +++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 node/modules/pushmessage.go diff --git a/cmd/lotus/daemon.go b/cmd/lotus/daemon.go index 54dcfe54c..f83e74118 100644 --- a/cmd/lotus/daemon.go +++ b/cmd/lotus/daemon.go @@ -15,6 +15,8 @@ import ( "runtime/pprof" "strings" + "github.com/filecoin-project/lotus/node/impl/full" + "github.com/filecoin-project/lotus/chain/types" paramfetch "github.com/filecoin-project/go-paramfetch" @@ -259,6 +261,7 @@ var DaemonCmd = &cli.Command{ liteMode = node.Options( node.Override(new(api.GatewayAPI), gapi), node.Override(new(stmgr.StateManagerAPI), modules.NewRPCStateManager), + node.Override(new(full.PushMessageAPI), modules.NewRPCPushMessageAPI), node.Unset(node.RunHelloKey), node.Unset(node.RunChainExchangeKey), node.Unset(node.RunPeerMgrKey), diff --git a/node/builder.go b/node/builder.go index 50a20d4bb..8579131b9 100644 --- a/node/builder.go +++ b/node/builder.go @@ -6,6 +6,8 @@ import ( "os" "time" + "github.com/filecoin-project/lotus/node/impl/full" + logging "github.com/ipfs/go-log" ci "github.com/libp2p/go-libp2p-core/crypto" "github.com/libp2p/go-libp2p-core/host" @@ -262,6 +264,7 @@ func Online() Option { Override(new(stmgr.UpgradeSchedule), stmgr.DefaultUpgradeSchedule()), Override(new(*stmgr.StateManager), stmgr.NewStateManagerWithUpgradeSchedule), Override(new(stmgr.StateManagerAPI), From(new(*stmgr.StateManager))), + Override(new(full.PushMessageAPI), From(new(full.MpoolAPI))), Override(new(*wallet.Wallet), wallet.NewWallet), Override(new(*messagesigner.MessageSigner), messagesigner.NewMessageSigner), diff --git a/node/impl/full/mpool.go b/node/impl/full/mpool.go index 1f093606c..000891bb2 100644 --- a/node/impl/full/mpool.go +++ b/node/impl/full/mpool.go @@ -15,11 +15,16 @@ import ( "github.com/filecoin-project/lotus/node/modules/dtypes" ) +type PushMessageAPI interface { + MpoolPush(ctx context.Context, smsg *types.SignedMessage) (cid.Cid, error) +} + type MpoolAPI struct { fx.In WalletAPI GasAPI + PushMessageAPI MessageSigner *messagesigner.MessageSigner @@ -107,7 +112,7 @@ func (a *MpoolAPI) MpoolClear(ctx context.Context, local bool) error { } func (a *MpoolAPI) MpoolPush(ctx context.Context, smsg *types.SignedMessage) (cid.Cid, error) { - return a.Mpool.Push(smsg) + return a.PushMessageAPI.MpoolPush(ctx, smsg) } func (a *MpoolAPI) MpoolPushUntrusted(ctx context.Context, smsg *types.SignedMessage) (cid.Cid, error) { diff --git a/node/modules/pushmessage.go b/node/modules/pushmessage.go new file mode 100644 index 000000000..59af435a7 --- /dev/null +++ b/node/modules/pushmessage.go @@ -0,0 +1,25 @@ +package modules + +import ( + "context" + + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/node/impl/full" + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/lotus/chain/types" +) + +type RPCPushMessageAPI struct { + gapi api.GatewayAPI +} + +func NewRPCPushMessageAPI(api api.GatewayAPI) *RPCPushMessageAPI { + return &RPCPushMessageAPI{gapi: api} +} + +func (p *RPCPushMessageAPI) MpoolPush(ctx context.Context, smsg *types.SignedMessage) (cid.Cid, error) { + return p.gapi.MpoolPush(ctx, smsg) +} + +var _ full.PushMessageAPI = (*RPCPushMessageAPI)(nil) From f1b1d8cec6375f7d3decd47aa1c43f1582c1dfce Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Thu, 1 Oct 2020 12:38:14 +0200 Subject: [PATCH 06/18] fix: MpoolAPI DI --- node/builder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/builder.go b/node/builder.go index 8579131b9..6bdb46ca5 100644 --- a/node/builder.go +++ b/node/builder.go @@ -264,7 +264,7 @@ func Online() Option { Override(new(stmgr.UpgradeSchedule), stmgr.DefaultUpgradeSchedule()), Override(new(*stmgr.StateManager), stmgr.NewStateManagerWithUpgradeSchedule), Override(new(stmgr.StateManagerAPI), From(new(*stmgr.StateManager))), - Override(new(full.PushMessageAPI), From(new(full.MpoolAPI))), + Override(new(full.PushMessageAPI), new(full.MpoolAPI)), Override(new(*wallet.Wallet), wallet.NewWallet), Override(new(*messagesigner.MessageSigner), messagesigner.NewMessageSigner), From 2719adc1b1e28eb31c561ed0bcfd8610a30abe00 Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Thu, 1 Oct 2020 17:51:27 +0200 Subject: [PATCH 07/18] feat: lite-mode - thin client for chan & state --- api/apistruct/struct.go | 7 +-- cmd/lotus/daemon.go | 4 +- node/builder.go | 5 +- node/impl/full/chain.go | 25 +++++++-- node/impl/full/mpool.go | 28 +++++++--- node/impl/full/state.go | 52 ++++++++++++++----- node/modules/pushmessage.go | 25 --------- .../{statemanager.go => rpcstatemanager.go} | 0 8 files changed, 93 insertions(+), 53 deletions(-) delete mode 100644 node/modules/pushmessage.go rename node/modules/{statemanager.go => rpcstatemanager.go} (100%) diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index 268fc4460..7ab50965a 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -364,11 +364,12 @@ type WorkerStruct struct { type GatewayStruct struct { Internal struct { // TODO: does the gateway need perms? - StateGetActor func(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) - ChainHead func(ctx context.Context) (*types.TipSet, error) ChainGetTipSet func(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) + ChainHead func(ctx context.Context) (*types.TipSet, error) MpoolPush func(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) StateAccountKey func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) + StateGetActor func(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) + StateLookupID func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) } } @@ -1404,7 +1405,7 @@ func (g GatewayStruct) StateGetActor(ctx context.Context, actor address.Address, } func (g GatewayStruct) StateLookupID(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) { - panic("implement me") + return g.Internal.StateLookupID(ctx, addr, tsk) } var _ api.Common = &CommonStruct{} diff --git a/cmd/lotus/daemon.go b/cmd/lotus/daemon.go index f83e74118..1dc9b3c78 100644 --- a/cmd/lotus/daemon.go +++ b/cmd/lotus/daemon.go @@ -260,8 +260,10 @@ var DaemonCmd = &cli.Command{ liteMode = node.Options( node.Override(new(api.GatewayAPI), gapi), + node.Override(new(full.ChainModuleAPI), 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.Override(new(full.PushMessageAPI), modules.NewRPCPushMessageAPI), node.Unset(node.RunHelloKey), node.Unset(node.RunChainExchangeKey), node.Unset(node.RunPeerMgrKey), diff --git a/node/builder.go b/node/builder.go index 6bdb46ca5..e8ce96ec8 100644 --- a/node/builder.go +++ b/node/builder.go @@ -264,10 +264,13 @@ func Online() Option { Override(new(stmgr.UpgradeSchedule), stmgr.DefaultUpgradeSchedule()), Override(new(*stmgr.StateManager), stmgr.NewStateManagerWithUpgradeSchedule), Override(new(stmgr.StateManagerAPI), From(new(*stmgr.StateManager))), - Override(new(full.PushMessageAPI), new(full.MpoolAPI)), Override(new(*wallet.Wallet), wallet.NewWallet), Override(new(*messagesigner.MessageSigner), messagesigner.NewMessageSigner), + Override(new(full.ChainModuleAPI), From(new(full.ChainModule))), + Override(new(full.StateModuleAPI), From(new(full.StateModule))), + Override(new(full.MpoolModuleAPI), From(new(full.MpoolModule))), + Override(new(dtypes.ChainGCLocker), blockstore.NewGCLocker), Override(new(dtypes.ChainGCBlockstore), modules.ChainGCBlockstore), Override(new(dtypes.ChainBitswap), modules.ChainBitswap), diff --git a/node/impl/full/chain.go b/node/impl/full/chain.go index aa2ae4df1..810c56c0b 100644 --- a/node/impl/full/chain.go +++ b/node/impl/full/chain.go @@ -39,10 +39,27 @@ import ( var log = logging.Logger("fullnode") +type ChainModuleAPI interface { + ChainHead(context.Context) (*types.TipSet, error) + ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) +} + +// ChainModule provides a default implementation of ChainModuleAPI. +// It can be swapped out with another implementation through Dependency +// Injection (for example with a thin RPC client). +type ChainModule struct { + fx.In + + Chain *store.ChainStore +} + +var _ ChainModuleAPI = (*ChainModule)(nil) + type ChainAPI struct { fx.In WalletAPI + ChainModuleAPI Chain *store.ChainStore } @@ -51,8 +68,8 @@ func (a *ChainAPI) ChainNotify(ctx context.Context) (<-chan []*api.HeadChange, e return a.Chain.SubHeadChanges(ctx), nil } -func (a *ChainAPI) ChainHead(context.Context) (*types.TipSet, error) { - return a.Chain.GetHeaviestTipSet(), nil +func (m *ChainModule) ChainHead(ctx context.Context) (*types.TipSet, error) { + return m.Chain.GetHeaviestTipSet(), nil } func (a *ChainAPI) ChainGetRandomnessFromTickets(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) { @@ -77,8 +94,8 @@ func (a *ChainAPI) ChainGetBlock(ctx context.Context, msg cid.Cid) (*types.Block return a.Chain.GetBlock(msg) } -func (a *ChainAPI) ChainGetTipSet(ctx context.Context, key types.TipSetKey) (*types.TipSet, error) { - return a.Chain.LoadTipSet(key) +func (m *ChainModule) ChainGetTipSet(ctx context.Context, key types.TipSetKey) (*types.TipSet, error) { + return m.Chain.LoadTipSet(key) } func (a *ChainAPI) ChainGetBlockMessages(ctx context.Context, msg cid.Cid) (*api.BlockMessages, error) { diff --git a/node/impl/full/mpool.go b/node/impl/full/mpool.go index 000891bb2..916d5785b 100644 --- a/node/impl/full/mpool.go +++ b/node/impl/full/mpool.go @@ -4,27 +4,43 @@ import ( "context" "encoding/json" + "github.com/filecoin-project/lotus/chain/messagepool" + + "github.com/filecoin-project/lotus/chain/messagesigner" + + "github.com/filecoin-project/lotus/node/modules/dtypes" + "github.com/filecoin-project/go-address" "github.com/ipfs/go-cid" "go.uber.org/fx" "golang.org/x/xerrors" "github.com/filecoin-project/lotus/api" - "github.com/filecoin-project/lotus/chain/messagesigner" "github.com/filecoin-project/lotus/chain/types" - "github.com/filecoin-project/lotus/node/modules/dtypes" ) -type PushMessageAPI interface { +type MpoolModuleAPI interface { MpoolPush(ctx context.Context, smsg *types.SignedMessage) (cid.Cid, error) } +// MpoolModule provides a default implementation of MpoolModuleAPI. +// It can be swapped out with another implementation through Dependency +// Injection (for example with a thin RPC client). +type MpoolModule struct { + fx.In + + Mpool *messagepool.MessagePool +} + +var _ MpoolModuleAPI = (*MpoolModule)(nil) + type MpoolAPI struct { fx.In + MpoolModuleAPI + WalletAPI GasAPI - PushMessageAPI MessageSigner *messagesigner.MessageSigner @@ -111,8 +127,8 @@ func (a *MpoolAPI) MpoolClear(ctx context.Context, local bool) error { return nil } -func (a *MpoolAPI) MpoolPush(ctx context.Context, smsg *types.SignedMessage) (cid.Cid, error) { - return a.PushMessageAPI.MpoolPush(ctx, smsg) +func (m *MpoolModule) MpoolPush(ctx context.Context, smsg *types.SignedMessage) (cid.Cid, error) { + return m.Mpool.Push(smsg) } func (a *MpoolAPI) MpoolPushUntrusted(ctx context.Context, smsg *types.SignedMessage) (cid.Cid, error) { diff --git a/node/impl/full/state.go b/node/impl/full/state.go index 37af5a9a4..a391dffa1 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -42,6 +42,24 @@ import ( "github.com/filecoin-project/lotus/node/modules/dtypes" ) +type StateModuleAPI interface { + StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) + StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) + StateLookupID(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) +} + +// StateModule provides a default implementation of StateModuleAPI. +// It can be swapped out with another implementation through Dependency +// Injection (for example with a thin RPC client). +type StateModule struct { + fx.In + + StateManager *stmgr.StateManager + Chain *store.ChainStore +} + +var _ StateModuleAPI = (*StateModule)(nil) + type StateAPI struct { fx.In @@ -49,6 +67,8 @@ type StateAPI struct { // API attached to the state API. It probably should live somewhere better Wallet *wallet.Wallet + StateModuleAPI + ProofVerifier ffiwrapper.Verifier StateManager *stmgr.StateManager Chain *store.ChainStore @@ -349,27 +369,33 @@ func (a *StateAPI) StateReplay(ctx context.Context, tsk types.TipSetKey, mc cid. }, nil } -func (a *StateAPI) stateForTs(ctx context.Context, ts *types.TipSet) (*state.StateTree, error) { +func stateForTs(ctx context.Context, ts *types.TipSet, cstore *store.ChainStore, smgr *stmgr.StateManager) (*state.StateTree, error) { if ts == nil { - ts = a.Chain.GetHeaviestTipSet() + ts = cstore.GetHeaviestTipSet() } - st, _, err := a.StateManager.TipSetState(ctx, ts) + st, _, err := smgr.TipSetState(ctx, ts) if err != nil { return nil, err } - buf := bufbstore.NewBufferedBstore(a.Chain.Blockstore()) + buf := bufbstore.NewBufferedBstore(cstore.Blockstore()) cst := cbor.NewCborStore(buf) return state.LoadStateTree(cst, st) } +func (a *StateAPI) stateForTs(ctx context.Context, ts *types.TipSet) (*state.StateTree, error) { + return stateForTs(ctx, ts, a.Chain, a.StateManager) +} +func (m *StateModule) stateForTs(ctx context.Context, ts *types.TipSet) (*state.StateTree, error) { + return stateForTs(ctx, ts, m.Chain, m.StateManager) +} -func (a *StateAPI) StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) { - ts, err := a.Chain.GetTipSetFromKey(tsk) +func (m *StateModule) StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) { + ts, err := m.Chain.GetTipSetFromKey(tsk) if err != nil { return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err) } - state, err := a.stateForTs(ctx, ts) + state, err := m.stateForTs(ctx, ts) if err != nil { return nil, xerrors.Errorf("computing tipset state failed: %w", err) } @@ -377,22 +403,22 @@ func (a *StateAPI) StateGetActor(ctx context.Context, actor address.Address, tsk return state.GetActor(actor) } -func (a *StateAPI) StateLookupID(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) { - ts, err := a.Chain.GetTipSetFromKey(tsk) +func (m *StateModule) StateLookupID(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) { + ts, err := m.Chain.GetTipSetFromKey(tsk) if err != nil { return address.Undef, xerrors.Errorf("loading tipset %s: %w", tsk, err) } - return a.StateManager.LookupID(ctx, addr, ts) + return m.StateManager.LookupID(ctx, addr, ts) } -func (a *StateAPI) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) { - ts, err := a.Chain.GetTipSetFromKey(tsk) +func (m *StateModule) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) { + ts, err := m.Chain.GetTipSetFromKey(tsk) if err != nil { return address.Undef, xerrors.Errorf("loading tipset %s: %w", tsk, err) } - return a.StateManager.ResolveToKeyAddress(ctx, addr, ts) + return m.StateManager.ResolveToKeyAddress(ctx, addr, ts) } func (a *StateAPI) StateReadState(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*api.ActorState, error) { diff --git a/node/modules/pushmessage.go b/node/modules/pushmessage.go deleted file mode 100644 index 59af435a7..000000000 --- a/node/modules/pushmessage.go +++ /dev/null @@ -1,25 +0,0 @@ -package modules - -import ( - "context" - - "github.com/filecoin-project/lotus/api" - "github.com/filecoin-project/lotus/node/impl/full" - "github.com/ipfs/go-cid" - - "github.com/filecoin-project/lotus/chain/types" -) - -type RPCPushMessageAPI struct { - gapi api.GatewayAPI -} - -func NewRPCPushMessageAPI(api api.GatewayAPI) *RPCPushMessageAPI { - return &RPCPushMessageAPI{gapi: api} -} - -func (p *RPCPushMessageAPI) MpoolPush(ctx context.Context, smsg *types.SignedMessage) (cid.Cid, error) { - return p.gapi.MpoolPush(ctx, smsg) -} - -var _ full.PushMessageAPI = (*RPCPushMessageAPI)(nil) diff --git a/node/modules/statemanager.go b/node/modules/rpcstatemanager.go similarity index 100% rename from node/modules/statemanager.go rename to node/modules/rpcstatemanager.go From be09a8a00acc3e41bbe1f08558c24a99785092c7 Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Thu, 1 Oct 2020 17:54:28 +0200 Subject: [PATCH 08/18] feat: lite-mode - check that gateway API implements required chain, mpool, state methods --- cmd/lotus-gateway/api.go | 4 ++++ cmd/lotus/daemon.go | 6 ++---- node/builder.go | 4 ++-- node/impl/full/chain.go | 2 +- node/impl/full/mpool.go | 9 +++------ node/impl/full/multisig.go | 8 +++----- node/impl/full/wallet.go | 4 ++-- 7 files changed, 17 insertions(+), 20 deletions(-) diff --git a/cmd/lotus-gateway/api.go b/cmd/lotus-gateway/api.go index 642ed4d86..5f8e9adf9 100644 --- a/cmd/lotus-gateway/api.go +++ b/cmd/lotus-gateway/api.go @@ -8,6 +8,7 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/node/impl/full" "github.com/ipfs/go-cid" ) @@ -93,3 +94,6 @@ func (a *GatewayAPI) StateLookupID(ctx context.Context, addr address.Address, ts } var _ api.GatewayAPI = &GatewayAPI{} +var _ full.ChainModuleAPI = (*GatewayAPI)(nil) +var _ full.MpoolModuleAPI = (*GatewayAPI)(nil) +var _ full.StateModuleAPI = (*GatewayAPI)(nil) diff --git a/cmd/lotus/daemon.go b/cmd/lotus/daemon.go index 1dc9b3c78..c05317458 100644 --- a/cmd/lotus/daemon.go +++ b/cmd/lotus/daemon.go @@ -15,10 +15,6 @@ import ( "runtime/pprof" "strings" - "github.com/filecoin-project/lotus/node/impl/full" - - "github.com/filecoin-project/lotus/chain/types" - paramfetch "github.com/filecoin-project/go-paramfetch" "github.com/mitchellh/go-homedir" "github.com/multiformats/go-multiaddr" @@ -34,6 +30,7 @@ import ( "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/store" + "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/vm" lcli "github.com/filecoin-project/lotus/cli" "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper" @@ -42,6 +39,7 @@ 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" diff --git a/node/builder.go b/node/builder.go index e8ce96ec8..d513fe626 100644 --- a/node/builder.go +++ b/node/builder.go @@ -6,8 +6,6 @@ import ( "os" "time" - "github.com/filecoin-project/lotus/node/impl/full" - logging "github.com/ipfs/go-log" ci "github.com/libp2p/go-libp2p-core/crypto" "github.com/libp2p/go-libp2p-core/host" @@ -61,6 +59,7 @@ import ( "github.com/filecoin-project/lotus/node/hello" "github.com/filecoin-project/lotus/node/impl" "github.com/filecoin-project/lotus/node/impl/common" + "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/helpers" @@ -270,6 +269,7 @@ func Online() Option { Override(new(full.ChainModuleAPI), From(new(full.ChainModule))), Override(new(full.StateModuleAPI), From(new(full.StateModule))), Override(new(full.MpoolModuleAPI), From(new(full.MpoolModule))), + Override(new(stmgr.StateManagerAPI), From(new(*stmgr.StateManager))), Override(new(dtypes.ChainGCLocker), blockstore.NewGCLocker), Override(new(dtypes.ChainGCBlockstore), modules.ChainGCBlockstore), diff --git a/node/impl/full/chain.go b/node/impl/full/chain.go index 810c56c0b..88b84cad2 100644 --- a/node/impl/full/chain.go +++ b/node/impl/full/chain.go @@ -68,7 +68,7 @@ func (a *ChainAPI) ChainNotify(ctx context.Context) (<-chan []*api.HeadChange, e return a.Chain.SubHeadChanges(ctx), nil } -func (m *ChainModule) ChainHead(ctx context.Context) (*types.TipSet, error) { +func (m *ChainModule) ChainHead(context.Context) (*types.TipSet, error) { return m.Chain.GetHeaviestTipSet(), nil } diff --git a/node/impl/full/mpool.go b/node/impl/full/mpool.go index 916d5785b..fc32aecd2 100644 --- a/node/impl/full/mpool.go +++ b/node/impl/full/mpool.go @@ -4,19 +4,16 @@ import ( "context" "encoding/json" - "github.com/filecoin-project/lotus/chain/messagepool" - - "github.com/filecoin-project/lotus/chain/messagesigner" - - "github.com/filecoin-project/lotus/node/modules/dtypes" - "github.com/filecoin-project/go-address" "github.com/ipfs/go-cid" "go.uber.org/fx" "golang.org/x/xerrors" "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/chain/messagepool" + "github.com/filecoin-project/lotus/chain/messagesigner" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/node/modules/dtypes" ) type MpoolModuleAPI interface { diff --git a/node/impl/full/multisig.go b/node/impl/full/multisig.go index 489658412..3b05e4493 100644 --- a/node/impl/full/multisig.go +++ b/node/impl/full/multisig.go @@ -3,8 +3,6 @@ package full import ( "context" - "github.com/filecoin-project/lotus/chain/stmgr" - "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-address" @@ -25,8 +23,8 @@ import ( type MsigAPI struct { fx.In - StateManagerAPI stmgr.StateManagerAPI - MpoolAPI MpoolAPI + StateAPI StateAPI + MpoolAPI MpoolAPI } func (a *MsigAPI) messageBuilder(ctx context.Context, from address.Address) (multisig.MessageBuilder, error) { @@ -153,7 +151,7 @@ func (a *MsigAPI) msigApproveOrCancel(ctx context.Context, operation api.MsigPro } if proposer.Protocol() != address.ID { - proposerID, err := a.StateManagerAPI.LookupID(ctx, proposer, nil) + proposerID, err := a.StateAPI.StateLookupID(ctx, proposer, types.EmptyTSK) if err != nil { return cid.Undef, err } diff --git a/node/impl/full/wallet.go b/node/impl/full/wallet.go index 33f924846..616c7b46e 100644 --- a/node/impl/full/wallet.go +++ b/node/impl/full/wallet.go @@ -19,8 +19,8 @@ import ( type WalletAPI struct { fx.In - stmgr.StateManagerAPI - Wallet *wallet.Wallet + StateManagerAPI stmgr.StateManagerAPI + Wallet *wallet.Wallet } func (a *WalletAPI) WalletNew(ctx context.Context, typ crypto.SigType) (address.Address, error) { From 767611247c0239f79473d59f02daaedea8a35b0c Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Fri, 2 Oct 2020 16:14:30 +0200 Subject: [PATCH 09/18] feat: add RPC for StateWaitMsg --- api/api_full.go | 4 ++ api/api_gateway.go | 3 ++ api/apistruct/struct.go | 27 +++++++--- chain/stmgr/stmgr.go | 28 ++++++++--- cmd/lotus-gateway/api.go | 86 +++++++++++++++++++++++--------- cmd/lotus/daemon.go | 2 +- markets/storageadapter/client.go | 2 +- node/impl/full/chain.go | 7 +-- node/impl/full/state.go | 15 ++++-- 9 files changed, 127 insertions(+), 47 deletions(-) diff --git a/api/api_full.go b/api/api_full.go index 601b14660..777e996cb 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -367,6 +367,10 @@ type FullNode interface { // StateWaitMsg looks back in the chain for a message. If not found, it blocks until the // message arrives on chain, and gets to the indicated confidence depth. StateWaitMsg(ctx context.Context, cid cid.Cid, confidence uint64) (*MsgLookup, error) + // StateWaitMsgLimited looks back up to limit epochs in the chain for a message. + // If not found, it blocks until the message arrives on chain, and gets to the + // indicated confidence depth. + StateWaitMsgLimited(ctx context.Context, cid cid.Cid, confidence uint64, limit abi.ChainEpoch) (*MsgLookup, error) // StateListMiners returns the addresses of every miner that has claimed power in the Power Actor StateListMiners(context.Context, types.TipSetKey) ([]address.Address, error) // StateListActors returns the addresses of every actor in the state diff --git a/api/api_gateway.go b/api/api_gateway.go index 398a7518f..3d2293ee1 100644 --- a/api/api_gateway.go +++ b/api/api_gateway.go @@ -4,6 +4,7 @@ import ( "context" "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/lotus/chain/types" "github.com/ipfs/go-cid" ) @@ -11,8 +12,10 @@ import ( type GatewayAPI 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) MpoolPush(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) StateLookupID(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) + StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64) (*MsgLookup, error) } diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index 7ab50965a..b73379cfa 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -190,6 +190,7 @@ type FullNodeStruct struct { StateReadState func(context.Context, address.Address, types.TipSetKey) (*api.ActorState, error) `perm:"read"` StateMsgGasCost func(context.Context, cid.Cid, types.TipSetKey) (*api.MsgGasCost, error) `perm:"read"` StateWaitMsg func(ctx context.Context, cid cid.Cid, confidence uint64) (*api.MsgLookup, error) `perm:"read"` + StateWaitMsgLimited func(context.Context, cid.Cid, uint64, abi.ChainEpoch) (*api.MsgLookup, error) `perm:"read"` StateSearchMsg func(context.Context, cid.Cid) (*api.MsgLookup, error) `perm:"read"` StateListMiners func(context.Context, types.TipSetKey) ([]address.Address, error) `perm:"read"` StateListActors func(context.Context, types.TipSetKey) ([]address.Address, error) `perm:"read"` @@ -364,12 +365,14 @@ type WorkerStruct struct { type GatewayStruct struct { Internal struct { // TODO: does the gateway need perms? - ChainGetTipSet func(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) - ChainHead func(ctx context.Context) (*types.TipSet, error) - MpoolPush func(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) - StateAccountKey func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) - StateGetActor func(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) - StateLookupID func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) + ChainGetTipSet func(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) + ChainGetTipSetByHeight func(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error) + ChainHead func(ctx context.Context) (*types.TipSet, error) + MpoolPush func(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) + StateAccountKey func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) + StateGetActor func(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) + StateLookupID func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) + StateWaitMsg func(ctx context.Context, msg cid.Cid, confidence uint64) (*api.MsgLookup, error) } } @@ -866,6 +869,10 @@ func (c *FullNodeStruct) StateWaitMsg(ctx context.Context, msgc cid.Cid, confide return c.Internal.StateWaitMsg(ctx, msgc, confidence) } +func (c *FullNodeStruct) StateWaitMsgLimited(ctx context.Context, msgc cid.Cid, confidence uint64, limit abi.ChainEpoch) (*api.MsgLookup, error) { + return c.Internal.StateWaitMsgLimited(ctx, msgc, confidence, limit) +} + func (c *FullNodeStruct) StateSearchMsg(ctx context.Context, msgc cid.Cid) (*api.MsgLookup, error) { return c.Internal.StateSearchMsg(ctx, msgc) } @@ -1392,6 +1399,10 @@ func (g GatewayStruct) ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) return g.Internal.ChainGetTipSet(ctx, tsk) } +func (g GatewayStruct) ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error) { + return g.Internal.ChainGetTipSetByHeight(ctx, h, tsk) +} + func (g GatewayStruct) MpoolPush(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) { return g.Internal.MpoolPush(ctx, sm) } @@ -1408,6 +1419,10 @@ func (g GatewayStruct) StateLookupID(ctx context.Context, addr address.Address, return g.Internal.StateLookupID(ctx, addr, tsk) } +func (g GatewayStruct) StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64) (*api.MsgLookup, error) { + return g.Internal.StateWaitMsg(ctx, msg, confidence) +} + var _ api.Common = &CommonStruct{} var _ api.FullNode = &FullNodeStruct{} var _ api.StorageMiner = &StorageMinerStruct{} diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index d3129ea20..d6b6f4360 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -38,6 +38,8 @@ import ( "github.com/filecoin-project/lotus/chain/vm" ) +const LookbackNoLimit = abi.ChainEpoch(-1) + var log = logging.Logger("statemgr") type StateManagerAPI interface { @@ -514,7 +516,7 @@ func (sm *StateManager) GetReceipt(ctx context.Context, msg cid.Cid, ts *types.T return nil, fmt.Errorf("failed to load message: %w", err) } - _, r, _, err := sm.searchBackForMsg(ctx, ts, m) + _, r, _, err := sm.searchBackForMsg(ctx, ts, m, LookbackNoLimit) if err != nil { return nil, fmt.Errorf("failed to look back through chain for message: %w", err) } @@ -523,9 +525,9 @@ func (sm *StateManager) GetReceipt(ctx context.Context, msg cid.Cid, ts *types.T } // WaitForMessage blocks until a message appears on chain. It looks backwards in the chain to see if this has already -// happened. It guarantees that the message has been on chain for at least confidence epochs without being reverted -// before returning. -func (sm *StateManager) WaitForMessage(ctx context.Context, mcid cid.Cid, confidence uint64) (*types.TipSet, *types.MessageReceipt, cid.Cid, error) { +// happened, with an optional limit to how many epochs it will search. It guarantees that the message has been on +// chain for at least confidence epochs without being reverted before returning. +func (sm *StateManager) WaitForMessage(ctx context.Context, mcid cid.Cid, confidence uint64, lookbackLimit abi.ChainEpoch) (*types.TipSet, *types.MessageReceipt, cid.Cid, error) { ctx, cancel := context.WithCancel(ctx) defer cancel() @@ -563,7 +565,7 @@ func (sm *StateManager) WaitForMessage(ctx context.Context, mcid cid.Cid, confid var backFm cid.Cid backSearchWait := make(chan struct{}) go func() { - fts, r, foundMsg, err := sm.searchBackForMsg(ctx, head[0].Val, msg) + fts, r, foundMsg, err := sm.searchBackForMsg(ctx, head[0].Val, msg, lookbackLimit) if err != nil { log.Warnf("failed to look back through chain for message: %w", err) return @@ -655,7 +657,7 @@ func (sm *StateManager) SearchForMessage(ctx context.Context, mcid cid.Cid) (*ty return head, r, foundMsg, nil } - fts, r, foundMsg, err := sm.searchBackForMsg(ctx, head, msg) + fts, r, foundMsg, err := sm.searchBackForMsg(ctx, head, msg, LookbackNoLimit) if err != nil { log.Warnf("failed to look back through chain for message %s", mcid) @@ -669,7 +671,15 @@ func (sm *StateManager) SearchForMessage(ctx context.Context, mcid cid.Cid) (*ty return fts, r, foundMsg, nil } -func (sm *StateManager) searchBackForMsg(ctx context.Context, from *types.TipSet, m types.ChainMsg) (*types.TipSet, *types.MessageReceipt, cid.Cid, error) { +// searchBackForMsg searches up to limit tipsets backwards from the given +// tipset for a message receipt. +// If limit is +// - 0 then no tipsets are searched +// - 5 then five tipset are searched +// - LookbackNoLimit then there is no limit +func (sm *StateManager) searchBackForMsg(ctx context.Context, from *types.TipSet, m types.ChainMsg, limit abi.ChainEpoch) (*types.TipSet, *types.MessageReceipt, cid.Cid, error) { + limitHeight := from.Height() - limit + noLimit := limit == LookbackNoLimit cur := from curActor, err := sm.LoadActor(ctx, m.VMMessage().From, cur) @@ -685,7 +695,9 @@ func (sm *StateManager) searchBackForMsg(ctx context.Context, from *types.TipSet mNonce := m.VMMessage().Nonce for { - if cur.Height() == 0 { + // If we've reached the genesis block, or we've reached the limit of + // how far back to look + if cur.Height() == 0 || !noLimit && cur.Height() <= limitHeight { // it ain't here! return nil, nil, cid.Undef, nil } diff --git a/cmd/lotus-gateway/api.go b/cmd/lotus-gateway/api.go index 5f8e9adf9..98c1a0fea 100644 --- a/cmd/lotus-gateway/api.go +++ b/cmd/lotus-gateway/api.go @@ -6,13 +6,18 @@ import ( "time" "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/build" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/node/impl/full" "github.com/ipfs/go-cid" ) -const LookbackCap = time.Hour +const ( + LookbackCap = time.Hour + stateWaitLookbackLimit = abi.ChainEpoch(20) +) var ( ErrLookbackTooLong = fmt.Errorf("lookbacks of more than %s are disallowed", LookbackCap) @@ -22,26 +27,41 @@ type GatewayAPI struct { api api.FullNode } -func (a *GatewayAPI) getTipsetTimestamp(ctx context.Context, tsk types.TipSetKey) (time.Time, error) { +func (a *GatewayAPI) checkTipsetKey(ctx context.Context, tsk types.TipSetKey) error { if tsk.IsEmpty() { - return time.Now(), nil + return nil } ts, err := a.api.ChainGetTipSet(ctx, tsk) - if err != nil { - return time.Time{}, err - } - - return time.Unix(int64(ts.Blocks()[0].Timestamp), 0), nil -} - -func (a *GatewayAPI) checkTipset(ctx context.Context, ts types.TipSetKey) error { - when, err := a.getTipsetTimestamp(ctx, ts) if err != nil { return err } - if time.Since(when) > time.Hour { + return a.checkTipset(ts) +} + +func (a *GatewayAPI) checkTipset(ts *types.TipSet) error { + at := time.Unix(int64(ts.Blocks()[0].Timestamp), 0) + if err := a.checkTimestamp(at); err != nil { + return fmt.Errorf("bad tipset: %w", err) + } + return nil +} + +// TODO: write tests for this check +func (a *GatewayAPI) checkTipsetHeight(ts *types.TipSet, h abi.ChainEpoch) error { + tsBlock := ts.Blocks()[0] + heightDelta := time.Duration(uint64(tsBlock.Height-h)*build.BlockDelaySecs) * time.Second + timeAtHeight := time.Unix(int64(tsBlock.Timestamp), 0).Add(-heightDelta) + + if err := a.checkTimestamp(timeAtHeight); err != nil { + return fmt.Errorf("bad tipset height: %w", err) + } + return nil +} + +func (a *GatewayAPI) checkTimestamp(at time.Time) error { + if time.Since(at) > LookbackCap { return ErrLookbackTooLong } @@ -55,12 +75,26 @@ func (a *GatewayAPI) ChainHead(ctx context.Context) (*types.TipSet, error) { } func (a *GatewayAPI) ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) { - if err := a.checkTipset(ctx, tsk); err != nil { - return nil, fmt.Errorf("bad tipset: %w", err) + 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.api.ChainGetTipSet(ctx, tsk) + if err != nil { + return nil, err } - // TODO: since we're limiting lookbacks, should just cache this (could really even cache the json response bytes) - return a.api.ChainGetTipSet(ctx, tsk) + // Check if the tipset key refers to a tipset that's too far in the past + if err := a.checkTipset(ts); err != nil { + return nil, err + } + + // Check if the height is too far in the past + if err := a.checkTipsetHeight(ts, h); err != nil { + return nil, err + } + + return a.api.ChainGetTipSetByHeight(ctx, h, tsk) } func (a *GatewayAPI) MpoolPush(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) { @@ -70,30 +104,34 @@ func (a *GatewayAPI) MpoolPush(ctx context.Context, sm *types.SignedMessage) (ci } func (a *GatewayAPI) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) { - if err := a.checkTipset(ctx, tsk); err != nil { - return address.Undef, fmt.Errorf("bad tipset: %w", err) + if err := a.checkTipsetKey(ctx, tsk); err != nil { + return address.Undef, err } return a.api.StateAccountKey(ctx, addr, tsk) } func (a *GatewayAPI) StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) { - if err := a.checkTipset(ctx, tsk); err != nil { - return nil, fmt.Errorf("bad tipset: %w", err) + if err := a.checkTipsetKey(ctx, tsk); err != nil { + return nil, err } return a.api.StateGetActor(ctx, actor, tsk) } func (a *GatewayAPI) StateLookupID(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) { - if err := a.checkTipset(ctx, tsk); err != nil { - return address.Undef, fmt.Errorf("bad tipset: %w", err) + if err := a.checkTipsetKey(ctx, tsk); err != nil { + return address.Undef, err } return a.api.StateLookupID(ctx, addr, tsk) } -var _ api.GatewayAPI = &GatewayAPI{} +func (a *GatewayAPI) StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64) (*api.MsgLookup, error) { + return a.api.StateWaitMsgLimited(ctx, msg, confidence, stateWaitLookbackLimit) +} + +var _ api.GatewayAPI = (*GatewayAPI)(nil) var _ full.ChainModuleAPI = (*GatewayAPI)(nil) var _ full.MpoolModuleAPI = (*GatewayAPI)(nil) var _ full.StateModuleAPI = (*GatewayAPI)(nil) diff --git a/cmd/lotus/daemon.go b/cmd/lotus/daemon.go index c05317458..04463a839 100644 --- a/cmd/lotus/daemon.go +++ b/cmd/lotus/daemon.go @@ -244,7 +244,7 @@ var DaemonCmd = &cli.Command{ shutdownChan := make(chan struct{}) - // If the daemon is started in "lite mode", replace the StateManager + // If the daemon is started in "lite mode", replace key APIs // with a thin client to a gateway server liteMode := node.Options() isLite := cctx.Bool("lite") diff --git a/markets/storageadapter/client.go b/markets/storageadapter/client.go index 411c86ec9..647a1675d 100644 --- a/markets/storageadapter/client.go +++ b/markets/storageadapter/client.go @@ -187,7 +187,7 @@ func (c *ClientNodeAdapter) ValidatePublishedDeal(ctx context.Context, deal stor } // TODO: timeout - _, ret, _, err := c.sm.WaitForMessage(ctx, *deal.PublishMessage, build.MessageConfidence) + _, ret, _, err := c.sm.WaitForMessage(ctx, *deal.PublishMessage, build.MessageConfidence, stmgr.LookbackNoLimit) if err != nil { return 0, xerrors.Errorf("waiting for deal publish message: %w", err) } diff --git a/node/impl/full/chain.go b/node/impl/full/chain.go index 88b84cad2..5b4f41114 100644 --- a/node/impl/full/chain.go +++ b/node/impl/full/chain.go @@ -42,6 +42,7 @@ var log = logging.Logger("fullnode") type ChainModuleAPI interface { ChainHead(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) } // ChainModule provides a default implementation of ChainModuleAPI. @@ -197,12 +198,12 @@ func (a *ChainAPI) ChainGetParentReceipts(ctx context.Context, bcid cid.Cid) ([] return out, nil } -func (a *ChainAPI) ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error) { - ts, err := a.Chain.GetTipSetFromKey(tsk) +func (m *ChainModule) ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error) { + ts, err := m.Chain.GetTipSetFromKey(tsk) if err != nil { return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err) } - return a.Chain.GetTipsetByHeight(ctx, h, ts, true) + return m.Chain.GetTipsetByHeight(ctx, h, ts, true) } func (a *ChainAPI) ChainReadObj(ctx context.Context, obj cid.Cid) ([]byte, error) { diff --git a/node/impl/full/state.go b/node/impl/full/state.go index a391dffa1..540d959ed 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -46,6 +46,7 @@ type StateModuleAPI interface { StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) StateLookupID(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) + StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64) (*api.MsgLookup, error) } // StateModule provides a default implementation of StateModuleAPI. @@ -475,22 +476,28 @@ func (a *StateAPI) MinerCreateBlock(ctx context.Context, bt *api.BlockTemplate) return &out, nil } -func (a *StateAPI) StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64) (*api.MsgLookup, error) { - ts, recpt, found, err := a.StateManager.WaitForMessage(ctx, msg, confidence) +func (m *StateModule) StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64) (*api.MsgLookup, error) { + return stateWaitMsgLimited(ctx, m.StateManager, m.Chain, msg, confidence, stmgr.LookbackNoLimit) +} +func (a *StateAPI) StateWaitMsgLimited(ctx context.Context, msg cid.Cid, confidence uint64, lookbackLimit abi.ChainEpoch) (*api.MsgLookup, error) { + return stateWaitMsgLimited(ctx, a.StateManager, a.Chain, msg, confidence, lookbackLimit) +} +func stateWaitMsgLimited(ctx context.Context, smgr *stmgr.StateManager, cstore *store.ChainStore, msg cid.Cid, confidence uint64, lookbackLimit abi.ChainEpoch) (*api.MsgLookup, error) { + ts, recpt, found, err := smgr.WaitForMessage(ctx, msg, confidence, lookbackLimit) if err != nil { return nil, err } var returndec interface{} if recpt.ExitCode == 0 && len(recpt.Return) > 0 { - cmsg, err := a.Chain.GetCMessage(msg) + cmsg, err := cstore.GetCMessage(msg) if err != nil { return nil, xerrors.Errorf("failed to load message after successful receipt search: %w", err) } vmsg := cmsg.VMMessage() - t, err := stmgr.GetReturnType(ctx, a.StateManager, vmsg.To, vmsg.Method, ts) + t, err := stmgr.GetReturnType(ctx, smgr, vmsg.To, vmsg.Method, ts) if err != nil { return nil, xerrors.Errorf("failed to get return type: %w", err) } From b32d25c562875a972714fd29c64bea3aaee636a1 Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Mon, 5 Oct 2020 17:09:21 +0200 Subject: [PATCH 10/18] feat: add RPC for GasEstimateMessageGas --- api/api_gateway.go | 1 + api/apistruct/struct.go | 5 ++ cmd/lotus-gateway/api.go | 9 ++++ cmd/lotus/daemon.go | 1 + node/builder.go | 3 +- node/impl/full/gas.go | 103 +++++++++++++++++++++++++++++++-------- 6 files changed, 100 insertions(+), 22 deletions(-) diff --git a/api/api_gateway.go b/api/api_gateway.go index 3d2293ee1..8c06304e9 100644 --- a/api/api_gateway.go +++ b/api/api_gateway.go @@ -13,6 +13,7 @@ type GatewayAPI 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) + GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) MpoolPush(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index b73379cfa..38d177aec 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -368,6 +368,7 @@ type GatewayStruct struct { ChainGetTipSet func(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) ChainGetTipSetByHeight func(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error) ChainHead func(ctx context.Context) (*types.TipSet, error) + GasEstimateMessageGas func(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) MpoolPush func(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) StateAccountKey func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) StateGetActor func(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) @@ -1403,6 +1404,10 @@ func (g GatewayStruct) ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEp return g.Internal.ChainGetTipSetByHeight(ctx, h, tsk) } +func (g GatewayStruct) GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) { + return g.Internal.GasEstimateMessageGas(ctx, msg, spec, tsk) +} + func (g GatewayStruct) MpoolPush(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) { return g.Internal.MpoolPush(ctx, sm) } diff --git a/cmd/lotus-gateway/api.go b/cmd/lotus-gateway/api.go index 98c1a0fea..8192c58ec 100644 --- a/cmd/lotus-gateway/api.go +++ b/cmd/lotus-gateway/api.go @@ -111,6 +111,14 @@ func (a *GatewayAPI) StateAccountKey(ctx context.Context, addr address.Address, return a.api.StateAccountKey(ctx, addr, tsk) } +func (a *GatewayAPI) GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) { + if err := a.checkTipsetKey(ctx, tsk); err != nil { + return nil, err + } + + return a.api.GasEstimateMessageGas(ctx, msg, spec, tsk) +} + func (a *GatewayAPI) StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) { if err := a.checkTipsetKey(ctx, tsk); err != nil { return nil, err @@ -133,5 +141,6 @@ func (a *GatewayAPI) StateWaitMsg(ctx context.Context, msg cid.Cid, confidence u var _ api.GatewayAPI = (*GatewayAPI)(nil) var _ full.ChainModuleAPI = (*GatewayAPI)(nil) +var _ full.GasModuleAPI = (*GatewayAPI)(nil) var _ full.MpoolModuleAPI = (*GatewayAPI)(nil) var _ full.StateModuleAPI = (*GatewayAPI)(nil) diff --git a/cmd/lotus/daemon.go b/cmd/lotus/daemon.go index 04463a839..495979ea9 100644 --- a/cmd/lotus/daemon.go +++ b/cmd/lotus/daemon.go @@ -259,6 +259,7 @@ var DaemonCmd = &cli.Command{ 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), diff --git a/node/builder.go b/node/builder.go index d513fe626..101ec3dbc 100644 --- a/node/builder.go +++ b/node/builder.go @@ -267,8 +267,9 @@ func Online() Option { Override(new(*messagesigner.MessageSigner), messagesigner.NewMessageSigner), Override(new(full.ChainModuleAPI), From(new(full.ChainModule))), - Override(new(full.StateModuleAPI), From(new(full.StateModule))), + Override(new(full.GasModuleAPI), From(new(full.GasModule))), Override(new(full.MpoolModuleAPI), From(new(full.MpoolModule))), + Override(new(full.StateModuleAPI), From(new(full.StateModule))), Override(new(stmgr.StateManagerAPI), From(new(*stmgr.StateManager))), Override(new(dtypes.ChainGCLocker), blockstore.NewGCLocker), diff --git a/node/impl/full/gas.go b/node/impl/full/gas.go index 3580ca26d..0cb1eb084 100644 --- a/node/impl/full/gas.go +++ b/node/impl/full/gas.go @@ -26,8 +26,27 @@ import ( "github.com/filecoin-project/lotus/chain/types" ) +type GasModuleAPI interface { + GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) +} + +// GasModule provides a default implementation of GasModuleAPI. +// It can be swapped out with another implementation through Dependency +// Injection (for example with a thin RPC client). +type GasModule struct { + fx.In + Stmgr *stmgr.StateManager + Chain *store.ChainStore + Mpool *messagepool.MessagePool +} + +var _ GasModuleAPI = (*GasModule)(nil) + type GasAPI struct { fx.In + + GasModuleAPI + Stmgr *stmgr.StateManager Chain *store.ChainStore Mpool *messagepool.MessagePool @@ -36,9 +55,24 @@ type GasAPI struct { const MinGasPremium = 100e3 const MaxSpendOnFeeDenom = 100 -func (a *GasAPI) GasEstimateFeeCap(ctx context.Context, msg *types.Message, maxqueueblks int64, - tsk types.TipSetKey) (types.BigInt, error) { - ts := a.Chain.GetHeaviestTipSet() +func (a *GasAPI) GasEstimateFeeCap( + ctx context.Context, + msg *types.Message, + maxqueueblks int64, + tsk types.TipSetKey, +) (types.BigInt, error) { + return gasEstimateFeeCap(a.Chain, msg, maxqueueblks) +} +func (m *GasModule) GasEstimateFeeCap( + ctx context.Context, + msg *types.Message, + maxqueueblks int64, + tsk types.TipSetKey, +) (types.BigInt, error) { + return gasEstimateFeeCap(m.Chain, msg, maxqueueblks) +} +func gasEstimateFeeCap(cstore *store.ChainStore, msg *types.Message, maxqueueblks int64) (types.BigInt, error) { + ts := cstore.GetHeaviestTipSet() parentBaseFee := ts.Blocks()[0].ParentBaseFee increaseFactor := math.Pow(1.+1./float64(build.BaseFeeMaxChangeDenom), float64(maxqueueblks)) @@ -82,9 +116,25 @@ func medianGasPremium(prices []gasMeta, blocks int) abi.TokenAmount { return premium } -func (a *GasAPI) GasEstimateGasPremium(ctx context.Context, nblocksincl uint64, - sender address.Address, gaslimit int64, _ types.TipSetKey) (types.BigInt, error) { - +func (a *GasAPI) GasEstimateGasPremium( + ctx context.Context, + nblocksincl uint64, + sender address.Address, + gaslimit int64, + _ types.TipSetKey, +) (types.BigInt, error) { + return gasEstimateGasPremium(a.Chain, nblocksincl) +} +func (m *GasModule) GasEstimateGasPremium( + ctx context.Context, + nblocksincl uint64, + sender address.Address, + gaslimit int64, + _ types.TipSetKey, +) (types.BigInt, error) { + return gasEstimateGasPremium(m.Chain, nblocksincl) +} +func gasEstimateGasPremium(cstore *store.ChainStore, nblocksincl uint64) (types.BigInt, error) { if nblocksincl == 0 { nblocksincl = 1 } @@ -92,20 +142,20 @@ func (a *GasAPI) GasEstimateGasPremium(ctx context.Context, nblocksincl uint64, var prices []gasMeta var blocks int - ts := a.Chain.GetHeaviestTipSet() + ts := cstore.GetHeaviestTipSet() for i := uint64(0); i < nblocksincl*2; i++ { if ts.Height() == 0 { break // genesis } - pts, err := a.Chain.LoadTipSet(ts.Parents()) + pts, err := cstore.LoadTipSet(ts.Parents()) if err != nil { return types.BigInt{}, err } blocks += len(pts.Blocks()) - msgs, err := a.Chain.MessagesForTipset(pts) + msgs, err := cstore.MessagesForTipset(pts) if err != nil { return types.BigInt{}, xerrors.Errorf("loading messages: %w", err) } @@ -142,19 +192,30 @@ func (a *GasAPI) GasEstimateGasPremium(ctx context.Context, nblocksincl uint64, } func (a *GasAPI) GasEstimateGasLimit(ctx context.Context, msgIn *types.Message, _ types.TipSetKey) (int64, error) { - + return gasEstimateGasLimit(ctx, a.Chain, a.Stmgr, a.Mpool, msgIn) +} +func (m *GasModule) GasEstimateGasLimit(ctx context.Context, msgIn *types.Message, _ types.TipSetKey) (int64, error) { + return gasEstimateGasLimit(ctx, m.Chain, m.Stmgr, m.Mpool, msgIn) +} +func gasEstimateGasLimit( + ctx context.Context, + cstore *store.ChainStore, + smgr *stmgr.StateManager, + mpool *messagepool.MessagePool, + msgIn *types.Message, +) (int64, error) { msg := *msgIn msg.GasLimit = build.BlockGasLimit msg.GasFeeCap = types.NewInt(uint64(build.MinimumBaseFee) + 1) msg.GasPremium = types.NewInt(1) - currTs := a.Chain.GetHeaviestTipSet() - fromA, err := a.Stmgr.ResolveToKeyAddress(ctx, msgIn.From, currTs) + currTs := cstore.GetHeaviestTipSet() + fromA, err := smgr.ResolveToKeyAddress(ctx, msgIn.From, currTs) if err != nil { return -1, xerrors.Errorf("getting key address: %w", err) } - pending, ts := a.Mpool.PendingFor(fromA) + pending, ts := mpool.PendingFor(fromA) priorMsgs := make([]types.ChainMsg, 0, len(pending)) for _, m := range pending { priorMsgs = append(priorMsgs, m) @@ -163,11 +224,11 @@ func (a *GasAPI) GasEstimateGasLimit(ctx context.Context, msgIn *types.Message, // Try calling until we find a height with no migration. var res *api.InvocResult for { - res, err = a.Stmgr.CallWithGas(ctx, &msg, priorMsgs, ts) + res, err = smgr.CallWithGas(ctx, &msg, priorMsgs, ts) if err != stmgr.ErrExpensiveFork { break } - ts, err = a.Chain.GetTipSetFromKey(ts.Parents()) + ts, err = cstore.GetTipSetFromKey(ts.Parents()) if err != nil { return -1, xerrors.Errorf("getting parent tipset: %w", err) } @@ -180,7 +241,7 @@ func (a *GasAPI) GasEstimateGasLimit(ctx context.Context, msgIn *types.Message, } // Special case for PaymentChannel collect, which is deleting actor - st, err := a.Stmgr.ParentState(ts) + st, err := smgr.ParentState(ts) if err != nil { _ = err // somewhat ignore it as it can happen and we just want to detect @@ -206,17 +267,17 @@ func (a *GasAPI) GasEstimateGasLimit(ctx context.Context, msgIn *types.Message, return res.MsgRct.GasUsed + 76e3, nil } -func (a *GasAPI) GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, _ types.TipSetKey) (*types.Message, error) { +func (m *GasModule) GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, _ types.TipSetKey) (*types.Message, error) { if msg.GasLimit == 0 { - gasLimit, err := a.GasEstimateGasLimit(ctx, msg, types.TipSetKey{}) + gasLimit, err := m.GasEstimateGasLimit(ctx, msg, types.TipSetKey{}) if err != nil { return nil, xerrors.Errorf("estimating gas used: %w", err) } - msg.GasLimit = int64(float64(gasLimit) * a.Mpool.GetConfig().GasLimitOverestimation) + msg.GasLimit = int64(float64(gasLimit) * m.Mpool.GetConfig().GasLimitOverestimation) } if msg.GasPremium == types.EmptyInt || types.BigCmp(msg.GasPremium, types.NewInt(0)) == 0 { - gasPremium, err := a.GasEstimateGasPremium(ctx, 2, msg.From, msg.GasLimit, types.TipSetKey{}) + gasPremium, err := m.GasEstimateGasPremium(ctx, 2, msg.From, msg.GasLimit, types.TipSetKey{}) if err != nil { return nil, xerrors.Errorf("estimating gas price: %w", err) } @@ -224,7 +285,7 @@ func (a *GasAPI) GasEstimateMessageGas(ctx context.Context, msg *types.Message, } if msg.GasFeeCap == types.EmptyInt || types.BigCmp(msg.GasFeeCap, types.NewInt(0)) == 0 { - feeCap, err := a.GasEstimateFeeCap(ctx, msg, 20, types.EmptyTSK) + feeCap, err := m.GasEstimateFeeCap(ctx, msg, 20, types.EmptyTSK) if err != nil { return nil, xerrors.Errorf("estimating fee cap: %w", err) } From 7b1bec91ed10aeeea3e93c2801edcaed574592d2 Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Tue, 6 Oct 2020 11:03:27 +0200 Subject: [PATCH 11/18] feat: add tests for gateway ChainGetTipsetByHeight --- chain/types/mock/chain.go | 3 ++ cmd/lotus-gateway/api.go | 60 +++++++++++++++++++++++---------------- cmd/lotus-gateway/main.go | 2 +- 3 files changed, 39 insertions(+), 26 deletions(-) diff --git a/chain/types/mock/chain.go b/chain/types/mock/chain.go index 559630619..85437079c 100644 --- a/chain/types/mock/chain.go +++ b/chain/types/mock/chain.go @@ -59,9 +59,11 @@ func MkBlock(parents *types.TipSet, weightInc uint64, ticketNonce uint64) *types var pcids []cid.Cid var height abi.ChainEpoch weight := types.NewInt(weightInc) + var timestamp uint64 if parents != nil { pcids = parents.Cids() height = parents.Height() + 1 + timestamp = parents.MinTimestamp() + build.BlockDelaySecs weight = types.BigAdd(parents.Blocks()[0].ParentWeight, weight) } @@ -79,6 +81,7 @@ func MkBlock(parents *types.TipSet, weightInc uint64, ticketNonce uint64) *types ParentWeight: weight, Messages: c, Height: height, + Timestamp: timestamp, ParentStateRoot: pstateRoot, BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS, Data: []byte("boo! im a signature")}, ParentBaseFee: types.NewInt(uint64(build.MinimumBaseFee)), diff --git a/cmd/lotus-gateway/api.go b/cmd/lotus-gateway/api.go index 8192c58ec..0400a4a30 100644 --- a/cmd/lotus-gateway/api.go +++ b/cmd/lotus-gateway/api.go @@ -23,8 +23,20 @@ var ( ErrLookbackTooLong = fmt.Errorf("lookbacks of more than %s are disallowed", LookbackCap) ) +type rpcAPI 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) + GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) + MpoolPushUntrusted(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) + StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) + StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) + StateLookupID(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) + StateWaitMsgLimited(ctx context.Context, msg cid.Cid, confidence uint64, h abi.ChainEpoch) (*api.MsgLookup, error) +} + type GatewayAPI struct { - api api.FullNode + rpc rpcAPI } func (a *GatewayAPI) checkTipsetKey(ctx context.Context, tsk types.TipSetKey) error { @@ -32,7 +44,7 @@ func (a *GatewayAPI) checkTipsetKey(ctx context.Context, tsk types.TipSetKey) er return nil } - ts, err := a.api.ChainGetTipSet(ctx, tsk) + ts, err := a.rpc.ChainGetTipSet(ctx, tsk) if err != nil { return err } @@ -48,7 +60,6 @@ func (a *GatewayAPI) checkTipset(ts *types.TipSet) error { return nil } -// TODO: write tests for this check func (a *GatewayAPI) checkTipsetHeight(ts *types.TipSet, h abi.ChainEpoch) error { tsBlock := ts.Blocks()[0] heightDelta := time.Duration(uint64(tsBlock.Height-h)*build.BlockDelaySecs) * time.Second @@ -71,15 +82,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.api.ChainHead(ctx) + return a.rpc.ChainHead(ctx) } func (a *GatewayAPI) ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) { - return a.api.ChainGetTipSet(ctx, tsk) + return a.rpc.ChainGetTipSet(ctx, tsk) } func (a *GatewayAPI) ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error) { - ts, err := a.api.ChainGetTipSet(ctx, tsk) + ts, err := a.rpc.ChainGetTipSet(ctx, tsk) if err != nil { return nil, err } @@ -94,21 +105,7 @@ func (a *GatewayAPI) ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpoc return nil, err } - return a.api.ChainGetTipSetByHeight(ctx, h, tsk) -} - -func (a *GatewayAPI) MpoolPush(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) { - // TODO: additional anti-spam checks - - return a.api.MpoolPushUntrusted(ctx, sm) -} - -func (a *GatewayAPI) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) { - if err := a.checkTipsetKey(ctx, tsk); err != nil { - return address.Undef, err - } - - return a.api.StateAccountKey(ctx, addr, tsk) + return a.rpc.ChainGetTipSetByHeight(ctx, h, tsk) } func (a *GatewayAPI) GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) { @@ -116,7 +113,20 @@ func (a *GatewayAPI) GasEstimateMessageGas(ctx context.Context, msg *types.Messa return nil, err } - return a.api.GasEstimateMessageGas(ctx, msg, spec, tsk) + return a.rpc.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) +} + +func (a *GatewayAPI) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) { + if err := a.checkTipsetKey(ctx, tsk); err != nil { + return address.Undef, err + } + + return a.rpc.StateAccountKey(ctx, addr, tsk) } func (a *GatewayAPI) StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) { @@ -124,7 +134,7 @@ func (a *GatewayAPI) StateGetActor(ctx context.Context, actor address.Address, t return nil, err } - return a.api.StateGetActor(ctx, actor, tsk) + return a.rpc.StateGetActor(ctx, actor, tsk) } func (a *GatewayAPI) StateLookupID(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) { @@ -132,11 +142,11 @@ func (a *GatewayAPI) StateLookupID(ctx context.Context, addr address.Address, ts return address.Undef, err } - return a.api.StateLookupID(ctx, addr, tsk) + return a.rpc.StateLookupID(ctx, addr, tsk) } func (a *GatewayAPI) StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64) (*api.MsgLookup, error) { - return a.api.StateWaitMsgLimited(ctx, msg, confidence, stateWaitLookbackLimit) + return a.rpc.StateWaitMsgLimited(ctx, msg, confidence, stateWaitLookbackLimit) } var _ api.GatewayAPI = (*GatewayAPI)(nil) diff --git a/cmd/lotus-gateway/main.go b/cmd/lotus-gateway/main.go index c19599084..3c8eb4019 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{api: api}) + rpcServer.Register("Filecoin", &GatewayAPI{rpc: api}) mux.Handle("/rpc/v0", rpcServer) mux.PathPrefix("/").Handler(http.DefaultServeMux) From ef73b964fb40a916b921e484ee65111080e12de5 Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Wed, 7 Oct 2020 11:26:15 +0200 Subject: [PATCH 12/18] feat: add end-to-end test for lite mode --- api/test/ccupgrade.go | 2 +- api/test/deals.go | 8 +- api/test/mining.go | 6 +- api/test/paych.go | 2 +- api/test/test.go | 22 ++- api/test/window_post.go | 4 +- chain/messagesigner/messagesigner.go | 11 +- chain/messagesigner/messagesigner_test.go | 2 +- cli/paych_test.go | 2 +- cmd/lotus-gateway/api_test.go | 183 ++++++++++++++++++++++ cmd/lotus-gateway/endtoend_test.go | 140 +++++++++++++++++ cmd/lotus-storage-miner/allinfo_test.go | 4 +- cmd/lotus/daemon.go | 16 +- node/builder.go | 18 +++ node/impl/full/mpool.go | 2 +- node/modules/mpoolnonceapi.go | 33 ++++ node/test/builder.go | 154 +++++++++++------- 17 files changed, 510 insertions(+), 99 deletions(-) create mode 100644 cmd/lotus-gateway/api_test.go create mode 100644 cmd/lotus-gateway/endtoend_test.go create mode 100644 node/modules/mpoolnonceapi.go diff --git a/api/test/ccupgrade.go b/api/test/ccupgrade.go index 97fb665ed..38342d7fe 100644 --- a/api/test/ccupgrade.go +++ b/api/test/ccupgrade.go @@ -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, diff --git a/api/test/deals.go b/api/test/deals.go index aa5bfa716..8b4a7fe8b 100644 --- a/api/test/deals.go +++ b/api/test/deals.go @@ -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] diff --git a/api/test/mining.go b/api/test/mining.go index e19774a76..8147c224b 100644 --- a/api/test/mining.go +++ b/api/test/mining.go @@ -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 }) diff --git a/api/test/paych.go b/api/test/paych.go index e95773b6a..a8ccebdde 100644 --- a/api/test/paych.go +++ b/api/test/paych.go @@ -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] diff --git a/api/test/test.go b/api/test/test.go index 853267eff..63e50bd73 100644 --- a/api/test/test.go +++ b/api/test/test.go @@ -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 { diff --git a/api/test/window_post.go b/api/test/window_post.go index eadcdbb05..c45d5e1bc 100644 --- a/api/test/window_post.go +++ b/api/test/window_post.go @@ -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, diff --git a/chain/messagesigner/messagesigner.go b/chain/messagesigner/messagesigner.go index ac94d6a3e..1fe8f9565 100644 --- a/chain/messagesigner/messagesigner.go +++ b/chain/messagesigner/messagesigner.go @@ -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, diff --git a/chain/messagesigner/messagesigner_test.go b/chain/messagesigner/messagesigner_test.go index 04869ff6d..de74b8a48 100644 --- a/chain/messagesigner/messagesigner_test.go +++ b/chain/messagesigner/messagesigner_test.go @@ -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 { diff --git a/cli/paych_test.go b/cli/paych_test.go index 18782b4e8..2aa5c6009 100644 --- a/cli/paych_test.go +++ b/cli/paych_test.go @@ -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] diff --git a/cmd/lotus-gateway/api_test.go b/cmd/lotus-gateway/api_test.go new file mode 100644 index 000000000..ab42c9310 --- /dev/null +++ b/cmd/lotus-gateway/api_test.go @@ -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") +} diff --git a/cmd/lotus-gateway/endtoend_test.go b/cmd/lotus-gateway/endtoend_test.go new file mode 100644 index 000000000..e92c3248b --- /dev/null +++ b/cmd/lotus-gateway/endtoend_test.go @@ -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 +} diff --git a/cmd/lotus-storage-miner/allinfo_test.go b/cmd/lotus-storage-miner/allinfo_test.go index 8f744c4b3..fbe1d1617 100644 --- a/cmd/lotus-storage-miner/allinfo_test.go +++ b/cmd/lotus-storage-miner/allinfo_test.go @@ -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) diff --git a/cmd/lotus/daemon.go b/cmd/lotus/daemon.go index 495979ea9..8c154d7d7 100644 --- a/cmd/lotus/daemon.go +++ b/cmd/lotus/daemon.go @@ -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 diff --git a/node/builder.go b/node/builder.go index 101ec3dbc..d521fdb94 100644 --- a/node/builder.go +++ b/node/builder.go @@ -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( diff --git a/node/impl/full/mpool.go b/node/impl/full/mpool.go index fc32aecd2..8ad209f3f 100644 --- a/node/impl/full/mpool.go +++ b/node/impl/full/mpool.go @@ -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 diff --git a/node/modules/mpoolnonceapi.go b/node/modules/mpoolnonceapi.go new file mode 100644 index 000000000..294f4d954 --- /dev/null +++ b/node/modules/mpoolnonceapi.go @@ -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) diff --git a/node/test/builder.go b/node/test/builder.go index d9ec04460..f38201697 100644 --- a/node/test/builder.go +++ b/node/test/builder.go @@ -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)) +func CreateRPCServer(handler interface{}) (multiaddr.Multiaddr, string, error) { + rpcServer := jsonrpc.NewServer() + rpcServer.Register("Filecoin", handler) + testServ := httptest.NewServer(rpcServer) // todo: close - for i, a := range fullApis { - rpcServer := jsonrpc.NewServer() - rpcServer.Register("Filecoin", a) - 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) - if err != nil { - t.Fatal(err) - } - ma, err := parseWSSMultiAddr(addr) - if err != nil { - t.Fatal(err) - } - fulls[i].ListenAddr = ma + addr := testServ.Listener.Addr() + listenAddr := "ws://" + addr.String() + ma, err := parseWSMultiAddr(addr) + if err != nil { + return nil, "", 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 + return ma, listenAddr, err } -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 +} From e9e27cbbb0834c93d5b6df949382ac28d07e69ed Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Wed, 7 Oct 2020 13:59:32 +0200 Subject: [PATCH 13/18] 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) From b2834baa4b2818a7773859b47f70e4ccc3975993 Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Wed, 7 Oct 2020 18:14:12 +0200 Subject: [PATCH 14/18] feat: add msig available balance and vested to lite mode --- api/api_gateway.go | 2 + api/apistruct/struct.go | 28 +++++++++---- cmd/lotus-gateway/api.go | 21 ++++++++++ cmd/lotus-gateway/api_test.go | 8 ++++ cmd/lotus-gateway/endtoend_test.go | 66 ++++++++++++++++++++++++++++++ node/impl/full/state.go | 20 +++++---- 6 files changed, 127 insertions(+), 18 deletions(-) diff --git a/api/api_gateway.go b/api/api_gateway.go index 8c06304e9..95d28887d 100644 --- a/api/api_gateway.go +++ b/api/api_gateway.go @@ -15,6 +15,8 @@ type GatewayAPI interface { ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error) GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) MpoolPush(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) + MsigGetAvailableBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (types.BigInt, error) + MsigGetVested(ctx context.Context, addr address.Address, start types.TipSetKey, end types.TipSetKey) (types.BigInt, error) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) StateLookupID(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index 38d177aec..df84eac80 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -365,15 +365,17 @@ type WorkerStruct struct { type GatewayStruct struct { Internal struct { // TODO: does the gateway need perms? - ChainGetTipSet func(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) - ChainGetTipSetByHeight func(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error) - ChainHead func(ctx context.Context) (*types.TipSet, error) - GasEstimateMessageGas func(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) - MpoolPush func(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) - StateAccountKey func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) - StateGetActor func(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) - StateLookupID func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) - StateWaitMsg func(ctx context.Context, msg cid.Cid, confidence uint64) (*api.MsgLookup, error) + ChainGetTipSet func(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) + ChainGetTipSetByHeight func(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error) + ChainHead func(ctx context.Context) (*types.TipSet, error) + GasEstimateMessageGas func(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) + MpoolPush func(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) + MsigGetAvailableBalance func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (types.BigInt, error) + MsigGetVested func(ctx context.Context, addr address.Address, start types.TipSetKey, end types.TipSetKey) (types.BigInt, error) + StateAccountKey func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) + StateGetActor func(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) + StateLookupID func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) + StateWaitMsg func(ctx context.Context, msg cid.Cid, confidence uint64) (*api.MsgLookup, error) } } @@ -1412,6 +1414,14 @@ func (g GatewayStruct) MpoolPush(ctx context.Context, sm *types.SignedMessage) ( return g.Internal.MpoolPush(ctx, sm) } +func (g GatewayStruct) MsigGetAvailableBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (types.BigInt, error) { + return g.Internal.MsigGetAvailableBalance(ctx, addr, tsk) +} + +func (g GatewayStruct) MsigGetVested(ctx context.Context, addr address.Address, start types.TipSetKey, end types.TipSetKey) (types.BigInt, error) { + return g.Internal.MsigGetVested(ctx, addr, start, end) +} + func (g GatewayStruct) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) { return g.Internal.StateAccountKey(ctx, addr, tsk) } diff --git a/cmd/lotus-gateway/api.go b/cmd/lotus-gateway/api.go index 373c02eaa..d5fac0a06 100644 --- a/cmd/lotus-gateway/api.go +++ b/cmd/lotus-gateway/api.go @@ -31,6 +31,8 @@ type gatewayDepsAPI interface { ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error) GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) MpoolPushUntrusted(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) + MsigGetAvailableBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (types.BigInt, error) + MsigGetVested(ctx context.Context, addr address.Address, start types.TipSetKey, end types.TipSetKey) (types.BigInt, error) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) StateLookupID(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) @@ -123,6 +125,25 @@ func (a *GatewayAPI) MpoolPush(ctx context.Context, sm *types.SignedMessage) (ci return a.api.MpoolPushUntrusted(ctx, sm) } +func (a *GatewayAPI) MsigGetAvailableBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (types.BigInt, error) { + if err := a.checkTipsetKey(ctx, tsk); err != nil { + return types.NewInt(0), err + } + + return a.api.MsigGetAvailableBalance(ctx, addr, tsk) +} + +func (a *GatewayAPI) MsigGetVested(ctx context.Context, addr address.Address, start types.TipSetKey, end types.TipSetKey) (types.BigInt, error) { + if err := a.checkTipsetKey(ctx, start); err != nil { + return types.NewInt(0), err + } + if err := a.checkTipsetKey(ctx, end); err != nil { + return types.NewInt(0), err + } + + return a.api.MsigGetVested(ctx, addr, start, end) +} + func (a *GatewayAPI) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) { if err := a.checkTipsetKey(ctx, tsk); err != nil { return address.Undef, err diff --git a/cmd/lotus-gateway/api_test.go b/cmd/lotus-gateway/api_test.go index 2df2c9ba4..f34f887f5 100644 --- a/cmd/lotus-gateway/api_test.go +++ b/cmd/lotus-gateway/api_test.go @@ -166,6 +166,14 @@ func (m *mockGatewayDepsAPI) MpoolPushUntrusted(ctx context.Context, sm *types.S panic("implement me") } +func (m *mockGatewayDepsAPI) MsigGetAvailableBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (types.BigInt, error) { + panic("implement me") +} + +func (m *mockGatewayDepsAPI) MsigGetVested(ctx context.Context, addr address.Address, start types.TipSetKey, end types.TipSetKey) (types.BigInt, error) { + panic("implement me") +} + func (m *mockGatewayDepsAPI) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) { panic("implement me") } diff --git a/cmd/lotus-gateway/endtoend_test.go b/cmd/lotus-gateway/endtoend_test.go index 317e676d7..ad62be3fb 100644 --- a/cmd/lotus-gateway/endtoend_test.go +++ b/cmd/lotus-gateway/endtoend_test.go @@ -1,12 +1,17 @@ package main import ( + "bytes" "context" "fmt" "os" "testing" "time" + "github.com/filecoin-project/specs-actors/actors/builtin/multisig" + + init0 "github.com/filecoin-project/specs-actors/actors/builtin/init" + "golang.org/x/xerrors" "github.com/filecoin-project/go-address" @@ -77,6 +82,67 @@ func TestEndToEnd(t *testing.T) { ok, err := lite.WalletVerify(ctx, liteWalletAddr, data, sig) require.NoError(t, err) require.True(t, ok) + + // Create some wallets on the lite node to use for testing multisig + var walletAddrs []address.Address + for i := 0; i < 4; i++ { + addr, err := lite.WalletNew(ctx, wallet.ActSigType("secp256k1")) + require.NoError(t, err) + + walletAddrs = append(walletAddrs, addr) + + err = sendFunds(ctx, t, lite, liteWalletAddr, addr, types.NewInt(1e15)) + require.NoError(t, err) + } + + // Create an msig with three of the addresses and threshold of two sigs + msigAddrs := walletAddrs[:3] + amt := types.NewInt(1000) + addProposal, err := lite.MsigCreate(ctx, 2, msigAddrs, abi.ChainEpoch(50), amt, liteWalletAddr, types.NewInt(0)) + require.NoError(t, err) + + res, err := lite.StateWaitMsg(ctx, addProposal, 1) + require.NoError(t, err) + require.EqualValues(t, 0, res.Receipt.ExitCode) + + var execReturn init0.ExecReturn + err = execReturn.UnmarshalCBOR(bytes.NewReader(res.Receipt.Return)) + require.NoError(t, err) + + // Get available balance of msig: should be greater than zero and less + // than initial amount + msig := execReturn.IDAddress + msigBalance, err := lite.MsigGetAvailableBalance(ctx, msig, types.EmptyTSK) + require.NoError(t, err) + require.Greater(t, msigBalance.Int64(), int64(0)) + require.Less(t, msigBalance.Int64(), amt.Int64()) + + // Propose to add a new address to the msig + addProposal, err = lite.MsigAddPropose(ctx, msig, walletAddrs[0], walletAddrs[3], false) + require.NoError(t, err) + + res, err = lite.StateWaitMsg(ctx, addProposal, 1) + require.NoError(t, err) + require.EqualValues(t, 0, res.Receipt.ExitCode) + + var proposeReturn multisig.ProposeReturn + err = proposeReturn.UnmarshalCBOR(bytes.NewReader(res.Receipt.Return)) + require.NoError(t, err) + + // Approve proposal (proposer is first (implicit) signer, approver is + // second signer + txnID := uint64(proposeReturn.TxnID) + approval1, err := lite.MsigAddApprove(ctx, msig, walletAddrs[1], txnID, walletAddrs[0], walletAddrs[3], false) + require.NoError(t, err) + + res, err = lite.StateWaitMsg(ctx, approval1, 1) + require.NoError(t, err) + require.EqualValues(t, 0, res.Receipt.ExitCode) + + var approveReturn multisig.ApproveReturn + err = approveReturn.UnmarshalCBOR(bytes.NewReader(res.Receipt.Return)) + require.NoError(t, err) + require.True(t, approveReturn.Applied) } func sendFunds(ctx context.Context, t *testing.T, fromNode test.TestNode, fromAddr address.Address, toAddr address.Address, amt types.BigInt) error { diff --git a/node/impl/full/state.go b/node/impl/full/state.go index 540d959ed..34724e205 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -47,6 +47,8 @@ type StateModuleAPI interface { StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) StateLookupID(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64) (*api.MsgLookup, error) + MsigGetAvailableBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (types.BigInt, error) + MsigGetVested(ctx context.Context, addr address.Address, start types.TipSetKey, end types.TipSetKey) (types.BigInt, error) } // StateModule provides a default implementation of StateModuleAPI. @@ -828,17 +830,17 @@ func (a *StateAPI) StateCompute(ctx context.Context, height abi.ChainEpoch, msgs }, nil } -func (a *StateAPI) MsigGetAvailableBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (types.BigInt, error) { - ts, err := a.Chain.GetTipSetFromKey(tsk) +func (m *StateModule) MsigGetAvailableBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (types.BigInt, error) { + ts, err := m.Chain.GetTipSetFromKey(tsk) if err != nil { return types.EmptyInt, xerrors.Errorf("loading tipset %s: %w", tsk, err) } - act, err := a.StateManager.LoadActor(ctx, addr, ts) + act, err := m.StateManager.LoadActor(ctx, addr, ts) if err != nil { return types.EmptyInt, xerrors.Errorf("failed to load multisig actor: %w", err) } - msas, err := multisig.Load(a.Chain.Store(ctx), act) + msas, err := multisig.Load(m.Chain.Store(ctx), act) if err != nil { return types.EmptyInt, xerrors.Errorf("failed to load multisig actor state: %w", err) } @@ -887,13 +889,13 @@ func (a *StateAPI) MsigGetVestingSchedule(ctx context.Context, addr address.Addr }, nil } -func (a *StateAPI) MsigGetVested(ctx context.Context, addr address.Address, start types.TipSetKey, end types.TipSetKey) (types.BigInt, error) { - startTs, err := a.Chain.GetTipSetFromKey(start) +func (m *StateModule) MsigGetVested(ctx context.Context, addr address.Address, start types.TipSetKey, end types.TipSetKey) (types.BigInt, error) { + startTs, err := m.Chain.GetTipSetFromKey(start) if err != nil { return types.EmptyInt, xerrors.Errorf("loading start tipset %s: %w", start, err) } - endTs, err := a.Chain.GetTipSetFromKey(end) + endTs, err := m.Chain.GetTipSetFromKey(end) if err != nil { return types.EmptyInt, xerrors.Errorf("loading end tipset %s: %w", end, err) } @@ -904,12 +906,12 @@ func (a *StateAPI) MsigGetVested(ctx context.Context, addr address.Address, star return big.Zero(), nil } - act, err := a.StateManager.LoadActor(ctx, addr, endTs) + act, err := m.StateManager.LoadActor(ctx, addr, endTs) if err != nil { return types.EmptyInt, xerrors.Errorf("failed to load multisig actor at end epoch: %w", err) } - msas, err := multisig.Load(a.Chain.Store(ctx), act) + msas, err := multisig.Load(m.Chain.Store(ctx), act) if err != nil { return types.EmptyInt, xerrors.Errorf("failed to load multisig actor state: %w", err) } From d69e4c7cf2fc2c3e64cd83c1a15363e31c9b1ec1 Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Thu, 8 Oct 2020 11:24:31 +0200 Subject: [PATCH 15/18] refactor: lite-mode - simplify organization of dep injection --- api/test/test.go | 20 +++++-- chain/sync_test.go | 4 +- cmd/lotus-gateway/endtoend_test.go | 48 ++++++++--------- cmd/lotus/daemon.go | 21 ++++---- node/builder.go | 87 +++++++++++++++--------------- node/test/builder.go | 24 ++++----- 6 files changed, 105 insertions(+), 99 deletions(-) diff --git a/api/test/test.go b/api/test/test.go index 63e50bd73..014859865 100644 --- a/api/test/test.go +++ b/api/test/test.go @@ -35,6 +35,7 @@ var PresealGenesis = -1 const GenesisPreseals = 2 +// Options for setting up a mock storage miner type StorageMiner struct { Full int Preseal int @@ -42,12 +43,19 @@ type StorageMiner struct { type OptionGenerator func([]TestNode) node.Option +// Options for setting up a mock full node +type FullNodeOpts struct { + Lite bool // run node in "lite" mode + Opts OptionGenerator // generate dependency injection options +} + // APIBuilder is a function which is invoked in test suite to provide // test nodes and networks // +// fullOpts array defines options for each full node // storage array defines storage nodes, numbers in the array specify full node // index the storage node 'belongs' to -type APIBuilder func(t *testing.T, full []OptionGenerator, storage []StorageMiner, opts ...node.Option) ([]TestNode, []TestStorageNode) +type APIBuilder func(t *testing.T, full []FullNodeOpts, storage []StorageMiner, opts ...node.Option) ([]TestNode, []TestStorageNode) type testSuite struct { makeNodes APIBuilder } @@ -65,11 +73,13 @@ func TestApis(t *testing.T, b APIBuilder) { t.Run("testMiningReal", ts.testMiningReal) } -func DefaultFullOpts(nFull int) []OptionGenerator { - full := make([]OptionGenerator, nFull) +func DefaultFullOpts(nFull int) []FullNodeOpts { + full := make([]FullNodeOpts, nFull) for i := range full { - full[i] = func(nodes []TestNode) node.Option { - return node.Options() + full[i] = FullNodeOpts{ + Opts: func(nodes []TestNode) node.Option { + return node.Options() + }, } } return full diff --git a/chain/sync_test.go b/chain/sync_test.go index 318f6d82f..1fe90a071 100644 --- a/chain/sync_test.go +++ b/chain/sync_test.go @@ -222,7 +222,7 @@ func (tu *syncTestUtil) addSourceNode(gen int) { var out api.FullNode stop, err := node.New(tu.ctx, - node.FullAPI(&out), + node.FullAPI(&out, false), node.Online(), node.Repo(sourceRepo), node.MockHost(tu.mn), @@ -254,7 +254,7 @@ func (tu *syncTestUtil) addClientNode() int { var out api.FullNode stop, err := node.New(tu.ctx, - node.FullAPI(&out), + node.FullAPI(&out, false), node.Online(), node.Repo(repo.NewMemory(nil)), node.MockHost(tu.mn), diff --git a/cmd/lotus-gateway/endtoend_test.go b/cmd/lotus-gateway/endtoend_test.go index ad62be3fb..206034968 100644 --- a/cmd/lotus-gateway/endtoend_test.go +++ b/cmd/lotus-gateway/endtoend_test.go @@ -8,32 +8,23 @@ import ( "testing" "time" + init0 "github.com/filecoin-project/specs-actors/actors/builtin/init" "github.com/filecoin-project/specs-actors/actors/builtin/multisig" - init0 "github.com/filecoin-project/specs-actors/actors/builtin/init" - + "github.com/stretchr/testify/require" "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" + "github.com/filecoin-project/lotus/api/client" "github.com/filecoin-project/lotus/api/test" "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/wallet" + "github.com/filecoin-project/lotus/node" + builder "github.com/filecoin-project/lotus/node/test" ) func init() { @@ -179,20 +170,23 @@ func startNodes(ctx context.Context, t *testing.T, blocktime time.Duration) (tes // Full node test.OneFull, // Lite node - func(nodes []test.TestNode) node.Option { - fullNode := nodes[0] + test.FullNodeOpts{ + Lite: true, + Opts: func(nodes []test.TestNode) node.Option { + fullNode := nodes[0] - // Create a gateway server in front of the full node - _, addr, err := builder.CreateRPCServer(&GatewayAPI{api: fullNode}) - require.NoError(t, err) + // Create a gateway server in front of the full node + _, addr, err := builder.CreateRPCServer(&GatewayAPI{api: fullNode}) + require.NoError(t, err) - // 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) + // 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) + // Provide the gateway API to dependency injection + return node.Override(new(api.GatewayAPI), gapi) + }, }, ) n, sn := builder.RPCMockSbBuilder(t, opts, test.OneMiner) diff --git a/cmd/lotus/daemon.go b/cmd/lotus/daemon.go index 8c154d7d7..084f75ef2 100644 --- a/cmd/lotus/daemon.go +++ b/cmd/lotus/daemon.go @@ -136,6 +136,8 @@ var DaemonCmd = &cli.Command{ }, }, Action: func(cctx *cli.Context) error { + isLite := cctx.Bool("lite") + err := runmetrics.Enable(runmetrics.RunMetricOptions{ EnableCPU: true, EnableMemory: true, @@ -195,8 +197,10 @@ var DaemonCmd = &cli.Command{ return xerrors.Errorf("repo init error: %w", err) } - if err := paramfetch.GetParams(lcli.ReqContext(cctx), build.ParametersJSON(), 0); err != nil { - return xerrors.Errorf("fetching proof parameters: %w", err) + if !isLite { + if err := paramfetch.GetParams(lcli.ReqContext(cctx), build.ParametersJSON(), 0); err != nil { + return xerrors.Errorf("fetching proof parameters: %w", err) + } } var genBytes []byte @@ -243,10 +247,9 @@ var DaemonCmd = &cli.Command{ shutdownChan := make(chan struct{}) - // If the daemon is started in "lite mode", replace key APIs - // with a thin client to a gateway server - liteMode := node.Options() - isLite := cctx.Bool("lite") + // If the daemon is started in "lite mode", provide a GatewayAPI + // for RPC calls + liteModeDeps := node.Options() if isLite { gapi, closer, err := lcli.GetGatewayAPI(cctx) if err != nil { @@ -254,13 +257,13 @@ var DaemonCmd = &cli.Command{ } defer closer() - liteMode = node.LiteModeOverrides(gapi) + liteModeDeps = node.Override(new(api.GatewayAPI), gapi) } var api api.FullNode stop, err := node.New(ctx, - node.FullAPI(&api), + node.FullAPI(&api, isLite), node.Override(new(dtypes.Bootstrapper), isBootstrapper), node.Override(new(dtypes.ShutdownChan), shutdownChan), @@ -268,7 +271,7 @@ var DaemonCmd = &cli.Command{ node.Repo(r), genesis, - liteMode, + liteModeDeps, node.ApplyIf(func(s *node.Settings) bool { return cctx.IsSet("api") }, node.Override(node.SetApiEndpointKey, func(lr repo.LockedRepo) error { diff --git a/node/builder.go b/node/builder.go index d521fdb94..452104ba8 100644 --- a/node/builder.go +++ b/node/builder.go @@ -6,6 +6,13 @@ import ( "os" "time" + "github.com/filecoin-project/lotus/chain" + "github.com/filecoin-project/lotus/chain/exchange" + "github.com/filecoin-project/lotus/chain/store" + "github.com/filecoin-project/lotus/chain/vm" + "github.com/filecoin-project/lotus/chain/wallet" + "github.com/filecoin-project/lotus/node/hello" + logging "github.com/ipfs/go-log" ci "github.com/libp2p/go-libp2p-core/crypto" "github.com/libp2p/go-libp2p-core/host" @@ -29,9 +36,7 @@ import ( storage2 "github.com/filecoin-project/specs-storage/storage" "github.com/filecoin-project/lotus/api" - "github.com/filecoin-project/lotus/chain" "github.com/filecoin-project/lotus/chain/beacon" - "github.com/filecoin-project/lotus/chain/exchange" "github.com/filecoin-project/lotus/chain/gen" "github.com/filecoin-project/lotus/chain/gen/slashfilter" "github.com/filecoin-project/lotus/chain/market" @@ -39,10 +44,7 @@ import ( "github.com/filecoin-project/lotus/chain/messagesigner" "github.com/filecoin-project/lotus/chain/metrics" "github.com/filecoin-project/lotus/chain/stmgr" - "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" - "github.com/filecoin-project/lotus/chain/vm" - "github.com/filecoin-project/lotus/chain/wallet" sectorstorage "github.com/filecoin-project/lotus/extern/sector-storage" "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper" "github.com/filecoin-project/lotus/extern/sector-storage/stores" @@ -56,7 +58,6 @@ import ( "github.com/filecoin-project/lotus/markets/storageadapter" "github.com/filecoin-project/lotus/miner" "github.com/filecoin-project/lotus/node/config" - "github.com/filecoin-project/lotus/node/hello" "github.com/filecoin-project/lotus/node/impl" "github.com/filecoin-project/lotus/node/impl/common" "github.com/filecoin-project/lotus/node/impl/full" @@ -159,7 +160,7 @@ type Settings struct { Online bool // Online option applied Config bool // Config option applied - + Lite bool // Start node in "lite" mode } func defaults() []Option { @@ -233,6 +234,10 @@ func isType(t repo.RepoType) func(s *Settings) bool { // Online sets up basic libp2p node func Online() Option { + isFullOrLiteNode := func(s *Settings) bool { return s.nodeType == repo.FullNode } + isFullNode := func(s *Settings) bool { return s.nodeType == repo.FullNode && !s.Lite } + isLiteNode := func(s *Settings) bool { return s.nodeType == repo.FullNode && s.Lite } + return Options( // make sure that online is applied before Config. // This is important because Config overrides some of Online units @@ -246,17 +251,14 @@ func Online() Option { // common Override(new(*slashfilter.SlashFilter), modules.NewSlashFilter), - // Full node - - ApplyIf(isType(repo.FullNode), + // Full node or lite node + ApplyIf(isFullOrLiteNode, // TODO: Fix offline mode Override(new(dtypes.BootstrapPeers), modules.BuiltinBootstrap), Override(new(dtypes.DrandBootstrap), modules.DrandBootstrap), Override(new(dtypes.DrandSchedule), modules.BuiltinDrandConfig), - Override(HandleIncomingMessagesKey, modules.HandleIncomingMessages), - Override(new(ffiwrapper.Verifier), ffiwrapper.ProofVerifier), Override(new(vm.SyscallBuilder), vm.Syscalls), Override(new(*store.ChainStore), modules.ChainStore), @@ -264,15 +266,8 @@ 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))), - Override(new(full.GasModuleAPI), From(new(full.GasModule))), - Override(new(full.MpoolModuleAPI), From(new(full.MpoolModule))), - Override(new(full.StateModuleAPI), From(new(full.StateModule))), - Override(new(stmgr.StateManagerAPI), From(new(*stmgr.StateManager))), - Override(new(dtypes.ChainGCLocker), blockstore.NewGCLocker), Override(new(dtypes.ChainGCBlockstore), modules.ChainGCBlockstore), Override(new(dtypes.ChainBitswap), modules.ChainBitswap), @@ -297,12 +292,6 @@ func Online() Option { Override(new(dtypes.Graphsync), modules.Graphsync), Override(new(*dtypes.MpoolLocker), new(dtypes.MpoolLocker)), - - Override(RunHelloKey, modules.RunHello), - Override(RunChainExchangeKey, modules.RunChainExchange), - Override(RunPeerMgrKey, modules.RunPeerMgr), - Override(HandleIncomingBlocksKey, modules.HandleIncomingBlocks), - Override(new(*discoveryimpl.Local), modules.NewLocalDiscovery), Override(new(discovery.PeerResolver), modules.RetrievalResolver), @@ -321,8 +310,34 @@ func Online() Option { Override(SettlePaymentChannelsKey, settler.SettlePaymentChannels), ), + // Lite node + ApplyIf(isLiteNode, + Override(new(messagesigner.MpoolNonceAPI), From(new(modules.MpoolNonceAPI))), + 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), + ), + + // Full node + ApplyIf(isFullNode, + Override(new(messagesigner.MpoolNonceAPI), From(new(*messagepool.MessagePool))), + Override(new(full.ChainModuleAPI), From(new(full.ChainModule))), + Override(new(full.GasModuleAPI), From(new(full.GasModule))), + Override(new(full.MpoolModuleAPI), From(new(full.MpoolModule))), + Override(new(full.StateModuleAPI), From(new(full.StateModule))), + Override(new(stmgr.StateManagerAPI), From(new(*stmgr.StateManager))), + + Override(RunHelloKey, modules.RunHello), + Override(RunChainExchangeKey, modules.RunChainExchange), + Override(RunPeerMgrKey, modules.RunPeerMgr), + Override(HandleIncomingMessagesKey, modules.HandleIncomingMessages), + Override(HandleIncomingBlocksKey, modules.HandleIncomingBlocks), + ), + // miner - ApplyIf(func(s *Settings) bool { return s.nodeType == repo.StorageMiner }, + ApplyIf(isType(repo.StorageMiner), Override(new(api.Common), From(new(common.CommonAPI))), Override(new(sectorstorage.StorageAuth), modules.StorageAuth), @@ -402,23 +417,6 @@ 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( @@ -533,10 +531,11 @@ func Repo(r repo.Repo) Option { } } -func FullAPI(out *api.FullNode) Option { +func FullAPI(out *api.FullNode, lite bool) Option { return Options( func(s *Settings) error { s.nodeType = repo.FullNode + s.Lite = lite return nil }, func(s *Settings) error { diff --git a/node/test/builder.go b/node/test/builder.go index ebf96e98f..cdb0b43dd 100644 --- a/node/test/builder.go +++ b/node/test/builder.go @@ -141,23 +141,23 @@ func CreateTestStorageNode(ctx context.Context, t *testing.T, waddr address.Addr return test.TestStorageNode{StorageMiner: minerapi, MineOne: mineOne} } -func Builder(t *testing.T, fullOpts []test.OptionGenerator, storage []test.StorageMiner, opts ...node.Option) ([]test.TestNode, []test.TestStorageNode) { +func Builder(t *testing.T, fullOpts []test.FullNodeOpts, 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) { +func MockSbBuilder(t *testing.T, fullOpts []test.FullNodeOpts, 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) { +func RPCBuilder(t *testing.T, fullOpts []test.FullNodeOpts, 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) { +func RPCMockSbBuilder(t *testing.T, fullOpts []test.FullNodeOpts, 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) { +func mockBuilderOpts(t *testing.T, fullOpts []test.FullNodeOpts, storage []test.StorageMiner, opts []node.Option, rpc bool) ([]test.TestNode, []test.TestStorageNode) { ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) @@ -237,14 +237,15 @@ func mockBuilderOpts(t *testing.T, fullOpts []test.OptionGenerator, storage []te } stop, err := node.New(ctx, - node.FullAPI(&fulls[i].FullNode), + node.FullAPI(&fulls[i].FullNode, fullOpts[i].Lite), node.Online(), node.Repo(repo.NewMemory(nil)), node.MockHost(mn), node.Test(), genesis, - fullOpts[i](fulls), + + fullOpts[i].Opts(fulls), node.Options(opts...), ) if err != nil { @@ -315,7 +316,7 @@ func mockBuilderOpts(t *testing.T, fullOpts []test.OptionGenerator, storage []te return fulls, storers } -func mockSbBuilderOpts(t *testing.T, fullOpts []test.OptionGenerator, storage []test.StorageMiner, options []node.Option, rpc bool) ([]test.TestNode, []test.TestStorageNode) { +func mockSbBuilderOpts(t *testing.T, fullOpts []test.FullNodeOpts, storage []test.StorageMiner, options []node.Option, rpc bool) ([]test.TestNode, []test.TestStorageNode) { ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) @@ -393,10 +394,8 @@ func mockSbBuilderOpts(t *testing.T, fullOpts []test.OptionGenerator, storage [] genesis = node.Override(new(modules.Genesis), modules.LoadGenesis(genbuf.Bytes())) } - var err error - // TODO: Don't ignore stop stop, err := node.New(ctx, - node.FullAPI(&fulls[i].FullNode), + node.FullAPI(&fulls[i].FullNode, fullOpts[i].Lite), node.Online(), node.Repo(repo.NewMemory(nil)), node.MockHost(mn), @@ -405,7 +404,8 @@ func mockSbBuilderOpts(t *testing.T, fullOpts []test.OptionGenerator, storage [] node.Override(new(ffiwrapper.Verifier), mock.MockVerifier), genesis, - fullOpts[i](fulls), + + fullOpts[i].Opts(fulls), node.Options(options...), ) if err != nil { From 200a95f82420757840b5c28a7e7a27c43e347ff1 Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Thu, 8 Oct 2020 12:21:15 +0200 Subject: [PATCH 16/18] refactor: unify test builder full node options --- api/test/ccupgrade.go | 9 +-------- api/test/test.go | 17 ++++++++++++++++- api/test/window_post.go | 8 +------- cmd/lotus-storage-miner/allinfo_test.go | 6 ++---- node/test/builder.go | 20 +++++++++----------- 5 files changed, 29 insertions(+), 31 deletions(-) diff --git a/api/test/ccupgrade.go b/api/test/ccupgrade.go index 38342d7fe..4a860c661 100644 --- a/api/test/ccupgrade.go +++ b/api/test/ccupgrade.go @@ -12,10 +12,7 @@ import ( "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/lotus/build" - "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/types" - "github.com/filecoin-project/lotus/node" "github.com/filecoin-project/lotus/node/impl" ) @@ -37,11 +34,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, OneFull, OneMiner, node.Override(new(stmgr.UpgradeSchedule), stmgr.UpgradeSchedule{{ - Network: build.ActorUpgradeNetworkVersion, - Height: upgradeHeight, - Migration: stmgr.UpgradeActorsV2, - }})) + n, sn := b(t, []FullNodeOpts{FullNodeWithUpgradeAt(upgradeHeight)}, OneMiner) client := n[0].FullNode.(*impl.FullNodeAPI) miner := sn[0] diff --git a/api/test/test.go b/api/test/test.go index 014859865..35b397740 100644 --- a/api/test/test.go +++ b/api/test/test.go @@ -4,11 +4,14 @@ import ( "context" "testing" + "github.com/filecoin-project/lotus/chain/stmgr" + "github.com/multiformats/go-multiaddr" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/miner" @@ -55,7 +58,7 @@ type FullNodeOpts struct { // fullOpts array defines options for each full node // storage array defines storage nodes, numbers in the array specify full node // index the storage node 'belongs' to -type APIBuilder func(t *testing.T, full []FullNodeOpts, storage []StorageMiner, opts ...node.Option) ([]TestNode, []TestStorageNode) +type APIBuilder func(t *testing.T, full []FullNodeOpts, storage []StorageMiner) ([]TestNode, []TestStorageNode) type testSuite struct { makeNodes APIBuilder } @@ -89,6 +92,18 @@ var OneMiner = []StorageMiner{{Full: 0, Preseal: PresealGenesis}} var OneFull = DefaultFullOpts(1) var TwoFull = DefaultFullOpts(2) +var FullNodeWithUpgradeAt = func(upgradeHeight abi.ChainEpoch) FullNodeOpts { + return FullNodeOpts{ + Opts: func(nodes []TestNode) node.Option { + return node.Override(new(stmgr.UpgradeSchedule), stmgr.UpgradeSchedule{{ + Network: build.ActorUpgradeNetworkVersion, + Height: upgradeHeight, + Migration: stmgr.UpgradeActorsV2, + }}) + }, + } +} + func (ts *testSuite) testVersion(t *testing.T) { build.RunningNodeType = build.NodeFull diff --git a/api/test/window_post.go b/api/test/window_post.go index c45d5e1bc..7bc56a562 100644 --- a/api/test/window_post.go +++ b/api/test/window_post.go @@ -15,11 +15,9 @@ import ( "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/lotus/extern/sector-storage/mock" sealing "github.com/filecoin-project/lotus/extern/storage-sealing" - "github.com/filecoin-project/lotus/node" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" - "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/types" bminer "github.com/filecoin-project/lotus/miner" "github.com/filecoin-project/lotus/node/impl" @@ -133,11 +131,7 @@ func testWindowPostUpgrade(t *testing.T, b APIBuilder, blocktime time.Duration, ctx, cancel := context.WithCancel(context.Background()) defer cancel() - n, sn := b(t, OneFull, OneMiner, node.Override(new(stmgr.UpgradeSchedule), stmgr.UpgradeSchedule{{ - Network: build.ActorUpgradeNetworkVersion, - Height: upgradeHeight, - Migration: stmgr.UpgradeActorsV2, - }})) + n, sn := b(t, []FullNodeOpts{FullNodeWithUpgradeAt(upgradeHeight)}, OneMiner) client := n[0].FullNode.(*impl.FullNodeAPI) miner := sn[0] diff --git a/cmd/lotus-storage-miner/allinfo_test.go b/cmd/lotus-storage-miner/allinfo_test.go index fbe1d1617..a458c024b 100644 --- a/cmd/lotus-storage-miner/allinfo_test.go +++ b/cmd/lotus-storage-miner/allinfo_test.go @@ -5,8 +5,6 @@ import ( "testing" "time" - "github.com/filecoin-project/lotus/node" - logging "github.com/ipfs/go-log/v2" "github.com/stretchr/testify/require" "github.com/urfave/cli/v2" @@ -64,8 +62,8 @@ func TestMinerAllInfo(t *testing.T) { require.NoError(t, infoAllCmd.Action(cctx)) } - 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...) + bp := func(t *testing.T, fullOpts []test.FullNodeOpts, storage []test.StorageMiner) ([]test.TestNode, []test.TestStorageNode) { + n, sn = builder.Builder(t, fullOpts, storage) t.Run("pre-info-all", run) diff --git a/node/test/builder.go b/node/test/builder.go index cdb0b43dd..289fae841 100644 --- a/node/test/builder.go +++ b/node/test/builder.go @@ -141,23 +141,23 @@ func CreateTestStorageNode(ctx context.Context, t *testing.T, waddr address.Addr return test.TestStorageNode{StorageMiner: minerapi, MineOne: mineOne} } -func Builder(t *testing.T, fullOpts []test.FullNodeOpts, storage []test.StorageMiner, opts ...node.Option) ([]test.TestNode, []test.TestStorageNode) { - return mockBuilderOpts(t, fullOpts, storage, opts, false) +func Builder(t *testing.T, fullOpts []test.FullNodeOpts, storage []test.StorageMiner) ([]test.TestNode, []test.TestStorageNode) { + return mockBuilderOpts(t, fullOpts, storage, false) } -func MockSbBuilder(t *testing.T, fullOpts []test.FullNodeOpts, storage []test.StorageMiner, opts ...node.Option) ([]test.TestNode, []test.TestStorageNode) { - return mockSbBuilderOpts(t, fullOpts, storage, opts, false) +func MockSbBuilder(t *testing.T, fullOpts []test.FullNodeOpts, storage []test.StorageMiner) ([]test.TestNode, []test.TestStorageNode) { + return mockSbBuilderOpts(t, fullOpts, storage, false) } -func RPCBuilder(t *testing.T, fullOpts []test.FullNodeOpts, storage []test.StorageMiner, opts ...node.Option) ([]test.TestNode, []test.TestStorageNode) { - return mockBuilderOpts(t, fullOpts, storage, opts, true) +func RPCBuilder(t *testing.T, fullOpts []test.FullNodeOpts, storage []test.StorageMiner) ([]test.TestNode, []test.TestStorageNode) { + return mockBuilderOpts(t, fullOpts, storage, true) } func RPCMockSbBuilder(t *testing.T, fullOpts []test.FullNodeOpts, storage []test.StorageMiner) ([]test.TestNode, []test.TestStorageNode) { - return mockSbBuilderOpts(t, fullOpts, storage, []node.Option{}, true) + return mockSbBuilderOpts(t, fullOpts, storage, true) } -func mockBuilderOpts(t *testing.T, fullOpts []test.FullNodeOpts, storage []test.StorageMiner, opts []node.Option, rpc bool) ([]test.TestNode, []test.TestStorageNode) { +func mockBuilderOpts(t *testing.T, fullOpts []test.FullNodeOpts, storage []test.StorageMiner, rpc bool) ([]test.TestNode, []test.TestStorageNode) { ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) @@ -246,7 +246,6 @@ func mockBuilderOpts(t *testing.T, fullOpts []test.FullNodeOpts, storage []test. genesis, fullOpts[i].Opts(fulls), - node.Options(opts...), ) if err != nil { t.Fatal(err) @@ -316,7 +315,7 @@ func mockBuilderOpts(t *testing.T, fullOpts []test.FullNodeOpts, storage []test. return fulls, storers } -func mockSbBuilderOpts(t *testing.T, fullOpts []test.FullNodeOpts, storage []test.StorageMiner, options []node.Option, rpc bool) ([]test.TestNode, []test.TestStorageNode) { +func mockSbBuilderOpts(t *testing.T, fullOpts []test.FullNodeOpts, storage []test.StorageMiner, rpc bool) ([]test.TestNode, []test.TestStorageNode) { ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) @@ -406,7 +405,6 @@ func mockSbBuilderOpts(t *testing.T, fullOpts []test.FullNodeOpts, storage []tes genesis, fullOpts[i].Opts(fulls), - node.Options(options...), ) if err != nil { t.Fatalf("%+v", err) From 7f7c9e978fc7641eecdb6c7271b468d32f217b4f Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Thu, 8 Oct 2020 13:25:51 +0200 Subject: [PATCH 17/18] feat: hide lite mode flag --- cmd/lotus/daemon.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/lotus/daemon.go b/cmd/lotus/daemon.go index 084f75ef2..298cb7a67 100644 --- a/cmd/lotus/daemon.go +++ b/cmd/lotus/daemon.go @@ -114,8 +114,9 @@ var DaemonCmd = &cli.Command{ Usage: "halt the process after importing chain from file", }, &cli.BoolFlag{ - Name: "lite", - Usage: "start lotus in lite mode", + Name: "lite", + Usage: "start lotus in lite mode", + Hidden: true, }, &cli.StringFlag{ Name: "pprof", From ab8286fa381c63d13e3e79609ba7659e2974e429 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 9 Oct 2020 18:43:22 +0200 Subject: [PATCH 18/18] Fix docsgen, lotus-soup build --- chain/sync_test.go | 4 +-- cmd/lotus/daemon.go | 2 +- documentation/en/api-methods.md | 44 +++++++++++++++++++++++++++++++++ node/builder.go | 13 ++++++++-- node/test/builder.go | 4 +-- 5 files changed, 60 insertions(+), 7 deletions(-) diff --git a/chain/sync_test.go b/chain/sync_test.go index 1fe90a071..318f6d82f 100644 --- a/chain/sync_test.go +++ b/chain/sync_test.go @@ -222,7 +222,7 @@ func (tu *syncTestUtil) addSourceNode(gen int) { var out api.FullNode stop, err := node.New(tu.ctx, - node.FullAPI(&out, false), + node.FullAPI(&out), node.Online(), node.Repo(sourceRepo), node.MockHost(tu.mn), @@ -254,7 +254,7 @@ func (tu *syncTestUtil) addClientNode() int { var out api.FullNode stop, err := node.New(tu.ctx, - node.FullAPI(&out, false), + node.FullAPI(&out), node.Online(), node.Repo(repo.NewMemory(nil)), node.MockHost(tu.mn), diff --git a/cmd/lotus/daemon.go b/cmd/lotus/daemon.go index 298cb7a67..5964a2458 100644 --- a/cmd/lotus/daemon.go +++ b/cmd/lotus/daemon.go @@ -264,7 +264,7 @@ var DaemonCmd = &cli.Command{ var api api.FullNode stop, err := node.New(ctx, - node.FullAPI(&api, isLite), + node.FullAPI(&api, node.Lite(isLite)), node.Override(new(dtypes.Bootstrapper), isBootstrapper), node.Override(new(dtypes.ShutdownChan), shutdownChan), diff --git a/documentation/en/api-methods.md b/documentation/en/api-methods.md index 8a288a4bf..cc6d92e4f 100644 --- a/documentation/en/api-methods.md +++ b/documentation/en/api-methods.md @@ -167,6 +167,7 @@ * [StateVerifiedRegistryRootKey](#StateVerifiedRegistryRootKey) * [StateVerifierStatus](#StateVerifierStatus) * [StateWaitMsg](#StateWaitMsg) + * [StateWaitMsgLimited](#StateWaitMsgLimited) * [Sync](#Sync) * [SyncCheckBad](#SyncCheckBad) * [SyncCheckpoint](#SyncCheckpoint) @@ -4322,6 +4323,49 @@ Response: } ``` +### StateWaitMsgLimited +StateWaitMsgLimited looks back up to limit epochs in the chain for a message. +If not found, it blocks until the message arrives on chain, and gets to the +indicated confidence depth. + + +Perms: read + +Inputs: +```json +[ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + 42, + 10101 +] +``` + +Response: +```json +{ + "Message": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Receipt": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "GasUsed": 9 + }, + "ReturnDec": {}, + "TipSet": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ], + "Height": 10101 +} +``` + ## Sync The Sync method group contains methods for interacting with and observing the lotus sync service. diff --git a/node/builder.go b/node/builder.go index 452104ba8..71295dbc8 100644 --- a/node/builder.go +++ b/node/builder.go @@ -531,13 +531,22 @@ func Repo(r repo.Repo) Option { } } -func FullAPI(out *api.FullNode, lite bool) Option { +type FullOption = Option + +func Lite(enable bool) FullOption { + return func(s *Settings) error { + s.Lite = enable + return nil + } +} + +func FullAPI(out *api.FullNode, fopts ...FullOption) Option { return Options( func(s *Settings) error { s.nodeType = repo.FullNode - s.Lite = lite return nil }, + Options(fopts...), func(s *Settings) error { resAPI := &impl.FullNodeAPI{} s.invokes[ExtractApiKey] = fx.Populate(resAPI) diff --git a/node/test/builder.go b/node/test/builder.go index 289fae841..4aa8a55ea 100644 --- a/node/test/builder.go +++ b/node/test/builder.go @@ -237,7 +237,7 @@ func mockBuilderOpts(t *testing.T, fullOpts []test.FullNodeOpts, storage []test. } stop, err := node.New(ctx, - node.FullAPI(&fulls[i].FullNode, fullOpts[i].Lite), + node.FullAPI(&fulls[i].FullNode, node.Lite(fullOpts[i].Lite)), node.Online(), node.Repo(repo.NewMemory(nil)), node.MockHost(mn), @@ -394,7 +394,7 @@ func mockSbBuilderOpts(t *testing.T, fullOpts []test.FullNodeOpts, storage []tes } stop, err := node.New(ctx, - node.FullAPI(&fulls[i].FullNode, fullOpts[i].Lite), + node.FullAPI(&fulls[i].FullNode, node.Lite(fullOpts[i].Lite)), node.Online(), node.Repo(repo.NewMemory(nil)), node.MockHost(mn),