From 4260cc38c91b2f62aed2c9b069f0755c31dde28e Mon Sep 17 00:00:00 2001 From: whyrusleeping Date: Sat, 7 Mar 2020 18:31:36 -0800 Subject: [PATCH] implement compute state trace output --- api/api_full.go | 7 +++++- api/apistruct/struct.go | 4 ++-- chain/stmgr/stmgr.go | 46 ++++++++++++++++++++++++++++++------- chain/stmgr/utils.go | 19 +++++++++------ chain/validation/applier.go | 4 ++++ cli/state.go | 19 ++++++++++++--- node/impl/full/state.go | 14 ++++++++--- 7 files changed, 89 insertions(+), 24 deletions(-) diff --git a/api/api_full.go b/api/api_full.go index 67a73abab..cb65246c2 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -138,7 +138,7 @@ type FullNode interface { StateGetReceipt(context.Context, cid.Cid, types.TipSetKey) (*types.MessageReceipt, error) StateMinerSectorCount(context.Context, address.Address, types.TipSetKey) (MinerSectors, error) StateListRewards(context.Context, address.Address, types.TipSetKey) ([]reward.Reward, error) - StateCompute(context.Context, abi.ChainEpoch, []*types.Message, types.TipSetKey) (cid.Cid, error) + StateCompute(context.Context, abi.ChainEpoch, []*types.Message, types.TipSetKey) (*ComputeStateOutput, error) MsigGetAvailableBalance(context.Context, address.Address, types.TipSetKey) (types.BigInt, error) @@ -362,6 +362,11 @@ type MpoolUpdate struct { Message *types.SignedMessage } +type ComputeStateOutput struct { + Root cid.Cid + Trace []*InvocResult +} + func ProofTypeFromSectorSize(ssize abi.SectorSize) (abi.RegisteredProof, abi.RegisteredProof, error) { switch ssize { case 2 << 10: diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index 758c19c6d..c3e32ddcc 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -131,7 +131,7 @@ type FullNodeStruct struct { StateMinerSectorCount func(context.Context, address.Address, types.TipSetKey) (api.MinerSectors, error) `perm:"read"` StateListMessages func(ctx context.Context, match *types.Message, tsk types.TipSetKey, toht abi.ChainEpoch) ([]cid.Cid, error) `perm:"read"` StateListRewards func(context.Context, address.Address, types.TipSetKey) ([]reward.Reward, error) `perm:"read"` - StateCompute func(context.Context, abi.ChainEpoch, []*types.Message, types.TipSetKey) (cid.Cid, error) `perm:"read"` + StateCompute func(context.Context, abi.ChainEpoch, []*types.Message, types.TipSetKey) (*api.ComputeStateOutput, error) `perm:"read"` MsigGetAvailableBalance func(context.Context, address.Address, types.TipSetKey) (types.BigInt, error) `perm:"read"` @@ -537,7 +537,7 @@ func (c *FullNodeStruct) StateListRewards(ctx context.Context, miner address.Add return c.Internal.StateListRewards(ctx, miner, tsk) } -func (c *FullNodeStruct) StateCompute(ctx context.Context, height abi.ChainEpoch, msgs []*types.Message, tsk types.TipSetKey) (cid.Cid, error) { +func (c *FullNodeStruct) StateCompute(ctx context.Context, height abi.ChainEpoch, msgs []*types.Message, tsk types.TipSetKey) (*api.ComputeStateOutput, error) { return c.Internal.StateCompute(ctx, height, msgs, tsk) } diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index 16e0aa9b3..472bc3e92 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -117,6 +117,27 @@ func (sm *StateManager) TipSetState(ctx context.Context, ts *types.TipSet) (st c return st, rec, nil } +func (sm *StateManager) ExecutionTrace(ctx context.Context, ts *types.TipSet) (cid.Cid, []*api.InvocResult, error) { + var trace []*api.InvocResult + st, _, err := sm.computeTipSetState(ctx, ts.Blocks(), func(mcid cid.Cid, msg *types.Message, ret *vm.ApplyRet) error { + ir := &api.InvocResult{ + Msg: msg, + MsgRct: &ret.MessageReceipt, + InternalExecutions: ret.InternalExecutions, + } + if ret.ActorErr != nil { + ir.Error = ret.ActorErr.Error() + } + trace = append(trace, ir) + return nil + }) + if err != nil { + return cid.Undef, nil, err + } + + return st, trace, nil +} + type BlockMessages struct { Miner address.Address BlsMessages []store.ChainMsg @@ -131,10 +152,6 @@ func (sm *StateManager) ApplyBlocks(ctx context.Context, pstate cid.Cid, bms []B return cid.Undef, cid.Undef, xerrors.Errorf("instantiating VM failed: %w", err) } - /* - } - */ - applied := make(map[address.Address]uint64) balances := make(map[address.Address]types.BigInt) @@ -203,7 +220,7 @@ func (sm *StateManager) ApplyBlocks(ctx context.Context, pstate cid.Cid, bms []B return cid.Undef, cid.Undef, xerrors.Errorf("failed to get system actor: %w", err) } - ret, err := vmi.ApplyMessage(ctx, &types.Message{ + rwMsg := &types.Message{ From: builtin.SystemActorAddr, To: builtin.RewardActorAddr, Nonce: sysAct.Nonce, @@ -212,10 +229,17 @@ func (sm *StateManager) ApplyBlocks(ctx context.Context, pstate cid.Cid, bms []B GasLimit: types.NewInt(1 << 30), Method: builtin.MethodsReward.AwardBlockReward, Params: params, - }) + } + ret, err := vmi.ApplyMessage(ctx, rwMsg) if err != nil { return cid.Undef, cid.Undef, xerrors.Errorf("failed to apply reward message for miner %s: %w", b.Miner, err) } + if cb != nil { + if err := cb(rwMsg.Cid(), rwMsg, ret); err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("callback failed on reward message: %w", err) + } + } + if ret.ExitCode != 0 { return cid.Undef, cid.Undef, xerrors.Errorf("reward application message failed (exit %d): %s", ret.ExitCode, ret.ActorErr) } @@ -228,7 +252,7 @@ func (sm *StateManager) ApplyBlocks(ctx context.Context, pstate cid.Cid, bms []B return cid.Undef, cid.Undef, err } - ret, err := vmi.ApplyMessage(ctx, &types.Message{ + cronMsg := &types.Message{ To: builtin.CronActorAddr, From: builtin.SystemActorAddr, Nonce: ca.Nonce, @@ -237,10 +261,16 @@ func (sm *StateManager) ApplyBlocks(ctx context.Context, pstate cid.Cid, bms []B GasLimit: types.NewInt(1 << 30), // Make super sure this is never too little Method: builtin.MethodsCron.EpochTick, Params: nil, - }) + } + ret, err := vmi.ApplyMessage(ctx, cronMsg) if err != nil { return cid.Undef, cid.Undef, err } + if cb != nil { + if err := cb(cronMsg.Cid(), cronMsg, ret); err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("callback failed on cron message: %w", err) + } + } if ret.ExitCode != 0 { return cid.Undef, cid.Undef, xerrors.Errorf("CheckProofSubmissions exit was non-zero: %d", ret.ExitCode) } diff --git a/chain/stmgr/utils.go b/chain/stmgr/utils.go index 511a35083..d19a8ef60 100644 --- a/chain/stmgr/utils.go +++ b/chain/stmgr/utils.go @@ -308,36 +308,41 @@ func LoadSectorsFromSet(ctx context.Context, bs blockstore.Blockstore, ssc cid.C return sset, nil } -func ComputeState(ctx context.Context, sm *StateManager, height abi.ChainEpoch, msgs []*types.Message, ts *types.TipSet) (cid.Cid, error) { +func ComputeState(ctx context.Context, sm *StateManager, height abi.ChainEpoch, msgs []*types.Message, ts *types.TipSet) (cid.Cid, []*api.InvocResult, error) { if ts == nil { ts = sm.cs.GetHeaviestTipSet() } - base, _, err := sm.TipSetState(ctx, ts) + base, trace, err := sm.ExecutionTrace(ctx, ts) if err != nil { - return cid.Undef, err + return cid.Undef, nil, err } fstate, err := sm.handleStateForks(ctx, base, height, ts.Height()) if err != nil { - return cid.Undef, err + return cid.Undef, nil, err } r := store.NewChainRand(sm.cs, ts.Cids(), height) vmi, err := vm.NewVM(fstate, height, r, builtin.SystemActorAddr, sm.cs.Blockstore(), sm.cs.VMSys()) if err != nil { - return cid.Undef, err + return cid.Undef, nil, err } for i, msg := range msgs { ret, err := vmi.ApplyMessage(ctx, msg) if err != nil { - return cid.Undef, xerrors.Errorf("applying message %s: %w", msg.Cid(), err) + return cid.Undef, nil, xerrors.Errorf("applying message %s: %w", msg.Cid(), err) } if ret.ExitCode != 0 { log.Infof("compute state apply message %d failed (exit: %d): %s", i, ret.ExitCode, ret.ActorErr) } } - return vmi.Flush(ctx) + root, err := vmi.Flush(ctx) + if err != nil { + return cid.Undef, nil, err + } + + return root, trace, nil } diff --git a/chain/validation/applier.go b/chain/validation/applier.go index 31fae2dbf..8b23392f5 100644 --- a/chain/validation/applier.go +++ b/chain/validation/applier.go @@ -4,6 +4,7 @@ import ( "context" "github.com/filecoin-project/specs-actors/actors/abi" + "github.com/filecoin-project/specs-actors/actors/builtin" "github.com/filecoin-project/specs-actors/actors/crypto" "github.com/filecoin-project/specs-actors/actors/runtime/exitcode" "github.com/ipfs/go-cid" @@ -83,6 +84,9 @@ func (a *Applier) ApplyTipSetMessages(state vstate.VMWrapper, blocks []vtypes.Bl var receipts []vtypes.MessageReceipt sroot, _, err := sm.ApplyBlocks(context.TODO(), state.Root(), bms, epoch, &randWrapper{rnd}, func(c cid.Cid, msg *types.Message, ret *vm.ApplyRet) error { + if msg.From == builtin.SystemActorAddr { + return nil // ignore reward and cron calls + } receipts = append(receipts, vtypes.MessageReceipt{ ExitCode: exitcode.ExitCode(ret.ExitCode), ReturnValue: ret.Return, diff --git a/cli/state.go b/cli/state.go index 67e2e0f94..ec7f08921 100644 --- a/cli/state.go +++ b/cli/state.go @@ -5,11 +5,12 @@ import ( "context" "encoding/json" "fmt" - "github.com/filecoin-project/specs-actors/actors/builtin" "reflect" "strconv" "strings" + "github.com/filecoin-project/specs-actors/actors/builtin" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/specs-actors/actors/abi" "github.com/filecoin-project/specs-actors/actors/builtin/market" @@ -741,6 +742,10 @@ var stateComputeStateCmd = &cli.Command{ Name: "apply-mpool-messages", Usage: "apply messages from the mempool to the computed state", }, + &cli.BoolFlag{ + Name: "show-trace", + Usage: "print out full execution trace for given tipset", + }, }, Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) @@ -785,12 +790,20 @@ var stateComputeStateCmd = &cli.Command{ } } - nstate, err := api.StateCompute(ctx, h, msgs, ts.Key()) + stout, err := api.StateCompute(ctx, h, msgs, ts.Key()) if err != nil { return err } - fmt.Println("computed state cid: ", nstate) + fmt.Println("computed state cid: ", stout.Root) + if cctx.Bool("show-trace") { + for _, ir := range stout.Trace { + fmt.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) + for _, im := range ir.InternalExecutions { + fmt.Printf("\t%s\t%s\t%s\t%d\t%x\t%d\t%x\n", im.Msg.From, im.Msg.To, im.Msg.Value, im.Msg.Method, im.Msg.Params, im.MsgRct.ExitCode, im.MsgRct.Return) + } + } + } return nil }, } diff --git a/node/impl/full/state.go b/node/impl/full/state.go index 61b962821..7c3c2c8d5 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -541,12 +541,20 @@ func (a *StateAPI) StateListMessages(ctx context.Context, match *types.Message, return out, nil } -func (a *StateAPI) StateCompute(ctx context.Context, height abi.ChainEpoch, msgs []*types.Message, tsk types.TipSetKey) (cid.Cid, error) { +func (a *StateAPI) StateCompute(ctx context.Context, height abi.ChainEpoch, msgs []*types.Message, tsk types.TipSetKey) (*api.ComputeStateOutput, error) { ts, err := a.Chain.GetTipSetFromKey(tsk) if err != nil { - return cid.Undef, xerrors.Errorf("loading tipset %s: %w", tsk, err) + return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err) } - return stmgr.ComputeState(ctx, a.StateManager, height, msgs, ts) + st, t, err := stmgr.ComputeState(ctx, a.StateManager, height, msgs, ts) + if err != nil { + return nil, err + } + + return &api.ComputeStateOutput{ + Root: st, + Trace: t, + }, nil } func (a *StateAPI) MsigGetAvailableBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (types.BigInt, error) {