From 71c2f5c714cf3ab6e71cced80ca7fd2be4d953e1 Mon Sep 17 00:00:00 2001 From: simlecode <69969590+simlecode@users.noreply.github.com> Date: Tue, 7 Mar 2023 15:58:04 +0800 Subject: [PATCH] feat: add compute state api --- app/submodule/chain/chaininfo_api.go | 16 ++ cmd/state.go | 85 +++++++ pkg/statemanger/state_manger.go | 105 +++++++++ venus-shared/api/chain/v0/chain.go | 33 +++ venus-shared/api/chain/v0/method.md | 214 ++++++++++++++++++ .../api/chain/v0/mock/mock_fullnode.go | 15 ++ venus-shared/api/chain/v0/proxy_gen.go | 4 + venus-shared/api/chain/v1/chain.go | 33 +++ venus-shared/api/chain/v1/method.md | 214 ++++++++++++++++++ .../api/chain/v1/mock/mock_fullnode.go | 15 ++ venus-shared/api/chain/v1/proxy_gen.go | 4 + venus-shared/compatible-checks/api-diff.txt | 2 - 12 files changed, 738 insertions(+), 2 deletions(-) diff --git a/app/submodule/chain/chaininfo_api.go b/app/submodule/chain/chaininfo_api.go index 8463f2c5cd..a233b96770 100644 --- a/app/submodule/chain/chaininfo_api.go +++ b/app/submodule/chain/chaininfo_api.go @@ -869,3 +869,19 @@ func (cia *chainInfoAPI) ChainGetEvents(ctx context.Context, root cid.Cid) ([]ty return ret, err } + +func (cia *chainInfoAPI) StateCompute(ctx context.Context, height abi.ChainEpoch, msgs []*types.Message, tsk types.TipSetKey) (*types.ComputeStateOutput, error) { + ts, err := cia.ChainGetTipSet(ctx, tsk) + if err != nil { + return nil, fmt.Errorf("loading tipset %s: %w", tsk, err) + } + st, t, err := statemanger.ComputeState(ctx, cia.chain.Stmgr, height, msgs, ts) + if err != nil { + return nil, err + } + + return &types.ComputeStateOutput{ + Root: st, + Trace: t, + }, nil +} diff --git a/cmd/state.go b/cmd/state.go index 8d6e69a3f7..c0019fd1f1 100644 --- a/cmd/state.go +++ b/cmd/state.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io" + "os" "strconv" "github.com/filecoin-project/venus/cmd/tablewriter" @@ -53,6 +54,7 @@ var stateCmd = &cmds.Command{ "list-actor": stateListActorCmd, "actor-cids": stateSysActorCIDsCmd, "replay": stateReplayCmd, + "compute-state": StateComputeStateCmd, }, } @@ -709,3 +711,86 @@ func makeActorView(act *types.Actor, addr address.Address) *ActorView { Head: act.Head, } } + +var StateComputeStateCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Perform state computations", + }, + Options: []cmds.Option{ + cmds.Uint64Option("vm-height", "set the height that the vm will see"), + cmds.BoolOption("apply-mpool-messages", "apply messages from the mempool to the computed state"), + cmds.BoolOption("show-trace", "print out full execution trace for given tipset"), + cmds.BoolOption("json", "generate json output"), + cmds.StringOption("compute-state-output", "a json file containing pre-existing compute-state output, to generate html reports without rerunning state changes"), + cmds.BoolOption("no-timing", "don't show timing information in html traces"), + cmds.StringOption("tipset", ""), + }, + Run: func(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment) error { + ctx := req.Context + + ts, err := LoadTipSet(ctx, req, getEnv(env).ChainAPI) + if err != nil { + return err + } + h, _ := req.Options["vm-height"].(uint64) + if h == 0 { + h = uint64(ts.Height()) + } + + var msgs []*types.Message + if applyMsg, _ := req.Options["apply-mpool-messages"].(bool); applyMsg { + pmsgs, err := getEnv(env).MessagePoolAPI.MpoolSelect(ctx, ts.Key(), 1) + if err != nil { + return err + } + + for _, sm := range pmsgs { + msgs = append(msgs, &sm.Message) + } + } + + var stout *types.ComputeStateOutput + if csofile, _ := req.Options["compute-state-output"].(string); len(csofile) != 0 { + data, err := os.ReadFile(csofile) + if err != nil { + return err + } + + var o types.ComputeStateOutput + if err := json.Unmarshal(data, &o); err != nil { + return err + } + + stout = &o + } else { + o, err := getEnv(env).ChainAPI.StateCompute(ctx, abi.ChainEpoch(h), msgs, ts.Key()) + if err != nil { + return err + } + + stout = o + } + + buf := &bytes.Buffer{} + writer := NewSilentWriter(buf) + + if ok, _ := req.Options["json"].(bool); ok { + out, err := json.Marshal(stout) + if err != nil { + return err + } + writer.Println(string(out)) + return re.Emit(buf) + } + + writer.Println("computed state cid: ", stout.Root) + if showTrace, _ := req.Options["show-trace"].(bool); showTrace { + for _, ir := range stout.Trace { + writer.Printf("%s\t%s\t%s\t%d\t%x\t%d\t%x\n", ir.Msg.From, ir.Msg.To, ir.Msg.Value, ir.Msg.Method, ir.Msg.Params, ir.MsgRct.ExitCode, ir.MsgRct.Return) + printInternalExecutions(writer, "\t", ir.ExecutionTrace.Subcalls) + } + } + + return re.Emit(buf) + }, +} diff --git a/pkg/statemanger/state_manger.go b/pkg/statemanger/state_manger.go index 03edbe5050..7e246681ad 100644 --- a/pkg/statemanger/state_manger.go +++ b/pkg/statemanger/state_manger.go @@ -14,12 +14,15 @@ import ( "github.com/filecoin-project/venus/pkg/chain" "github.com/filecoin-project/venus/pkg/consensus" "github.com/filecoin-project/venus/pkg/fork" + "github.com/filecoin-project/venus/pkg/fvm" appstate "github.com/filecoin-project/venus/pkg/state" "github.com/filecoin-project/venus/pkg/state/tree" "github.com/filecoin-project/venus/pkg/vm" "github.com/filecoin-project/venus/pkg/vm/gas" + "github.com/filecoin-project/venus/pkg/vm/vmcontext" "github.com/filecoin-project/venus/venus-shared/actors/builtin/market" "github.com/filecoin-project/venus/venus-shared/actors/builtin/paych" + blockstoreutil "github.com/filecoin-project/venus/venus-shared/blockstore" "github.com/filecoin-project/venus/venus-shared/types" "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log/v2" @@ -443,7 +446,39 @@ func (s *Stmgr) Replay(ctx context.Context, ts *types.TipSet, msgCID cid.Cid) (* return outm, outr, nil } +func (s *Stmgr) ExecutionTrace(ctx context.Context, ts *types.TipSet) (cid.Cid, []*types.InvocResult, error) { + var invocTrace []*types.InvocResult + + cb := func(mcid cid.Cid, msg *types.Message, ret *vm.Ret) error { + ir := &types.InvocResult{ + MsgCid: mcid, + Msg: msg, + MsgRct: &ret.Receipt, + ExecutionTrace: ret.GasTracker.ExecutionTrace, + Duration: ret.Duration, + } + if ret.ActorErr != nil { + ir.Error = ret.ActorErr.Error() + } + if !ret.OutPuts.Refund.Nil() { + ir.GasCost = MakeMsgGasCost(msg, ret) + } + + invocTrace = append(invocTrace, ir) + + return nil + } + + st, _, err := s.cp.RunStateTransition(ctx, ts, cb, true) + if err != nil { + return cid.Undef, nil, err + } + + return st, invocTrace, nil +} + func MakeMsgGasCost(msg *types.Message, ret *vm.Ret) types.MsgGasCost { + fmt.Println(msg.RequiredFunds(), ret.OutPuts.Refund) return types.MsgGasCost{ Message: msg.Cid(), GasUsed: big.NewInt(ret.Receipt.GasUsed), @@ -456,6 +491,76 @@ func MakeMsgGasCost(msg *types.Message, ret *vm.Ret) types.MsgGasCost { } } +func ComputeState(ctx context.Context, s *Stmgr, height abi.ChainEpoch, msgs []*types.Message, ts *types.TipSet) (cid.Cid, []*types.InvocResult, error) { + if ts == nil { + ts = s.cs.GetHead() + } + + base, trace, err := s.ExecutionTrace(ctx, ts) + if err != nil { + return cid.Undef, nil, err + } + + for i := ts.Height(); i < height; i++ { + // Technically, the tipset we're passing in here should be ts+1, but that may not exist. + base, err = s.fork.HandleStateForks(ctx, base, i, ts) + if err != nil { + return cid.Undef, nil, fmt.Errorf("error handling state forks: %w", err) + } + + // We intentionally don't run cron here, as we may be trying to look into the + // future. It's not guaranteed to be accurate... but that's fine. + } + + buffStore := blockstoreutil.NewTieredBstore(s.cs.Blockstore(), blockstoreutil.NewTemporarySync()) + vmopt := vm.VmOption{ + CircSupplyCalculator: func(ctx context.Context, epoch abi.ChainEpoch, tree tree.Tree) (abi.TokenAmount, error) { + cs, err := s.cs.GetCirculatingSupplyDetailed(ctx, epoch, tree) + if err != nil { + return abi.TokenAmount{}, err + } + return cs.FilCirculating, nil + }, + PRoot: base, + Epoch: ts.Height(), + Timestamp: ts.MinTimestamp(), + Rnd: consensus.NewHeadRandomness(s.rnd, ts.Key()), + Bsstore: buffStore, + SysCallsImpl: s.syscallsImpl, + GasPriceSchedule: s.gasSchedule, + NetworkVersion: s.GetNetworkVersion(ctx, height), + BaseFee: ts.Blocks()[0].ParentBaseFee, + Fork: s.fork, + LookbackStateGetter: vmcontext.LookbackStateGetterForTipset(ctx, s.cs, s.fork, ts), + TipSetGetter: vmcontext.TipSetGetterForTipset(s.cs.GetTipSetByHeight, ts), + Tracing: true, + ActorDebugging: s.actorDebugging, + } + + vmi, err := fvm.NewVM(ctx, vmopt) + if err != nil { + return cid.Undef, nil, err + } + + for i, msg := range msgs { + // TODO: Use the signed message length for secp messages + ret, err := vmi.ApplyMessage(ctx, msg) + if err != nil { + return cid.Undef, nil, fmt.Errorf("applying message %s: %w", msg.Cid(), err) + } + if ret.Receipt.ExitCode != 0 { + s.log.Infof("compute state apply message %d failed (exit: %d): %s", i, ret.Receipt.ExitCode, ret.ActorErr) + } + } + + root, err := vmi.Flush(ctx) + if err != nil { + return cid.Undef, nil, err + } + + return root, trace, nil +} + func (s *Stmgr) FlushChainHead() (*types.TipSet, error) { head := s.cs.GetHead() _, _, err := s.RunStateTransition(context.TODO(), head, nil, false) diff --git a/venus-shared/api/chain/v0/chain.go b/venus-shared/api/chain/v0/chain.go index 0b0c17aa70..18b15dd80d 100644 --- a/venus-shared/api/chain/v0/chain.go +++ b/venus-shared/api/chain/v0/chain.go @@ -83,6 +83,39 @@ type IChainInfo interface { StateActorManifestCID(context.Context, network.Version) (cid.Cid, error) //perm:read StateCall(ctx context.Context, msg *types.Message, tsk types.TipSetKey) (*types.InvocResult, error) //perm:read StateReplay(context.Context, types.TipSetKey, cid.Cid) (*types.InvocResult, error) //perm:read + // StateCompute is a flexible command that applies the given messages on the given tipset. + // The messages are run as though the VM were at the provided height. + // + // When called, StateCompute will: + // - Load the provided tipset, or use the current chain head if not provided + // - Compute the tipset state of the provided tipset on top of the parent state + // - (note that this step runs before vmheight is applied to the execution) + // - Execute state upgrade if any were scheduled at the epoch, or in null + // blocks preceding the tipset + // - Call the cron actor on null blocks preceding the tipset + // - For each block in the tipset + // - Apply messages in blocks in the specified + // - Award block reward by calling the reward actor + // - Call the cron actor for the current epoch + // - If the specified vmheight is higher than the current epoch, apply any + // needed state upgrades to the state + // - Apply the specified messages to the state + // + // The vmheight parameter sets VM execution epoch, and can be used to simulate + // message execution in different network versions. If the specified vmheight + // epoch is higher than the epoch of the specified tipset, any state upgrades + // until the vmheight will be executed on the state before applying messages + // specified by the user. + // + // Note that the initial tipset state computation is not affected by the + // vmheight parameter - only the messages in the `apply` set are + // + // If the caller wants to simply compute the state, vmheight should be set to + // the epoch of the specified tipset. + // + // Messages in the `apply` parameter must have the correct nonces, and gas + // values set. + StateCompute(context.Context, abi.ChainEpoch, []*types.Message, types.TipSetKey) (*types.ComputeStateOutput, error) //perm:read } type IMinerState interface { diff --git a/venus-shared/api/chain/v0/method.md b/venus-shared/api/chain/v0/method.md index 4559b006ee..c95bff29eb 100644 --- a/venus-shared/api/chain/v0/method.md +++ b/venus-shared/api/chain/v0/method.md @@ -48,6 +48,7 @@ curl http://:/rpc/v0 -X POST -H "Content-Type: application/json" -H " * [StateActorCodeCIDs](#stateactorcodecids) * [StateActorManifestCID](#stateactormanifestcid) * [StateCall](#statecall) + * [StateCompute](#statecompute) * [StateGetNetworkParams](#stategetnetworkparams) * [StateGetReceipt](#stategetreceipt) * [StateNetworkName](#statenetworkname) @@ -1405,6 +1406,219 @@ Response: } ``` +### StateCompute +StateCompute is a flexible command that applies the given messages on the given tipset. +The messages are run as though the VM were at the provided height. + +When called, StateCompute will: +- Load the provided tipset, or use the current chain head if not provided +- Compute the tipset state of the provided tipset on top of the parent state +- (note that this step runs before vmheight is applied to the execution) +- Execute state upgrade if any were scheduled at the epoch, or in null +blocks preceding the tipset +- Call the cron actor on null blocks preceding the tipset +- For each block in the tipset +- Apply messages in blocks in the specified +- Award block reward by calling the reward actor +- Call the cron actor for the current epoch +- If the specified vmheight is higher than the current epoch, apply any +needed state upgrades to the state +- Apply the specified messages to the state + +The vmheight parameter sets VM execution epoch, and can be used to simulate +message execution in different network versions. If the specified vmheight +epoch is higher than the epoch of the specified tipset, any state upgrades +until the vmheight will be executed on the state before applying messages +specified by the user. + +Note that the initial tipset state computation is not affected by the +vmheight parameter - only the messages in the `apply` set are + +If the caller wants to simply compute the state, vmheight should be set to +the epoch of the specified tipset. + +Messages in the `apply` parameter must have the correct nonces, and gas +values set. + + +Perms: read + +Inputs: +```json +[ + 10101, + [ + { + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + }, + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==" + } + ], + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] +] +``` + +Response: +```json +{ + "Root": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Trace": [ + { + "MsgCid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Msg": { + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + }, + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==" + }, + "MsgRct": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "GasUsed": 9, + "EventsRoot": null + }, + "GasCost": { + "Message": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "GasUsed": "0", + "BaseFeeBurn": "0", + "OverEstimationBurn": "0", + "MinerPenalty": "0", + "MinerTip": "0", + "Refund": "0", + "TotalCost": "0" + }, + "ExecutionTrace": { + "Msg": { + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + }, + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==" + }, + "MsgRct": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "GasUsed": 9, + "EventsRoot": null + }, + "Error": "string value", + "Duration": 60000000000, + "GasCharges": [ + { + "Name": "string value", + "loc": [ + { + "File": "string value", + "Line": 123, + "Function": "string value" + } + ], + "tg": 9, + "cg": 9, + "sg": 9, + "vtg": 9, + "vcg": 9, + "vsg": 9, + "tt": 60000000000, + "ex": {} + } + ], + "Subcalls": [ + { + "Msg": { + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + }, + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==" + }, + "MsgRct": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "GasUsed": 9, + "EventsRoot": null + }, + "Error": "string value", + "Duration": 60000000000, + "GasCharges": [ + { + "Name": "string value", + "loc": [ + { + "File": "string value", + "Line": 123, + "Function": "string value" + } + ], + "tg": 9, + "cg": 9, + "sg": 9, + "vtg": 9, + "vcg": 9, + "vsg": 9, + "tt": 60000000000, + "ex": {} + } + ], + "Subcalls": null + } + ] + }, + "Error": "string value", + "Duration": 60000000000 + } + ] +} +``` + ### StateGetNetworkParams StateGetNetworkParams return current network params diff --git a/venus-shared/api/chain/v0/mock/mock_fullnode.go b/venus-shared/api/chain/v0/mock/mock_fullnode.go index c574e40cb4..e7a9878061 100644 --- a/venus-shared/api/chain/v0/mock/mock_fullnode.go +++ b/venus-shared/api/chain/v0/mock/mock_fullnode.go @@ -1608,6 +1608,21 @@ func (mr *MockFullNodeMockRecorder) StateCirculatingSupply(arg0, arg1 interface{ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateCirculatingSupply", reflect.TypeOf((*MockFullNode)(nil).StateCirculatingSupply), arg0, arg1) } +// StateCompute mocks base method. +func (m *MockFullNode) StateCompute(arg0 context.Context, arg1 abi.ChainEpoch, arg2 []*types.Message, arg3 types0.TipSetKey) (*types0.ComputeStateOutput, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateCompute", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(*types0.ComputeStateOutput) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateCompute indicates an expected call of StateCompute. +func (mr *MockFullNodeMockRecorder) StateCompute(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateCompute", reflect.TypeOf((*MockFullNode)(nil).StateCompute), arg0, arg1, arg2, arg3) +} + // StateDealProviderCollateralBounds mocks base method. func (m *MockFullNode) StateDealProviderCollateralBounds(arg0 context.Context, arg1 abi.PaddedPieceSize, arg2 bool, arg3 types0.TipSetKey) (types0.DealCollateralBounds, error) { m.ctrl.T.Helper() diff --git a/venus-shared/api/chain/v0/proxy_gen.go b/venus-shared/api/chain/v0/proxy_gen.go index 2713a3aa68..32717345e9 100644 --- a/venus-shared/api/chain/v0/proxy_gen.go +++ b/venus-shared/api/chain/v0/proxy_gen.go @@ -259,6 +259,7 @@ type IChainInfoStruct struct { StateActorCodeCIDs func(context.Context, network.Version) (map[string]cid.Cid, error) `perm:"read"` StateActorManifestCID func(context.Context, network.Version) (cid.Cid, error) `perm:"read"` StateCall func(ctx context.Context, msg *types.Message, tsk types.TipSetKey) (*types.InvocResult, error) `perm:"read"` + StateCompute func(context.Context, abi.ChainEpoch, []*types.Message, types.TipSetKey) (*types.ComputeStateOutput, error) `perm:"read"` StateGetNetworkParams func(ctx context.Context) (*types.NetworkParams, error) `perm:"read"` StateGetReceipt func(ctx context.Context, msg cid.Cid, from types.TipSetKey) (*types.MessageReceipt, error) `perm:"read"` StateNetworkName func(ctx context.Context) (types.NetworkName, error) `perm:"read"` @@ -358,6 +359,9 @@ func (s *IChainInfoStruct) StateActorManifestCID(p0 context.Context, p1 network. func (s *IChainInfoStruct) StateCall(p0 context.Context, p1 *types.Message, p2 types.TipSetKey) (*types.InvocResult, error) { return s.Internal.StateCall(p0, p1, p2) } +func (s *IChainInfoStruct) StateCompute(p0 context.Context, p1 abi.ChainEpoch, p2 []*types.Message, p3 types.TipSetKey) (*types.ComputeStateOutput, error) { + return s.Internal.StateCompute(p0, p1, p2, p3) +} func (s *IChainInfoStruct) StateGetNetworkParams(p0 context.Context) (*types.NetworkParams, error) { return s.Internal.StateGetNetworkParams(p0) } diff --git a/venus-shared/api/chain/v1/chain.go b/venus-shared/api/chain/v1/chain.go index 88d151352b..5ea92ed118 100644 --- a/venus-shared/api/chain/v1/chain.go +++ b/venus-shared/api/chain/v1/chain.go @@ -117,6 +117,39 @@ type IChainInfo interface { StateReplay(context.Context, types.TipSetKey, cid.Cid) (*types.InvocResult, error) //perm:read // ChainGetEvents returns the events under an event AMT root CID. ChainGetEvents(context.Context, cid.Cid) ([]types.Event, error) //perm:read + // StateCompute is a flexible command that applies the given messages on the given tipset. + // The messages are run as though the VM were at the provided height. + // + // When called, StateCompute will: + // - Load the provided tipset, or use the current chain head if not provided + // - Compute the tipset state of the provided tipset on top of the parent state + // - (note that this step runs before vmheight is applied to the execution) + // - Execute state upgrade if any were scheduled at the epoch, or in null + // blocks preceding the tipset + // - Call the cron actor on null blocks preceding the tipset + // - For each block in the tipset + // - Apply messages in blocks in the specified + // - Award block reward by calling the reward actor + // - Call the cron actor for the current epoch + // - If the specified vmheight is higher than the current epoch, apply any + // needed state upgrades to the state + // - Apply the specified messages to the state + // + // The vmheight parameter sets VM execution epoch, and can be used to simulate + // message execution in different network versions. If the specified vmheight + // epoch is higher than the epoch of the specified tipset, any state upgrades + // until the vmheight will be executed on the state before applying messages + // specified by the user. + // + // Note that the initial tipset state computation is not affected by the + // vmheight parameter - only the messages in the `apply` set are + // + // If the caller wants to simply compute the state, vmheight should be set to + // the epoch of the specified tipset. + // + // Messages in the `apply` parameter must have the correct nonces, and gas + // values set. + StateCompute(context.Context, abi.ChainEpoch, []*types.Message, types.TipSetKey) (*types.ComputeStateOutput, error) //perm:read } type IMinerState interface { diff --git a/venus-shared/api/chain/v1/method.md b/venus-shared/api/chain/v1/method.md index 1b26a996a5..9b2ed40944 100644 --- a/venus-shared/api/chain/v1/method.md +++ b/venus-shared/api/chain/v1/method.md @@ -46,6 +46,7 @@ curl http://:/rpc/v1 -X POST -H "Content-Type: application/json" -H " * [StateActorCodeCIDs](#stateactorcodecids) * [StateActorManifestCID](#stateactormanifestcid) * [StateCall](#statecall) + * [StateCompute](#statecompute) * [StateGetBeaconEntry](#stategetbeaconentry) * [StateGetNetworkParams](#stategetnetworkparams) * [StateGetRandomnessFromBeacon](#stategetrandomnessfrombeacon) @@ -1448,6 +1449,219 @@ Response: } ``` +### StateCompute +StateCompute is a flexible command that applies the given messages on the given tipset. +The messages are run as though the VM were at the provided height. + +When called, StateCompute will: +- Load the provided tipset, or use the current chain head if not provided +- Compute the tipset state of the provided tipset on top of the parent state +- (note that this step runs before vmheight is applied to the execution) +- Execute state upgrade if any were scheduled at the epoch, or in null +blocks preceding the tipset +- Call the cron actor on null blocks preceding the tipset +- For each block in the tipset +- Apply messages in blocks in the specified +- Award block reward by calling the reward actor +- Call the cron actor for the current epoch +- If the specified vmheight is higher than the current epoch, apply any +needed state upgrades to the state +- Apply the specified messages to the state + +The vmheight parameter sets VM execution epoch, and can be used to simulate +message execution in different network versions. If the specified vmheight +epoch is higher than the epoch of the specified tipset, any state upgrades +until the vmheight will be executed on the state before applying messages +specified by the user. + +Note that the initial tipset state computation is not affected by the +vmheight parameter - only the messages in the `apply` set are + +If the caller wants to simply compute the state, vmheight should be set to +the epoch of the specified tipset. + +Messages in the `apply` parameter must have the correct nonces, and gas +values set. + + +Perms: read + +Inputs: +```json +[ + 10101, + [ + { + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + }, + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==" + } + ], + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] +] +``` + +Response: +```json +{ + "Root": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Trace": [ + { + "MsgCid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Msg": { + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + }, + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==" + }, + "MsgRct": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "GasUsed": 9, + "EventsRoot": null + }, + "GasCost": { + "Message": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "GasUsed": "0", + "BaseFeeBurn": "0", + "OverEstimationBurn": "0", + "MinerPenalty": "0", + "MinerTip": "0", + "Refund": "0", + "TotalCost": "0" + }, + "ExecutionTrace": { + "Msg": { + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + }, + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==" + }, + "MsgRct": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "GasUsed": 9, + "EventsRoot": null + }, + "Error": "string value", + "Duration": 60000000000, + "GasCharges": [ + { + "Name": "string value", + "loc": [ + { + "File": "string value", + "Line": 123, + "Function": "string value" + } + ], + "tg": 9, + "cg": 9, + "sg": 9, + "vtg": 9, + "vcg": 9, + "vsg": 9, + "tt": 60000000000, + "ex": {} + } + ], + "Subcalls": [ + { + "Msg": { + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + }, + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==" + }, + "MsgRct": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "GasUsed": 9, + "EventsRoot": null + }, + "Error": "string value", + "Duration": 60000000000, + "GasCharges": [ + { + "Name": "string value", + "loc": [ + { + "File": "string value", + "Line": 123, + "Function": "string value" + } + ], + "tg": 9, + "cg": 9, + "sg": 9, + "vtg": 9, + "vcg": 9, + "vsg": 9, + "tt": 60000000000, + "ex": {} + } + ], + "Subcalls": null + } + ] + }, + "Error": "string value", + "Duration": 60000000000 + } + ] +} +``` + ### StateGetBeaconEntry StateGetBeaconEntry returns the beacon entry for the given filecoin epoch. If the entry has not yet been produced, the call will block until the entry diff --git a/venus-shared/api/chain/v1/mock/mock_fullnode.go b/venus-shared/api/chain/v1/mock/mock_fullnode.go index efba34c5b4..94c25a62fd 100644 --- a/venus-shared/api/chain/v1/mock/mock_fullnode.go +++ b/venus-shared/api/chain/v1/mock/mock_fullnode.go @@ -2255,6 +2255,21 @@ func (mr *MockFullNodeMockRecorder) StateCirculatingSupply(arg0, arg1 interface{ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateCirculatingSupply", reflect.TypeOf((*MockFullNode)(nil).StateCirculatingSupply), arg0, arg1) } +// StateCompute mocks base method. +func (m *MockFullNode) StateCompute(arg0 context.Context, arg1 abi.ChainEpoch, arg2 []*types.Message, arg3 types0.TipSetKey) (*types0.ComputeStateOutput, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateCompute", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(*types0.ComputeStateOutput) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateCompute indicates an expected call of StateCompute. +func (mr *MockFullNodeMockRecorder) StateCompute(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateCompute", reflect.TypeOf((*MockFullNode)(nil).StateCompute), arg0, arg1, arg2, arg3) +} + // StateComputeDataCID mocks base method. func (m *MockFullNode) StateComputeDataCID(arg0 context.Context, arg1 address.Address, arg2 abi.RegisteredSealProof, arg3 []abi.DealID, arg4 types0.TipSetKey) (cid.Cid, error) { m.ctrl.T.Helper() diff --git a/venus-shared/api/chain/v1/proxy_gen.go b/venus-shared/api/chain/v1/proxy_gen.go index 5084c92842..bf320b13fc 100644 --- a/venus-shared/api/chain/v1/proxy_gen.go +++ b/venus-shared/api/chain/v1/proxy_gen.go @@ -287,6 +287,7 @@ type IChainInfoStruct struct { StateActorCodeCIDs func(context.Context, network.Version) (map[string]cid.Cid, error) `perm:"read"` StateActorManifestCID func(context.Context, network.Version) (cid.Cid, error) `perm:"read"` StateCall func(ctx context.Context, msg *types.Message, tsk types.TipSetKey) (*types.InvocResult, error) `perm:"read"` + StateCompute func(context.Context, abi.ChainEpoch, []*types.Message, types.TipSetKey) (*types.ComputeStateOutput, error) `perm:"read"` StateGetBeaconEntry func(ctx context.Context, epoch abi.ChainEpoch) (*types.BeaconEntry, error) `perm:"read"` StateGetNetworkParams func(ctx context.Context) (*types.NetworkParams, error) `perm:"read"` StateGetRandomnessFromBeacon func(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte, tsk types.TipSetKey) (abi.Randomness, error) `perm:"read"` @@ -386,6 +387,9 @@ func (s *IChainInfoStruct) StateActorManifestCID(p0 context.Context, p1 network. func (s *IChainInfoStruct) StateCall(p0 context.Context, p1 *types.Message, p2 types.TipSetKey) (*types.InvocResult, error) { return s.Internal.StateCall(p0, p1, p2) } +func (s *IChainInfoStruct) StateCompute(p0 context.Context, p1 abi.ChainEpoch, p2 []*types.Message, p3 types.TipSetKey) (*types.ComputeStateOutput, error) { + return s.Internal.StateCompute(p0, p1, p2, p3) +} func (s *IChainInfoStruct) StateGetBeaconEntry(p0 context.Context, p1 abi.ChainEpoch) (*types.BeaconEntry, error) { return s.Internal.StateGetBeaconEntry(p0, p1) } diff --git a/venus-shared/compatible-checks/api-diff.txt b/venus-shared/compatible-checks/api-diff.txt index a090b67bda..1618742131 100644 --- a/venus-shared/compatible-checks/api-diff.txt +++ b/venus-shared/compatible-checks/api-diff.txt @@ -92,7 +92,6 @@ github.com/filecoin-project/venus/venus-shared/api/chain/v0.FullNode <> github.c - Shutdown - StateAllMinerFaults - StateChangedActors - - StateCompute - StateDecodeParams > StateGetNetworkParams {[func(context.Context) (*types.NetworkParams, error) <> func(context.Context) (*api.NetworkParams, error)] base=func out type: #0 input; nested={[*types.NetworkParams <> *api.NetworkParams] base=pointed type; nested={[types.NetworkParams <> api.NetworkParams] base=struct field; nested={[types.NetworkParams <> api.NetworkParams] base=exported field type: #5 field named ForkUpgradeParams; nested={[types.ForkUpgradeParams <> api.ForkUpgradeParams] base=struct field; nested={[types.ForkUpgradeParams <> api.ForkUpgradeParams] base=exported fields count: 22 != 23; nested=nil}}}}}} - StateGetRandomnessFromBeacon @@ -221,7 +220,6 @@ github.com/filecoin-project/venus/venus-shared/api/chain/v1.FullNode <> github.c + SetConcurrent + SetPassword - Shutdown - - StateCompute > StateGetNetworkParams {[func(context.Context) (*types.NetworkParams, error) <> func(context.Context) (*api.NetworkParams, error)] base=func out type: #0 input; nested={[*types.NetworkParams <> *api.NetworkParams] base=pointed type; nested={[types.NetworkParams <> api.NetworkParams] base=struct field; nested={[types.NetworkParams <> api.NetworkParams] base=exported field type: #5 field named ForkUpgradeParams; nested={[types.ForkUpgradeParams <> api.ForkUpgradeParams] base=struct field; nested={[types.ForkUpgradeParams <> api.ForkUpgradeParams] base=exported fields count: 22 != 23; nested=nil}}}}}} + StateMinerSectorSize + StateMinerWorkerAddress