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)