From cb801d47c7712898aa25a757ce225916f605a354 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 15 Oct 2020 18:48:51 -0400 Subject: [PATCH 1/6] Add GasCost to InvocResult --- api/api_full.go | 1 + chain/stmgr/call.go | 2 ++ chain/stmgr/stmgr.go | 1 + chain/stmgr/utils.go | 13 +++++++++++++ node/impl/full/state.go | 13 +++---------- 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/api/api_full.go b/api/api_full.go index 65182078a..130cba957 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -740,6 +740,7 @@ type InvocResult struct { MsgCid cid.Cid Msg *types.Message MsgRct *types.MessageReceipt + GasCost MsgGasCost ExecutionTrace types.ExecutionTrace Error string Duration time.Duration diff --git a/chain/stmgr/call.go b/chain/stmgr/call.go index 86035140b..5eaa2e16a 100644 --- a/chain/stmgr/call.go +++ b/chain/stmgr/call.go @@ -116,6 +116,7 @@ func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types. MsgCid: msg.Cid(), Msg: msg, MsgRct: &ret.MessageReceipt, + GasCost: MakeMsgGasCost(msg, ret), ExecutionTrace: ret.ExecutionTrace, Error: errs, Duration: ret.Duration, @@ -232,6 +233,7 @@ func (sm *StateManager) CallWithGas(ctx context.Context, msg *types.Message, pri MsgCid: msg.Cid(), Msg: msg, MsgRct: &ret.MessageReceipt, + GasCost: MakeMsgGasCost(msg, ret), ExecutionTrace: ret.ExecutionTrace, Error: errs, Duration: ret.Duration, diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index b7d5050e1..d5cb1958d 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -203,6 +203,7 @@ func traceFunc(trace *[]*api.InvocResult) func(mcid cid.Cid, msg *types.Message, MsgCid: mcid, Msg: msg, MsgRct: &ret.MessageReceipt, + GasCost: MakeMsgGasCost(msg, ret), ExecutionTrace: ret.ExecutionTrace, Duration: ret.Duration, } diff --git a/chain/stmgr/utils.go b/chain/stmgr/utils.go index cbc78ca5b..de4f947df 100644 --- a/chain/stmgr/utils.go +++ b/chain/stmgr/utils.go @@ -701,3 +701,16 @@ func CheckTotalFIL(ctx context.Context, sm *StateManager, ts *types.TipSet) (abi return sum, nil } + +func MakeMsgGasCost(msg *types.Message, ret *vm.ApplyRet) api.MsgGasCost { + return api.MsgGasCost{ + Message: msg.Cid(), + GasUsed: big.NewInt(ret.GasUsed), + BaseFeeBurn: ret.GasCosts.BaseFeeBurn, + OverEstimationBurn: ret.GasCosts.OverEstimationBurn, + MinerPenalty: ret.GasCosts.MinerPenalty, + MinerTip: ret.GasCosts.MinerTip, + Refund: ret.GasCosts.Refund, + TotalCost: big.Sub(msg.RequiredFunds(), ret.GasCosts.Refund), + } +} diff --git a/node/impl/full/state.go b/node/impl/full/state.go index 08ac62f2e..9558219c7 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -365,6 +365,7 @@ func (a *StateAPI) StateReplay(ctx context.Context, tsk types.TipSetKey, mc cid. MsgCid: mc, Msg: m, MsgRct: &r.MessageReceipt, + GasCost: stmgr.MakeMsgGasCost(m, r), ExecutionTrace: r.ExecutionTrace, Error: errstr, Duration: r.Duration, @@ -1304,14 +1305,6 @@ func (a *StateAPI) StateMsgGasCost(ctx context.Context, inputMsg cid.Cid, tsk ty return nil, err } - return &api.MsgGasCost{ - Message: msg, - GasUsed: big.NewInt(r.GasUsed), - BaseFeeBurn: r.GasCosts.BaseFeeBurn, - OverEstimationBurn: r.GasCosts.OverEstimationBurn, - MinerPenalty: r.GasCosts.MinerPenalty, - MinerTip: r.GasCosts.MinerTip, - Refund: r.GasCosts.Refund, - TotalCost: big.Sub(m.RequiredFunds(), r.GasCosts.Refund), - }, nil + gc := stmgr.MakeMsgGasCost(m, r) + return &gc, nil } From 7826cc0babf005dfb84f5c4ab7614ab3d1fa201b Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 15 Oct 2020 19:14:53 -0400 Subject: [PATCH 2/6] Rename StateReplay to StateTransplant --- api/api_full.go | 4 ++-- api/apistruct/struct.go | 6 +++--- cli/state.go | 27 ++++++++------------------- lotuspond/front/src/Block.js | 2 +- node/impl/full/state.go | 2 +- 5 files changed, 15 insertions(+), 26 deletions(-) diff --git a/api/api_full.go b/api/api_full.go index 130cba957..9eaca53f9 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -320,8 +320,8 @@ type FullNode interface { // StateCall runs the given message and returns its result without any persisted changes. StateCall(context.Context, *types.Message, types.TipSetKey) (*InvocResult, error) - // StateReplay returns the result of executing the indicated message, assuming it was executed in the indicated tipset. - StateReplay(context.Context, types.TipSetKey, cid.Cid) (*InvocResult, error) + // StateTransplant returns the result of executing the indicated message, assuming it was executed in the indicated tipset. + StateTransplant(context.Context, types.TipSetKey, cid.Cid) (*InvocResult, error) // StateGetActor returns the indicated actor's nonce and balance. StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) // StateReadState returns the indicated actor's state. diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index 3854e1dd6..3e474f596 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -188,7 +188,7 @@ type FullNodeStruct struct { StateSectorExpiration func(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (*miner.SectorExpiration, error) `perm:"read"` StateSectorPartition func(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (*miner.SectorLocation, error) `perm:"read"` StateCall func(context.Context, *types.Message, types.TipSetKey) (*api.InvocResult, error) `perm:"read"` - StateReplay func(context.Context, types.TipSetKey, cid.Cid) (*api.InvocResult, error) `perm:"read"` + StateTransplant func(context.Context, types.TipSetKey, cid.Cid) (*api.InvocResult, error) `perm:"read"` StateGetActor func(context.Context, address.Address, types.TipSetKey) (*types.Actor, error) `perm:"read"` 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"` @@ -880,8 +880,8 @@ func (c *FullNodeStruct) StateCall(ctx context.Context, msg *types.Message, tsk return c.Internal.StateCall(ctx, msg, tsk) } -func (c *FullNodeStruct) StateReplay(ctx context.Context, tsk types.TipSetKey, mc cid.Cid) (*api.InvocResult, error) { - return c.Internal.StateReplay(ctx, tsk, mc) +func (c *FullNodeStruct) StateTransplant(ctx context.Context, tsk types.TipSetKey, mc cid.Cid) (*api.InvocResult, error) { + return c.Internal.StateTransplant(ctx, tsk, mc) } func (c *FullNodeStruct) StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) { diff --git a/cli/state.go b/cli/state.go index 1e7e24ddb..362e29636 100644 --- a/cli/state.go +++ b/cli/state.go @@ -60,7 +60,7 @@ var stateCmd = &cli.Command{ stateSectorCmd, stateGetActorCmd, stateLookupIDCmd, - stateReplaySetCmd, + stateTransplantCmd, stateSectorSizeCmd, stateReadStateCmd, stateListMessagesCmd, @@ -384,9 +384,9 @@ var stateExecTraceCmd = &cli.Command{ }, } -var stateReplaySetCmd = &cli.Command{ - Name: "replay", - Usage: "Replay a particular message within a tipset", +var stateTransplantCmd = &cli.Command{ + Name: "transplant", + Usage: "Play a particular message within a tipset", ArgsUsage: "[tipsetKey messageCid]", Action: func(cctx *cli.Context) error { if cctx.Args().Len() < 1 { @@ -437,30 +437,19 @@ var stateReplaySetCmd = &cli.Command{ return err } } else { - var r *api.MsgLookup - r, err = fapi.StateWaitMsg(ctx, mcid, build.MessageConfidence) - if err != nil { - return xerrors.Errorf("finding message in chain: %w", err) - } - - childTs, err := fapi.ChainGetTipSet(ctx, r.TipSet) - if err != nil { - return xerrors.Errorf("loading tipset: %w", err) - } - ts, err = fapi.ChainGetTipSet(ctx, childTs.Parents()) + ts, err = fapi.ChainHead(ctx) if err != nil { return err } } - } - res, err := fapi.StateReplay(ctx, ts.Key(), mcid) + res, err := fapi.StateTransplant(ctx, ts.Key(), mcid) if err != nil { - return xerrors.Errorf("replay call failed: %w", err) + return xerrors.Errorf("transplant call failed: %w", err) } - fmt.Println("Replay receipt:") + fmt.Println("Transplant receipt:") fmt.Printf("Exit code: %d\n", res.MsgRct.ExitCode) fmt.Printf("Return: %x\n", res.MsgRct.Return) fmt.Printf("Gas Used: %d\n", res.MsgRct.GasUsed) diff --git a/lotuspond/front/src/Block.js b/lotuspond/front/src/Block.js index 4bae57c81..cb8d25c9d 100644 --- a/lotuspond/front/src/Block.js +++ b/lotuspond/front/src/Block.js @@ -26,7 +26,7 @@ class Block extends React.Component { messages = await Promise.all(messages.map(async (msg, i) => { if (msg.receipt.ExitCode !== 0) { - let reply = await this.props.conn.call('Filecoin.StateReplay', [{Cids: [this.props.cid], Blocks: [header], Height: header.Height}, msg.Cid]) + let reply = await this.props.conn.call('Filecoin.StateTransplant', [{Cids: [this.props.cid], Blocks: [header], Height: header.Height}, msg.Cid]) if(!reply.Error) { reply.Error = "reply: no error" } diff --git a/node/impl/full/state.go b/node/impl/full/state.go index 9558219c7..349a4918b 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -346,7 +346,7 @@ func (a *StateAPI) StateCall(ctx context.Context, msg *types.Message, tsk types. return res, err } -func (a *StateAPI) StateReplay(ctx context.Context, tsk types.TipSetKey, mc cid.Cid) (*api.InvocResult, error) { +func (a *StateAPI) StateTransplant(ctx context.Context, tsk types.TipSetKey, mc cid.Cid) (*api.InvocResult, error) { ts, err := a.Chain.GetTipSetFromKey(tsk) if err != nil { return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err) From 99c07703f8f60799e9e2637bffc30613267d14de Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 15 Oct 2020 19:47:02 -0400 Subject: [PATCH 3/6] Add a new StateReplay CLI/API endpoint --- api/api_full.go | 4 +- api/apistruct/struct.go | 5 +++ cli/state.go | 91 +++++++++++++++++++++++++++++++++++++++++ node/impl/full/state.go | 40 ++++++++++++++++++ 4 files changed, 139 insertions(+), 1 deletion(-) diff --git a/api/api_full.go b/api/api_full.go index 9eaca53f9..29688a5e6 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -315,13 +315,15 @@ type FullNode interface { // MethodGroup: State // The State methods are used to query, inspect, and interact with chain state. - // All methods take a TipSetKey as a parameter. The state looked up is the state at that tipset. + // Most methods take a TipSetKey as a parameter. The state looked up is the state at that tipset. // A nil TipSetKey can be provided as a param, this will cause the heaviest tipset in the chain to be used. // StateCall runs the given message and returns its result without any persisted changes. StateCall(context.Context, *types.Message, types.TipSetKey) (*InvocResult, error) // StateTransplant returns the result of executing the indicated message, assuming it was executed in the indicated tipset. StateTransplant(context.Context, types.TipSetKey, cid.Cid) (*InvocResult, error) + // StateReplay searches for where the given message was executed, and replays it in that tipset. + StateReplay(context.Context, cid.Cid) (*InvocResult, error) // StateGetActor returns the indicated actor's nonce and balance. StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) // StateReadState returns the indicated actor's state. diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index 3e474f596..33670c276 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -189,6 +189,7 @@ type FullNodeStruct struct { StateSectorPartition func(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (*miner.SectorLocation, error) `perm:"read"` StateCall func(context.Context, *types.Message, types.TipSetKey) (*api.InvocResult, error) `perm:"read"` StateTransplant func(context.Context, types.TipSetKey, cid.Cid) (*api.InvocResult, error) `perm:"read"` + StateReplay func(context.Context, cid.Cid) (*api.InvocResult, error) `perm:"read"` StateGetActor func(context.Context, address.Address, types.TipSetKey) (*types.Actor, error) `perm:"read"` 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"` @@ -884,6 +885,10 @@ func (c *FullNodeStruct) StateTransplant(ctx context.Context, tsk types.TipSetKe return c.Internal.StateTransplant(ctx, tsk, mc) } +func (c *FullNodeStruct) StateReplay(ctx context.Context, mc cid.Cid) (*api.InvocResult, error) { + return c.Internal.StateReplay(ctx, mc) +} + func (c *FullNodeStruct) StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) { return c.Internal.StateGetActor(ctx, actor, tsk) } diff --git a/cli/state.go b/cli/state.go index 362e29636..bff41e268 100644 --- a/cli/state.go +++ b/cli/state.go @@ -61,6 +61,7 @@ var stateCmd = &cli.Command{ stateGetActorCmd, stateLookupIDCmd, stateTransplantCmd, + stateReplayCmd, stateSectorSizeCmd, stateReadStateCmd, stateListMessagesCmd, @@ -388,6 +389,16 @@ var stateTransplantCmd = &cli.Command{ Name: "transplant", Usage: "Play a particular message within a tipset", ArgsUsage: "[tipsetKey messageCid]", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "show-trace", + Usage: "print out full execution trace for given message", + }, + &cli.BoolFlag{ + Name: "detailed-gas", + Usage: "print out detailed gas costs for given message", + }, + }, Action: func(cctx *cli.Context) error { if cctx.Args().Len() < 1 { fmt.Println("usage: [tipset] ") @@ -453,10 +464,90 @@ var stateTransplantCmd = &cli.Command{ fmt.Printf("Exit code: %d\n", res.MsgRct.ExitCode) fmt.Printf("Return: %x\n", res.MsgRct.Return) fmt.Printf("Gas Used: %d\n", res.MsgRct.GasUsed) + + if cctx.Bool("detailed-gas") { + fmt.Printf("Base Fee Burn: %d\n", res.GasCost.BaseFeeBurn) + fmt.Printf("Overestimaton Burn: %d\n", res.GasCost.OverEstimationBurn) + fmt.Printf("Miner Penalty: %d\n", res.GasCost.MinerPenalty) + fmt.Printf("Miner Tip: %d\n", res.GasCost.MinerTip) + fmt.Printf("Refund: %d\n", res.GasCost.Refund) + } + fmt.Printf("Total Message Cost: %d\n", res.GasCost.TotalCost) + if res.MsgRct.ExitCode != 0 { fmt.Printf("Error message: %q\n", res.Error) } + if cctx.Bool("show-trace") { + fmt.Printf("%s\t%s\t%s\t%d\t%x\t%d\t%x\n", res.Msg.From, res.Msg.To, res.Msg.Value, res.Msg.Method, res.Msg.Params, res.MsgRct.ExitCode, res.MsgRct.Return) + printInternalExecutions("\t", res.ExecutionTrace.Subcalls) + } + + return nil + }, +} + +var stateReplayCmd = &cli.Command{ + Name: "replay", + Usage: "Replay a particular message", + ArgsUsage: "", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "show-trace", + Usage: "print out full execution trace for given message", + }, + &cli.BoolFlag{ + Name: "detailed-gas", + Usage: "print out detailed gas costs for given message", + }, + }, + Action: func(cctx *cli.Context) error { + if cctx.Args().Len() != 1 { + fmt.Println("must provide cid of message to replay") + return nil + } + + mcid, err := cid.Decode(cctx.Args().First()) + if err != nil { + return fmt.Errorf("message cid was invalid: %s", err) + } + + fapi, closer, err := GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer closer() + + ctx := ReqContext(cctx) + + res, err := fapi.StateReplay(ctx, mcid) + if err != nil { + return xerrors.Errorf("replay call failed: %w", err) + } + + fmt.Println("Replay receipt:") + fmt.Printf("Exit code: %d\n", res.MsgRct.ExitCode) + fmt.Printf("Return: %x\n", res.MsgRct.Return) + fmt.Printf("Gas Used: %d\n", res.MsgRct.GasUsed) + + if cctx.Bool("detailed-gas") { + fmt.Printf("Base Fee Burn: %d\n", res.GasCost.BaseFeeBurn) + fmt.Printf("Overestimaton Burn: %d\n", res.GasCost.OverEstimationBurn) + fmt.Printf("Miner Penalty: %d\n", res.GasCost.MinerPenalty) + fmt.Printf("Miner Tip: %d\n", res.GasCost.MinerTip) + fmt.Printf("Refund: %d\n", res.GasCost.Refund) + } + fmt.Printf("Total Message Cost: %d\n", res.GasCost.TotalCost) + + if res.MsgRct.ExitCode != 0 { + fmt.Printf("Error message: %q\n", res.Error) + } + + if cctx.Bool("show-trace") { + fmt.Printf("%s\t%s\t%s\t%d\t%x\t%d\t%x\n", res.Msg.From, res.Msg.To, res.Msg.Value, res.Msg.Method, res.Msg.Params, res.MsgRct.ExitCode, res.MsgRct.Return) + printInternalExecutions("\t", res.ExecutionTrace.Subcalls) + } + return nil }, } diff --git a/node/impl/full/state.go b/node/impl/full/state.go index 349a4918b..959660085 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -372,6 +372,46 @@ func (a *StateAPI) StateTransplant(ctx context.Context, tsk types.TipSetKey, mc }, nil } +func (a *StateAPI) StateReplay(ctx context.Context, mc cid.Cid) (*api.InvocResult, error) { + mlkp, err := a.StateSearchMsg(ctx, mc) + if err != nil { + return nil, xerrors.Errorf("searching for msg %s: %w", mc, err) + } + if mlkp == nil { + return nil, xerrors.Errorf("didn't find msg %s", mc) + } + + executionTs, err := a.Chain.GetTipSetFromKey(mlkp.TipSet) + if err != nil { + return nil, xerrors.Errorf("loading tipset %s: %w", mlkp.TipSet, err) + } + + ts, err := a.Chain.LoadTipSet(executionTs.Parents()) + if err != nil { + return nil, xerrors.Errorf("loading parent tipset %s: %w", mlkp.TipSet, err) + } + + m, r, err := a.StateManager.Replay(ctx, ts, mlkp.Message) + if err != nil { + return nil, err + } + + var errstr string + if r.ActorErr != nil { + errstr = r.ActorErr.Error() + } + + return &api.InvocResult{ + MsgCid: mlkp.Message, + Msg: m, + MsgRct: &r.MessageReceipt, + GasCost: stmgr.MakeMsgGasCost(m, r), + ExecutionTrace: r.ExecutionTrace, + Error: errstr, + Duration: r.Duration, + }, nil +} + func stateForTs(ctx context.Context, ts *types.TipSet, cstore *store.ChainStore, smgr *stmgr.StateManager) (*state.StateTree, error) { if ts == nil { ts = cstore.GetHeaviestTipSet() From 6f86b95f62bd59b38f1499ee381b93643247500b Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 15 Oct 2020 21:00:47 -0400 Subject: [PATCH 4/6] Remove StateMsgGasCost --- api/api_full.go | 2 - api/apistruct/struct.go | 5 - cli/state.go | 55 ----------- documentation/en/api-methods.md | 169 ++++++++++++++++++++++---------- node/impl/full/state.go | 41 -------- 5 files changed, 119 insertions(+), 153 deletions(-) diff --git a/api/api_full.go b/api/api_full.go index 29688a5e6..88bcb3cba 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -372,8 +372,6 @@ type FullNode interface { StateSectorPartition(ctx context.Context, maddr address.Address, sectorNumber abi.SectorNumber, tok types.TipSetKey) (*miner.SectorLocation, error) // StateSearchMsg searches for a message in the chain, and returns its receipt and the tipset where it was executed StateSearchMsg(context.Context, cid.Cid) (*MsgLookup, error) - // StateMsgGasCost searches for a message in the chain, and returns details of the messages gas costs, including the penalty and miner tip - StateMsgGasCost(context.Context, cid.Cid, types.TipSetKey) (*MsgGasCost, error) // 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) diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index 33670c276..0279e8c8a 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -192,7 +192,6 @@ type FullNodeStruct struct { StateReplay func(context.Context, cid.Cid) (*api.InvocResult, error) `perm:"read"` StateGetActor func(context.Context, address.Address, types.TipSetKey) (*types.Actor, error) `perm:"read"` 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"` @@ -897,10 +896,6 @@ func (c *FullNodeStruct) StateReadState(ctx context.Context, addr address.Addres return c.Internal.StateReadState(ctx, addr, tsk) } -func (c *FullNodeStruct) StateMsgGasCost(ctx context.Context, msgc cid.Cid, tsk types.TipSetKey) (*api.MsgGasCost, error) { - return c.Internal.StateMsgGasCost(ctx, msgc, tsk) -} - func (c *FullNodeStruct) StateWaitMsg(ctx context.Context, msgc cid.Cid, confidence uint64) (*api.MsgLookup, error) { return c.Internal.StateWaitMsg(ctx, msgc, confidence) } diff --git a/cli/state.go b/cli/state.go index bff41e268..38454ec27 100644 --- a/cli/state.go +++ b/cli/state.go @@ -70,7 +70,6 @@ var stateCmd = &cli.Command{ stateGetDealSetCmd, stateWaitMsgCmd, stateSearchMsgCmd, - stateMsgCostCmd, stateMinerInfo, stateMarketCmd, stateExecTraceCmd, @@ -1504,60 +1503,6 @@ var stateSearchMsgCmd = &cli.Command{ }, } -var stateMsgCostCmd = &cli.Command{ - Name: "msg-cost", - Usage: "Get the detailed gas costs of a message", - ArgsUsage: "[messageCid]", - Action: func(cctx *cli.Context) error { - if !cctx.Args().Present() { - return fmt.Errorf("must specify message cid to get gas costs for") - } - - api, closer, err := GetFullNodeAPI(cctx) - if err != nil { - return err - } - defer closer() - - ctx := ReqContext(cctx) - - msg, err := cid.Decode(cctx.Args().First()) - if err != nil { - return err - } - - tsk := types.EmptyTSK - - ts, err := LoadTipSet(ctx, cctx, api) - if err != nil { - return err - } - - if ts != nil { - tsk = ts.Key() - } - - mgc, err := api.StateMsgGasCost(ctx, msg, tsk) - if err != nil { - return err - } - - if mgc != nil { - fmt.Printf("Message CID: %s", mgc.Message) - fmt.Printf("\nGas Used: %d", mgc.GasUsed) - fmt.Printf("\nBase Fee Burn: %d", mgc.BaseFeeBurn) - fmt.Printf("\nOverestimation Burn: %d", mgc.OverEstimationBurn) - fmt.Printf("\nMiner Tip: %d", mgc.MinerTip) - fmt.Printf("\nRefund: %d", mgc.Refund) - fmt.Printf("\nTotal Cost: %d", mgc.TotalCost) - fmt.Printf("\nMiner Penalty: %d", mgc.MinerPenalty) - } else { - fmt.Print("message was not found on chain") - } - return nil - }, -} - var stateCallCmd = &cli.Command{ Name: "call", Usage: "Invoke a method on an actor locally", diff --git a/documentation/en/api-methods.md b/documentation/en/api-methods.md index a905ef2e3..9be12722d 100644 --- a/documentation/en/api-methods.md +++ b/documentation/en/api-methods.md @@ -156,7 +156,6 @@ * [StateMinerRecoveries](#StateMinerRecoveries) * [StateMinerSectorCount](#StateMinerSectorCount) * [StateMinerSectors](#StateMinerSectors) - * [StateMsgGasCost](#StateMsgGasCost) * [StateNetworkName](#StateNetworkName) * [StateNetworkVersion](#StateNetworkVersion) * [StateReadState](#StateReadState) @@ -166,6 +165,7 @@ * [StateSectorGetInfo](#StateSectorGetInfo) * [StateSectorPartition](#StateSectorPartition) * [StateSectorPreCommitInfo](#StateSectorPreCommitInfo) + * [StateTransplant](#StateTransplant) * [StateVMCirculatingSupplyInternal](#StateVMCirculatingSupplyInternal) * [StateVerifiedClientStatus](#StateVerifiedClientStatus) * [StateVerifiedRegistryRootKey](#StateVerifiedRegistryRootKey) @@ -2989,7 +2989,7 @@ Response: ## State The State methods are used to query, inspect, and interact with chain state. -All methods take a TipSetKey as a parameter. The state looked up is the state at that tipset. +Most methods take a TipSetKey as a parameter. The state looked up is the state at that tipset. A nil TipSetKey can be provided as a param, this will cause the heaviest tipset in the chain to be used. @@ -3100,6 +3100,18 @@ Response: "Return": "Ynl0ZSBhcnJheQ==", "GasUsed": 9 }, + "GasCost": { + "Message": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "GasUsed": "0", + "BaseFeeBurn": "0", + "OverEstimationBurn": "0", + "MinerPenalty": "0", + "MinerTip": "0", + "Refund": "0", + "TotalCost": "0" + }, "ExecutionTrace": { "Msg": { "Version": 42, @@ -3974,45 +3986,6 @@ Inputs: Response: `null` -### StateMsgGasCost -StateMsgGasCost searches for a message in the chain, and returns details of the messages gas costs, including the penalty and miner tip - - -Perms: read - -Inputs: -```json -[ - { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" - }, - [ - { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" - }, - { - "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" - } - ] -] -``` - -Response: -```json -{ - "Message": { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" - }, - "GasUsed": "0", - "BaseFeeBurn": "0", - "OverEstimationBurn": "0", - "MinerPenalty": "0", - "MinerTip": "0", - "Refund": "0", - "TotalCost": "0" -} -``` - ### StateNetworkName StateNetworkName returns the name of the network the node is synced to @@ -4075,7 +4048,7 @@ Response: ``` ### StateReplay -StateReplay returns the result of executing the indicated message, assuming it was executed in the indicated tipset. +StateReplay searches for where the given message was executed, and replays it in that tipset. Perms: read @@ -4083,14 +4056,6 @@ Perms: read Inputs: ```json [ - [ - { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" - }, - { - "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" - } - ], { "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" } @@ -4123,6 +4088,18 @@ Response: "Return": "Ynl0ZSBhcnJheQ==", "GasUsed": 9 }, + "GasCost": { + "Message": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "GasUsed": "0", + "BaseFeeBurn": "0", + "OverEstimationBurn": "0", + "MinerPenalty": "0", + "MinerTip": "0", + "Refund": "0", + "TotalCost": "0" + }, "ExecutionTrace": { "Msg": { "Version": 42, @@ -4342,6 +4319,98 @@ Response: } ``` +### StateTransplant +StateTransplant returns the result of executing the indicated message, assuming it was executed in the indicated tipset. + + +Perms: read + +Inputs: +```json +[ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ], + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } +] +``` + +Response: +```json +{ + "MsgCid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Msg": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "MsgRct": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "GasUsed": 9 + }, + "GasCost": { + "Message": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "GasUsed": "0", + "BaseFeeBurn": "0", + "OverEstimationBurn": "0", + "MinerPenalty": "0", + "MinerTip": "0", + "Refund": "0", + "TotalCost": "0" + }, + "ExecutionTrace": { + "Msg": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "MsgRct": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "GasUsed": 9 + }, + "Error": "string value", + "Duration": 60000000000, + "GasCharges": null, + "Subcalls": null + }, + "Error": "string value", + "Duration": 60000000000 +} +``` + ### StateVMCirculatingSupplyInternal StateVMCirculatingSupplyInternal returns an approximation of the circulating supply of Filecoin at the given tipset. This is the value reported by the runtime interface to actors code. diff --git a/node/impl/full/state.go b/node/impl/full/state.go index 959660085..a63ac8c67 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -1307,44 +1307,3 @@ func (a *StateAPI) StateNetworkVersion(ctx context.Context, tsk types.TipSetKey) return a.StateManager.GetNtwkVersion(ctx, ts.Height()), nil } - -func (a *StateAPI) StateMsgGasCost(ctx context.Context, inputMsg cid.Cid, tsk types.TipSetKey) (*api.MsgGasCost, error) { - var msg cid.Cid - var ts *types.TipSet - var err error - if tsk != types.EmptyTSK { - msg = inputMsg - ts, err = a.Chain.LoadTipSet(tsk) - if err != nil { - return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err) - } - } else { - mlkp, err := a.StateSearchMsg(ctx, inputMsg) - if err != nil { - return nil, xerrors.Errorf("searching for msg %s: %w", inputMsg, err) - } - if mlkp == nil { - return nil, xerrors.Errorf("didn't find msg %s", inputMsg) - } - - executionTs, err := a.Chain.GetTipSetFromKey(mlkp.TipSet) - if err != nil { - return nil, xerrors.Errorf("loading tipset %s: %w", mlkp.TipSet, err) - } - - ts, err = a.Chain.LoadTipSet(executionTs.Parents()) - if err != nil { - return nil, xerrors.Errorf("loading parent tipset %s: %w", mlkp.TipSet, err) - } - - msg = mlkp.Message - } - - m, r, err := a.StateManager.Replay(ctx, ts, msg) - if err != nil { - return nil, err - } - - gc := stmgr.MakeMsgGasCost(m, r) - return &gc, nil -} From 89f46cb5ccd7a19b72c7432404f658eb1d7ecfb5 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 15 Oct 2020 21:12:38 -0400 Subject: [PATCH 5/6] Make ApplyRet's GasOutputs a pointer, return nil when unused --- chain/stmgr/call.go | 1 - chain/stmgr/forks.go | 4 ++-- chain/stmgr/stmgr.go | 4 +++- chain/vm/vm.go | 16 ++++++++-------- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/chain/stmgr/call.go b/chain/stmgr/call.go index 5eaa2e16a..3d7d284bc 100644 --- a/chain/stmgr/call.go +++ b/chain/stmgr/call.go @@ -116,7 +116,6 @@ func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types. MsgCid: msg.Cid(), Msg: msg, MsgRct: &ret.MessageReceipt, - GasCost: MakeMsgGasCost(msg, ret), ExecutionTrace: ret.ExecutionTrace, Error: errs, Duration: ret.Duration, diff --git a/chain/stmgr/forks.go b/chain/stmgr/forks.go index 156c68783..488f84167 100644 --- a/chain/stmgr/forks.go +++ b/chain/stmgr/forks.go @@ -498,7 +498,7 @@ func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, cb ExecCal Subcalls: subcalls, }, Duration: 0, - GasCosts: vm.ZeroGasOutputs(), + GasCosts: nil, }); err != nil { return cid.Undef, xerrors.Errorf("recording transfers: %w", err) } @@ -799,7 +799,7 @@ func splitGenesisMultisig(ctx context.Context, cb ExecCallback, addr address.Add Subcalls: subcalls, }, Duration: 0, - GasCosts: vm.ZeroGasOutputs(), + GasCosts: nil, }); err != nil { return xerrors.Errorf("recording transfers: %w", err) } diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index d5cb1958d..be14797d9 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -203,13 +203,15 @@ func traceFunc(trace *[]*api.InvocResult) func(mcid cid.Cid, msg *types.Message, MsgCid: mcid, Msg: msg, MsgRct: &ret.MessageReceipt, - GasCost: MakeMsgGasCost(msg, ret), ExecutionTrace: ret.ExecutionTrace, Duration: ret.Duration, } if ret.ActorErr != nil { ir.Error = ret.ActorErr.Error() } + if ret.GasCosts != nil { + ir.GasCost = MakeMsgGasCost(msg, ret) + } *trace = append(*trace, ir) return nil } diff --git a/chain/vm/vm.go b/chain/vm/vm.go index a4efccb29..72ad731aa 100644 --- a/chain/vm/vm.go +++ b/chain/vm/vm.go @@ -214,7 +214,7 @@ type ApplyRet struct { ActorErr aerrors.ActorError ExecutionTrace types.ExecutionTrace Duration time.Duration - GasCosts GasOutputs + GasCosts *GasOutputs } func (vm *VM) send(ctx context.Context, msg *types.Message, parent *Runtime, @@ -361,7 +361,7 @@ func (vm *VM) ApplyImplicitMessage(ctx context.Context, msg *types.Message) (*Ap }, ActorErr: actorErr, ExecutionTrace: rt.executionTrace, - GasCosts: GasOutputs{}, + GasCosts: nil, Duration: time.Since(start), }, actorErr } @@ -397,7 +397,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet, ExitCode: exitcode.SysErrOutOfGas, GasUsed: 0, }, - GasCosts: gasOutputs, + GasCosts: &gasOutputs, Duration: time.Since(start), }, nil } @@ -417,7 +417,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet, GasUsed: 0, }, ActorErr: aerrors.Newf(exitcode.SysErrSenderInvalid, "actor not found: %s", msg.From), - GasCosts: gasOutputs, + GasCosts: &gasOutputs, Duration: time.Since(start), }, nil } @@ -434,7 +434,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet, GasUsed: 0, }, ActorErr: aerrors.Newf(exitcode.SysErrSenderInvalid, "send from not account actor: %s", fromActor.Code), - GasCosts: gasOutputs, + GasCosts: &gasOutputs, Duration: time.Since(start), }, nil } @@ -450,7 +450,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet, ActorErr: aerrors.Newf(exitcode.SysErrSenderStateInvalid, "actor nonce invalid: msg:%d != state:%d", msg.Nonce, fromActor.Nonce), - GasCosts: gasOutputs, + GasCosts: &gasOutputs, Duration: time.Since(start), }, nil } @@ -466,7 +466,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet, }, ActorErr: aerrors.Newf(exitcode.SysErrSenderStateInvalid, "actor balance less than needed: %s < %s", types.FIL(fromActor.Balance), types.FIL(gascost)), - GasCosts: gasOutputs, + GasCosts: &gasOutputs, Duration: time.Since(start), }, nil } @@ -560,7 +560,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet, }, ActorErr: actorErr, ExecutionTrace: rt.executionTrace, - GasCosts: gasOutputs, + GasCosts: &gasOutputs, Duration: time.Since(start), }, nil } From 2fd4a976984bb3b51e8b227b3175ed0c5eb61af3 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 15 Oct 2020 22:45:11 -0400 Subject: [PATCH 6/6] Remove StateTransplant, modify StateReplay --- api/api_full.go | 7 +-- api/apistruct/struct.go | 11 +--- cli/state.go | 105 +------------------------------- documentation/en/api-methods.md | 104 +++---------------------------- lotuspond/front/src/Block.js | 2 +- node/impl/full/state.go | 74 +++++++++------------- 6 files changed, 46 insertions(+), 257 deletions(-) diff --git a/api/api_full.go b/api/api_full.go index 88bcb3cba..1946ca28f 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -320,10 +320,9 @@ type FullNode interface { // StateCall runs the given message and returns its result without any persisted changes. StateCall(context.Context, *types.Message, types.TipSetKey) (*InvocResult, error) - // StateTransplant returns the result of executing the indicated message, assuming it was executed in the indicated tipset. - StateTransplant(context.Context, types.TipSetKey, cid.Cid) (*InvocResult, error) - // StateReplay searches for where the given message was executed, and replays it in that tipset. - StateReplay(context.Context, cid.Cid) (*InvocResult, error) + // StateReplay replays a given message, assuming it was included in a block in the specified tipset. + // If no tipset key is provided, the appropriate tipset is looked up. + StateReplay(context.Context, types.TipSetKey, cid.Cid) (*InvocResult, error) // StateGetActor returns the indicated actor's nonce and balance. StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) // StateReadState returns the indicated actor's state. diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index 0279e8c8a..6effcf727 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -188,8 +188,7 @@ type FullNodeStruct struct { StateSectorExpiration func(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (*miner.SectorExpiration, error) `perm:"read"` StateSectorPartition func(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (*miner.SectorLocation, error) `perm:"read"` StateCall func(context.Context, *types.Message, types.TipSetKey) (*api.InvocResult, error) `perm:"read"` - StateTransplant func(context.Context, types.TipSetKey, cid.Cid) (*api.InvocResult, error) `perm:"read"` - StateReplay func(context.Context, cid.Cid) (*api.InvocResult, error) `perm:"read"` + StateReplay func(context.Context, types.TipSetKey, cid.Cid) (*api.InvocResult, error) `perm:"read"` StateGetActor func(context.Context, address.Address, types.TipSetKey) (*types.Actor, error) `perm:"read"` StateReadState func(context.Context, address.Address, types.TipSetKey) (*api.ActorState, error) `perm:"read"` StateWaitMsg func(ctx context.Context, cid cid.Cid, confidence uint64) (*api.MsgLookup, error) `perm:"read"` @@ -880,12 +879,8 @@ func (c *FullNodeStruct) StateCall(ctx context.Context, msg *types.Message, tsk return c.Internal.StateCall(ctx, msg, tsk) } -func (c *FullNodeStruct) StateTransplant(ctx context.Context, tsk types.TipSetKey, mc cid.Cid) (*api.InvocResult, error) { - return c.Internal.StateTransplant(ctx, tsk, mc) -} - -func (c *FullNodeStruct) StateReplay(ctx context.Context, mc cid.Cid) (*api.InvocResult, error) { - return c.Internal.StateReplay(ctx, mc) +func (c *FullNodeStruct) StateReplay(ctx context.Context, tsk types.TipSetKey, mc cid.Cid) (*api.InvocResult, error) { + return c.Internal.StateReplay(ctx, tsk, mc) } func (c *FullNodeStruct) StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) { diff --git a/cli/state.go b/cli/state.go index 38454ec27..0fb3ecde6 100644 --- a/cli/state.go +++ b/cli/state.go @@ -60,7 +60,6 @@ var stateCmd = &cli.Command{ stateSectorCmd, stateGetActorCmd, stateLookupIDCmd, - stateTransplantCmd, stateReplayCmd, stateSectorSizeCmd, stateReadStateCmd, @@ -384,108 +383,6 @@ var stateExecTraceCmd = &cli.Command{ }, } -var stateTransplantCmd = &cli.Command{ - Name: "transplant", - Usage: "Play a particular message within a tipset", - ArgsUsage: "[tipsetKey messageCid]", - Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "show-trace", - Usage: "print out full execution trace for given message", - }, - &cli.BoolFlag{ - Name: "detailed-gas", - Usage: "print out detailed gas costs for given message", - }, - }, - Action: func(cctx *cli.Context) error { - if cctx.Args().Len() < 1 { - fmt.Println("usage: [tipset] ") - fmt.Println("The last cid passed will be used as the message CID") - fmt.Println("All preceding ones will be used as the tipset") - return nil - } - - args := cctx.Args().Slice() - mcid, err := cid.Decode(args[len(args)-1]) - if err != nil { - return fmt.Errorf("message cid was invalid: %s", err) - } - - fapi, closer, err := GetFullNodeAPI(cctx) - if err != nil { - return err - } - defer closer() - - ctx := ReqContext(cctx) - - var ts *types.TipSet - { - var tscids []cid.Cid - for _, s := range args[:len(args)-1] { - c, err := cid.Decode(s) - if err != nil { - return fmt.Errorf("tipset cid was invalid: %s", err) - } - tscids = append(tscids, c) - } - - if len(tscids) > 0 { - var headers []*types.BlockHeader - for _, c := range tscids { - h, err := fapi.ChainGetBlock(ctx, c) - if err != nil { - return err - } - - headers = append(headers, h) - } - - ts, err = types.NewTipSet(headers) - if err != nil { - return err - } - } else { - ts, err = fapi.ChainHead(ctx) - if err != nil { - return err - } - } - } - - res, err := fapi.StateTransplant(ctx, ts.Key(), mcid) - if err != nil { - return xerrors.Errorf("transplant call failed: %w", err) - } - - fmt.Println("Transplant receipt:") - fmt.Printf("Exit code: %d\n", res.MsgRct.ExitCode) - fmt.Printf("Return: %x\n", res.MsgRct.Return) - fmt.Printf("Gas Used: %d\n", res.MsgRct.GasUsed) - - if cctx.Bool("detailed-gas") { - fmt.Printf("Base Fee Burn: %d\n", res.GasCost.BaseFeeBurn) - fmt.Printf("Overestimaton Burn: %d\n", res.GasCost.OverEstimationBurn) - fmt.Printf("Miner Penalty: %d\n", res.GasCost.MinerPenalty) - fmt.Printf("Miner Tip: %d\n", res.GasCost.MinerTip) - fmt.Printf("Refund: %d\n", res.GasCost.Refund) - } - fmt.Printf("Total Message Cost: %d\n", res.GasCost.TotalCost) - - if res.MsgRct.ExitCode != 0 { - fmt.Printf("Error message: %q\n", res.Error) - } - - if cctx.Bool("show-trace") { - fmt.Printf("%s\t%s\t%s\t%d\t%x\t%d\t%x\n", res.Msg.From, res.Msg.To, res.Msg.Value, res.Msg.Method, res.Msg.Params, res.MsgRct.ExitCode, res.MsgRct.Return) - printInternalExecutions("\t", res.ExecutionTrace.Subcalls) - } - - return nil - }, -} - var stateReplayCmd = &cli.Command{ Name: "replay", Usage: "Replay a particular message", @@ -519,7 +416,7 @@ var stateReplayCmd = &cli.Command{ ctx := ReqContext(cctx) - res, err := fapi.StateReplay(ctx, mcid) + res, err := fapi.StateReplay(ctx, types.EmptyTSK, mcid) if err != nil { return xerrors.Errorf("replay call failed: %w", err) } diff --git a/documentation/en/api-methods.md b/documentation/en/api-methods.md index 9be12722d..9c2a82cad 100644 --- a/documentation/en/api-methods.md +++ b/documentation/en/api-methods.md @@ -165,7 +165,6 @@ * [StateSectorGetInfo](#StateSectorGetInfo) * [StateSectorPartition](#StateSectorPartition) * [StateSectorPreCommitInfo](#StateSectorPreCommitInfo) - * [StateTransplant](#StateTransplant) * [StateVMCirculatingSupplyInternal](#StateVMCirculatingSupplyInternal) * [StateVerifiedClientStatus](#StateVerifiedClientStatus) * [StateVerifiedRegistryRootKey](#StateVerifiedRegistryRootKey) @@ -4048,7 +4047,8 @@ Response: ``` ### StateReplay -StateReplay searches for where the given message was executed, and replays it in that tipset. +StateReplay replays a given message, assuming it was included in a block in the specified tipset. +If no tipset key is provided, the appropriate tipset is looked up. Perms: read @@ -4056,6 +4056,14 @@ Perms: read Inputs: ```json [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ], { "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" } @@ -4319,98 +4327,6 @@ Response: } ``` -### StateTransplant -StateTransplant returns the result of executing the indicated message, assuming it was executed in the indicated tipset. - - -Perms: read - -Inputs: -```json -[ - [ - { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" - }, - { - "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" - } - ], - { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" - } -] -``` - -Response: -```json -{ - "MsgCid": { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" - }, - "Msg": { - "Version": 42, - "To": "f01234", - "From": "f01234", - "Nonce": 42, - "Value": "0", - "GasLimit": 9, - "GasFeeCap": "0", - "GasPremium": "0", - "Method": 1, - "Params": "Ynl0ZSBhcnJheQ==", - "CID": { - "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" - } - }, - "MsgRct": { - "ExitCode": 0, - "Return": "Ynl0ZSBhcnJheQ==", - "GasUsed": 9 - }, - "GasCost": { - "Message": { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" - }, - "GasUsed": "0", - "BaseFeeBurn": "0", - "OverEstimationBurn": "0", - "MinerPenalty": "0", - "MinerTip": "0", - "Refund": "0", - "TotalCost": "0" - }, - "ExecutionTrace": { - "Msg": { - "Version": 42, - "To": "f01234", - "From": "f01234", - "Nonce": 42, - "Value": "0", - "GasLimit": 9, - "GasFeeCap": "0", - "GasPremium": "0", - "Method": 1, - "Params": "Ynl0ZSBhcnJheQ==", - "CID": { - "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" - } - }, - "MsgRct": { - "ExitCode": 0, - "Return": "Ynl0ZSBhcnJheQ==", - "GasUsed": 9 - }, - "Error": "string value", - "Duration": 60000000000, - "GasCharges": null, - "Subcalls": null - }, - "Error": "string value", - "Duration": 60000000000 -} -``` - ### StateVMCirculatingSupplyInternal StateVMCirculatingSupplyInternal returns an approximation of the circulating supply of Filecoin at the given tipset. This is the value reported by the runtime interface to actors code. diff --git a/lotuspond/front/src/Block.js b/lotuspond/front/src/Block.js index cb8d25c9d..4bae57c81 100644 --- a/lotuspond/front/src/Block.js +++ b/lotuspond/front/src/Block.js @@ -26,7 +26,7 @@ class Block extends React.Component { messages = await Promise.all(messages.map(async (msg, i) => { if (msg.receipt.ExitCode !== 0) { - let reply = await this.props.conn.call('Filecoin.StateTransplant', [{Cids: [this.props.cid], Blocks: [header], Height: header.Height}, msg.Cid]) + let reply = await this.props.conn.call('Filecoin.StateReplay', [{Cids: [this.props.cid], Blocks: [header], Height: header.Height}, msg.Cid]) if(!reply.Error) { reply.Error = "reply: no error" } diff --git a/node/impl/full/state.go b/node/impl/full/state.go index a63ac8c67..221b83e3a 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -346,12 +346,34 @@ func (a *StateAPI) StateCall(ctx context.Context, msg *types.Message, tsk types. return res, err } -func (a *StateAPI) StateTransplant(ctx context.Context, tsk types.TipSetKey, mc cid.Cid) (*api.InvocResult, error) { - ts, err := a.Chain.GetTipSetFromKey(tsk) - if err != nil { - return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err) +func (a *StateAPI) StateReplay(ctx context.Context, tsk types.TipSetKey, mc cid.Cid) (*api.InvocResult, error) { + msgToReplay := mc + var ts *types.TipSet + if tsk == types.EmptyTSK { + mlkp, err := a.StateSearchMsg(ctx, mc) + if err != nil { + return nil, xerrors.Errorf("searching for msg %s: %w", mc, err) + } + if mlkp == nil { + return nil, xerrors.Errorf("didn't find msg %s", mc) + } + + msgToReplay = mlkp.Message + + executionTs, err := a.Chain.GetTipSetFromKey(mlkp.TipSet) + if err != nil { + return nil, xerrors.Errorf("loading tipset %s: %w", mlkp.TipSet, err) + } + + ts, err = a.Chain.LoadTipSet(executionTs.Parents()) + if err != nil { + return nil, xerrors.Errorf("loading parent tipset %s: %w", mlkp.TipSet, err) + } + } else { + ts = a.Chain.GetHeaviestTipSet() } - m, r, err := a.StateManager.Replay(ctx, ts, mc) + + m, r, err := a.StateManager.Replay(ctx, ts, msgToReplay) if err != nil { return nil, err } @@ -362,47 +384,7 @@ func (a *StateAPI) StateTransplant(ctx context.Context, tsk types.TipSetKey, mc } return &api.InvocResult{ - MsgCid: mc, - Msg: m, - MsgRct: &r.MessageReceipt, - GasCost: stmgr.MakeMsgGasCost(m, r), - ExecutionTrace: r.ExecutionTrace, - Error: errstr, - Duration: r.Duration, - }, nil -} - -func (a *StateAPI) StateReplay(ctx context.Context, mc cid.Cid) (*api.InvocResult, error) { - mlkp, err := a.StateSearchMsg(ctx, mc) - if err != nil { - return nil, xerrors.Errorf("searching for msg %s: %w", mc, err) - } - if mlkp == nil { - return nil, xerrors.Errorf("didn't find msg %s", mc) - } - - executionTs, err := a.Chain.GetTipSetFromKey(mlkp.TipSet) - if err != nil { - return nil, xerrors.Errorf("loading tipset %s: %w", mlkp.TipSet, err) - } - - ts, err := a.Chain.LoadTipSet(executionTs.Parents()) - if err != nil { - return nil, xerrors.Errorf("loading parent tipset %s: %w", mlkp.TipSet, err) - } - - m, r, err := a.StateManager.Replay(ctx, ts, mlkp.Message) - if err != nil { - return nil, err - } - - var errstr string - if r.ActorErr != nil { - errstr = r.ActorErr.Error() - } - - return &api.InvocResult{ - MsgCid: mlkp.Message, + MsgCid: msgToReplay, Msg: m, MsgRct: &r.MessageReceipt, GasCost: stmgr.MakeMsgGasCost(m, r),