Skip to content

Commit

Permalink
Merge pull request #4429 from filecoin-project/asr/state-apis
Browse files Browse the repository at this point in the history
Improve StateMsg APIs
  • Loading branch information
arajasek authored Oct 16, 2020
2 parents 17fcf30 + 2fd4a97 commit 0775c6c
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 226 deletions.
8 changes: 4 additions & 4 deletions api/api_full.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,12 +315,13 @@ 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)
// StateReplay returns the result of executing the indicated message, assuming it was executed in the indicated 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.
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)
Expand Down Expand Up @@ -370,8 +371,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)
Expand Down Expand Up @@ -740,6 +739,7 @@ type InvocResult struct {
MsgCid cid.Cid
Msg *types.Message
MsgRct *types.MessageReceipt
GasCost MsgGasCost
ExecutionTrace types.ExecutionTrace
Error string
Duration time.Duration
Expand Down
5 changes: 0 additions & 5 deletions api/apistruct/struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,6 @@ type FullNodeStruct struct {
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"`
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"`
Expand Down Expand Up @@ -892,10 +891,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)
}
Expand Down
1 change: 1 addition & 0 deletions chain/stmgr/call.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,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,
Expand Down
4 changes: 2 additions & 2 deletions chain/stmgr/forks.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
Expand Down
3 changes: 3 additions & 0 deletions chain/stmgr/stmgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,9 @@ func traceFunc(trace *[]*api.InvocResult) func(mcid cid.Cid, msg *types.Message,
if ret.ActorErr != nil {
ir.Error = ret.ActorErr.Error()
}
if ret.GasCosts != nil {
ir.GasCost = MakeMsgGasCost(msg, ret)
}
*trace = append(*trace, ir)
return nil
}
Expand Down
13 changes: 13 additions & 0 deletions chain/stmgr/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
}
16 changes: 8 additions & 8 deletions chain/vm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
Expand All @@ -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
}
Expand All @@ -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
}
Expand All @@ -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
}
Expand All @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down
144 changes: 33 additions & 111 deletions cli/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ var stateCmd = &cli.Command{
stateSectorCmd,
stateGetActorCmd,
stateLookupIDCmd,
stateReplaySetCmd,
stateReplayCmd,
stateSectorSizeCmd,
stateReadStateCmd,
stateListMessagesCmd,
Expand All @@ -69,7 +69,6 @@ var stateCmd = &cli.Command{
stateGetDealSetCmd,
stateWaitMsgCmd,
stateSearchMsgCmd,
stateMsgCostCmd,
stateMinerInfo,
stateMarketCmd,
stateExecTraceCmd,
Expand Down Expand Up @@ -384,20 +383,27 @@ var stateExecTraceCmd = &cli.Command{
},
}

var stateReplaySetCmd = &cli.Command{
var stateReplayCmd = &cli.Command{
Name: "replay",
Usage: "Replay a particular message within a tipset",
ArgsUsage: "[tipsetKey messageCid]",
Usage: "Replay a particular message",
ArgsUsage: "<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] <message cid>")
fmt.Println("The last cid passed will be used as the message CID")
fmt.Println("All preceding ones will be used as the tipset")
if cctx.Args().Len() != 1 {
fmt.Println("must provide cid of message to replay")
return nil
}

args := cctx.Args().Slice()
mcid, err := cid.Decode(args[len(args)-1])
mcid, err := cid.Decode(cctx.Args().First())
if err != nil {
return fmt.Errorf("message cid was invalid: %s", err)
}
Expand All @@ -410,52 +416,7 @@ var stateReplaySetCmd = &cli.Command{

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 {
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())
if err != nil {
return err
}
}

}

res, err := fapi.StateReplay(ctx, ts.Key(), mcid)
res, err := fapi.StateReplay(ctx, types.EmptyTSK, mcid)
if err != nil {
return xerrors.Errorf("replay call failed: %w", err)
}
Expand All @@ -464,10 +425,25 @@ var stateReplaySetCmd = &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
},
}
Expand Down Expand Up @@ -1424,60 +1400,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",
Expand Down
Loading

0 comments on commit 0775c6c

Please sign in to comment.