Merge pull request #3800 from filecoin-project/asr/gascost

Add an API to get detailed gas costs for a message
This commit is contained in:
Łukasz Magiera 2020-09-15 19:13:20 +02:00 committed by GitHub
commit d4a7732d0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 186 additions and 19 deletions

View File

@ -354,6 +354,8 @@ type FullNode interface {
StateSectorPartition(ctx context.Context, maddr address.Address, sectorNumber abi.SectorNumber, tok types.TipSetKey) (*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)
@ -531,6 +533,17 @@ type MsgLookup struct {
Height abi.ChainEpoch
}
type MsgGasCost struct {
Message cid.Cid // Can be different than requested, in case it was replaced, but only gas values changed
GasUsed abi.TokenAmount
BaseFeeBurn abi.TokenAmount
OverEstimationBurn abi.TokenAmount
MinerPenalty abi.TokenAmount
MinerTip abi.TokenAmount
Refund abi.TokenAmount
TotalCost abi.TokenAmount
}
type BlockMessages struct {
BlsMessages []*types.Message
SecpkMessages []*types.SignedMessage

View File

@ -183,6 +183,7 @@ 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"`
StateSearchMsg func(context.Context, cid.Cid) (*api.MsgLookup, error) `perm:"read"`
StateListMiners func(context.Context, types.TipSetKey) ([]address.Address, error) `perm:"read"`
@ -816,6 +817,10 @@ 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)
}

View File

@ -29,7 +29,7 @@ func init() {
BuildType |= Build2k
}
const BlockDelaySecs = uint64(30)
const BlockDelaySecs = uint64(4)
const PropagationDelaySecs = uint64(1)

View File

@ -233,8 +233,8 @@ func (sm *StateManager) ApplyBlocks(ctx context.Context, parentEpoch abi.ChainEp
}
receipts = append(receipts, &r.MessageReceipt)
gasReward = big.Add(gasReward, r.MinerTip)
penalty = big.Add(penalty, r.Penalty)
gasReward = big.Add(gasReward, r.GasCosts.MinerTip)
penalty = big.Add(penalty, r.GasCosts.MinerPenalty)
if cb != nil {
if err := cb(cm.Cid(), m, r); err != nil {

View File

@ -201,10 +201,9 @@ type Rand interface {
type ApplyRet struct {
types.MessageReceipt
ActorErr aerrors.ActorError
Penalty types.BigInt
MinerTip types.BigInt
ExecutionTrace types.ExecutionTrace
Duration time.Duration
GasCosts GasOutputs
}
func (vm *VM) send(ctx context.Context, msg *types.Message, parent *Runtime,
@ -328,8 +327,7 @@ func (vm *VM) ApplyImplicitMessage(ctx context.Context, msg *types.Message) (*Ap
},
ActorErr: actorErr,
ExecutionTrace: rt.executionTrace,
Penalty: types.NewInt(0),
MinerTip: types.NewInt(0),
GasCosts: GasOutputs{},
Duration: time.Since(start),
}, actorErr
}
@ -362,9 +360,10 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
ExitCode: exitcode.SysErrOutOfGas,
GasUsed: 0,
},
Penalty: types.BigMul(vm.baseFee, abi.NewTokenAmount(msgGasCost)),
GasCosts: GasOutputs{
MinerPenalty: types.BigMul(vm.baseFee, abi.NewTokenAmount(msgGasCost)),
},
Duration: time.Since(start),
MinerTip: big.Zero(),
}, nil
}
@ -381,9 +380,10 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
GasUsed: 0,
},
ActorErr: aerrors.Newf(exitcode.SysErrSenderInvalid, "actor not found: %s", msg.From),
Penalty: minerPenaltyAmount,
GasCosts: GasOutputs{
MinerPenalty: minerPenaltyAmount,
},
Duration: time.Since(start),
MinerTip: big.Zero(),
}, nil
}
return nil, xerrors.Errorf("failed to look up from actor: %w", err)
@ -397,9 +397,10 @@ 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),
Penalty: minerPenaltyAmount,
GasCosts: GasOutputs{
MinerPenalty: minerPenaltyAmount,
},
Duration: time.Since(start),
MinerTip: big.Zero(),
}, nil
}
@ -411,9 +412,11 @@ 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),
Penalty: minerPenaltyAmount,
GasCosts: GasOutputs{
MinerPenalty: minerPenaltyAmount,
},
Duration: time.Since(start),
MinerTip: big.Zero(),
}, nil
}
@ -426,9 +429,10 @@ 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)),
Penalty: minerPenaltyAmount,
GasCosts: GasOutputs{
MinerPenalty: minerPenaltyAmount,
},
Duration: time.Since(start),
MinerTip: big.Zero(),
}, nil
}
@ -521,8 +525,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
},
ActorErr: actorErr,
ExecutionTrace: rt.executionTrace,
Penalty: gasOutputs.MinerPenalty,
MinerTip: gasOutputs.MinerTip,
GasCosts: gasOutputs,
Duration: time.Since(start),
}, nil
}

View File

@ -66,6 +66,7 @@ var stateCmd = &cli.Command{
stateGetDealSetCmd,
stateWaitMsgCmd,
stateSearchMsgCmd,
stateMsgCostCmd,
stateMinerInfo,
stateMarketCmd,
},
@ -1312,6 +1313,60 @@ 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",

View File

@ -148,6 +148,7 @@
* [StateMinerRecoveries](#StateMinerRecoveries)
* [StateMinerSectorCount](#StateMinerSectorCount)
* [StateMinerSectors](#StateMinerSectors)
* [StateMsgGasCost](#StateMsgGasCost)
* [StateNetworkName](#StateNetworkName)
* [StateReadState](#StateReadState)
* [StateReplay](#StateReplay)
@ -3737,6 +3738,45 @@ 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

View File

@ -67,6 +67,8 @@ func (m *Sealing) tryUpgradeSector(ctx context.Context, params *miner.SectorPreC
params.ReplaceSectorDeadline = loc.Deadline
params.ReplaceSectorPartition = loc.Partition
log.Infof("replacing sector %d with %d", *replace, params.SectorNumber)
ri, err := m.api.StateSectorGetInfo(ctx, m.maddr, *replace, nil)
if err != nil {
log.Errorf("error calling StateSectorGetInfo for replaced sector: %+v", err)

View File

@ -1225,3 +1225,52 @@ func (a *StateAPI) StateCirculatingSupply(ctx context.Context, tsk types.TipSetK
return a.StateManager.GetCirculatingSupplyDetailed(ctx, ts.Height(), sTree)
}
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
}
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
}