diff --git a/chain/sync.go b/chain/sync.go
index 03ccc3ab7..f4b32f2bc 100644
--- a/chain/sync.go
+++ b/chain/sync.go
@@ -857,7 +857,7 @@ func (syncer *Syncer) checkBlockMessages(ctx context.Context, b *types.FullBlock
// Phase 1: syntactic validation, as defined in the spec
minGas := vm.PricelistByEpoch(baseTs.Height()).OnChainMessage(msg.ChainLength())
- if err := m.ValidForBlockInclusion(minGas); err != nil {
+ if err := m.ValidForBlockInclusion(minGas.Total()); err != nil {
return err
}
diff --git a/chain/types/execresult.go b/chain/types/execresult.go
index dd5c8fa3e..41372b832 100644
--- a/chain/types/execresult.go
+++ b/chain/types/execresult.go
@@ -1,12 +1,57 @@
package types
-import "time"
+import (
+ "encoding/json"
+ "fmt"
+ "runtime"
+ "strings"
+ "time"
+)
type ExecutionTrace struct {
- Msg *Message
- MsgRct *MessageReceipt
- Error string
- Duration time.Duration
+ Msg *Message
+ MsgRct *MessageReceipt
+ Error string
+ Duration time.Duration
+ GasCharges []*GasTrace
Subcalls []ExecutionTrace
}
+
+type GasTrace struct {
+ Name string
+ Location string
+ TotalGas int64
+ ComputeGas int64
+ StorageGas int64
+
+ TimeTaken time.Duration
+
+ Callers []uintptr `json:"-"`
+}
+
+func (gt *GasTrace) MarshalJSON() ([]byte, error) {
+ type GasTraceCopy GasTrace
+ if gt.Location == "" {
+ if len(gt.Callers) != 0 {
+ frames := runtime.CallersFrames(gt.Callers)
+ for {
+ frame, more := frames.Next()
+ fn := strings.Split(frame.Function, "/")
+
+ split := strings.Split(frame.File, "/")
+ file := strings.Join(split[len(split)-2:], "/")
+ gt.Location += fmt.Sprintf("%s@%s:%d", fn[len(fn)-1], file, frame.Line)
+ if !more {
+ break
+ }
+ gt.Location += "|"
+ }
+ } else {
+ gt.Location = "n/a"
+ }
+ }
+
+ cpy := (*GasTraceCopy)(gt)
+ return json.Marshal(cpy)
+}
diff --git a/chain/vm/gas.go b/chain/vm/gas.go
index f1cf89845..c030621f6 100644
--- a/chain/vm/gas.go
+++ b/chain/vm/gas.go
@@ -12,34 +12,57 @@ import (
"github.com/ipfs/go-cid"
)
+const (
+ GasStorageMulti = 1
+ GasComputeMulti = 1
+)
+
+type GasCharge struct {
+ Name string
+ ComputeGas int64
+ StorageGas int64
+}
+
+func (g GasCharge) Total() int64 {
+ return g.ComputeGas*GasComputeMulti + g.StorageGas*GasStorageMulti
+}
+
+func newGasCharge(name string, computeGas int64, storageGas int64) GasCharge {
+ return GasCharge{
+ Name: name,
+ ComputeGas: computeGas,
+ StorageGas: storageGas,
+ }
+}
+
// Pricelist provides prices for operations in the VM.
//
// Note: this interface should be APPEND ONLY since last chain checkpoint
type Pricelist interface {
// OnChainMessage returns the gas used for storing a message of a given size in the chain.
- OnChainMessage(msgSize int) int64
+ OnChainMessage(msgSize int) GasCharge
// OnChainReturnValue returns the gas used for storing the response of a message in the chain.
- OnChainReturnValue(dataSize int) int64
+ OnChainReturnValue(dataSize int) GasCharge
// OnMethodInvocation returns the gas used when invoking a method.
- OnMethodInvocation(value abi.TokenAmount, methodNum abi.MethodNum) int64
+ OnMethodInvocation(value abi.TokenAmount, methodNum abi.MethodNum) GasCharge
// OnIpldGet returns the gas used for storing an object
- OnIpldGet(dataSize int) int64
+ OnIpldGet(dataSize int) GasCharge
// OnIpldPut returns the gas used for storing an object
- OnIpldPut(dataSize int) int64
+ OnIpldPut(dataSize int) GasCharge
// OnCreateActor returns the gas used for creating an actor
- OnCreateActor() int64
+ OnCreateActor() GasCharge
// OnDeleteActor returns the gas used for deleting an actor
- OnDeleteActor() int64
+ OnDeleteActor() GasCharge
- OnVerifySignature(sigType crypto.SigType, planTextSize int) (int64, error)
- OnHashing(dataSize int) int64
- OnComputeUnsealedSectorCid(proofType abi.RegisteredProof, pieces []abi.PieceInfo) int64
- OnVerifySeal(info abi.SealVerifyInfo) int64
- OnVerifyPost(info abi.WindowPoStVerifyInfo) int64
- OnVerifyConsensusFault() int64
+ OnVerifySignature(sigType crypto.SigType, planTextSize int) (GasCharge, error)
+ OnHashing(dataSize int) GasCharge
+ OnComputeUnsealedSectorCid(proofType abi.RegisteredProof, pieces []abi.PieceInfo) GasCharge
+ OnVerifySeal(info abi.SealVerifyInfo) GasCharge
+ OnVerifyPost(info abi.WindowPoStVerifyInfo) GasCharge
+ OnVerifyConsensusFault() GasCharge
}
var prices = map[abi.ChainEpoch]Pricelist{
@@ -93,7 +116,7 @@ func PricelistByEpoch(epoch abi.ChainEpoch) Pricelist {
type pricedSyscalls struct {
under vmr.Syscalls
pl Pricelist
- chargeGas func(int64)
+ chargeGas func(GasCharge)
}
// Verifies that a signature is valid for an address and plaintext.
@@ -146,6 +169,6 @@ func (ps pricedSyscalls) VerifyConsensusFault(h1 []byte, h2 []byte, extra []byte
}
func (ps pricedSyscalls) BatchVerifySeals(inp map[address.Address][]abi.SealVerifyInfo) (map[address.Address][]bool, error) {
- ps.chargeGas(0) // TODO: this is only called by the cron actor. Should we even charge gas?
+ ps.chargeGas(newGasCharge("BatchVerifySeals", 0, 0)) // TODO: this is only called by the cron actor. Should we even charge gas?
return ps.under.BatchVerifySeals(inp)
}
diff --git a/chain/vm/gas_v0.go b/chain/vm/gas_v0.go
index a20ac927d..c631beaee 100644
--- a/chain/vm/gas_v0.go
+++ b/chain/vm/gas_v0.go
@@ -2,6 +2,7 @@ package vm
import (
"fmt"
+
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/specs-actors/actors/crypto"
@@ -84,17 +85,17 @@ type pricelistV0 struct {
var _ Pricelist = (*pricelistV0)(nil)
// OnChainMessage returns the gas used for storing a message of a given size in the chain.
-func (pl *pricelistV0) OnChainMessage(msgSize int) int64 {
- return pl.onChainMessageBase + pl.onChainMessagePerByte*int64(msgSize)
+func (pl *pricelistV0) OnChainMessage(msgSize int) GasCharge {
+ return newGasCharge("OnChainMessage", 0, pl.onChainMessageBase+pl.onChainMessagePerByte*int64(msgSize))
}
// OnChainReturnValue returns the gas used for storing the response of a message in the chain.
-func (pl *pricelistV0) OnChainReturnValue(dataSize int) int64 {
- return int64(dataSize) * pl.onChainReturnValuePerByte
+func (pl *pricelistV0) OnChainReturnValue(dataSize int) GasCharge {
+ return newGasCharge("OnChainReturnValue", 0, int64(dataSize)*pl.onChainReturnValuePerByte)
}
// OnMethodInvocation returns the gas used when invoking a method.
-func (pl *pricelistV0) OnMethodInvocation(value abi.TokenAmount, methodNum abi.MethodNum) int64 {
+func (pl *pricelistV0) OnMethodInvocation(value abi.TokenAmount, methodNum abi.MethodNum) GasCharge {
ret := pl.sendBase
if value != abi.NewTokenAmount(0) {
ret += pl.sendTransferFunds
@@ -102,62 +103,63 @@ func (pl *pricelistV0) OnMethodInvocation(value abi.TokenAmount, methodNum abi.M
if methodNum != builtin.MethodSend {
ret += pl.sendInvokeMethod
}
- return ret
+ return newGasCharge("OnMethodInvocation", ret, 0)
}
// OnIpldGet returns the gas used for storing an object
-func (pl *pricelistV0) OnIpldGet(dataSize int) int64 {
- return pl.ipldGetBase + int64(dataSize)*pl.ipldGetPerByte
+func (pl *pricelistV0) OnIpldGet(dataSize int) GasCharge {
+ return newGasCharge(fmt.Sprintf("OnIpldGet:%db", dataSize), pl.ipldGetBase+int64(dataSize)*pl.ipldGetPerByte, 0)
}
// OnIpldPut returns the gas used for storing an object
-func (pl *pricelistV0) OnIpldPut(dataSize int) int64 {
- return pl.ipldPutBase + int64(dataSize)*pl.ipldPutPerByte
+func (pl *pricelistV0) OnIpldPut(dataSize int) GasCharge {
+ return newGasCharge(fmt.Sprintf("OnIpldPut:%db", dataSize), pl.ipldPutBase, int64(dataSize)*pl.ipldPutPerByte)
}
// OnCreateActor returns the gas used for creating an actor
-func (pl *pricelistV0) OnCreateActor() int64 {
- return pl.createActorBase + pl.createActorExtra
+func (pl *pricelistV0) OnCreateActor() GasCharge {
+ return newGasCharge("OnCreateActor", pl.createActorBase, pl.createActorExtra)
}
// OnDeleteActor returns the gas used for deleting an actor
-func (pl *pricelistV0) OnDeleteActor() int64 {
- return pl.deleteActor
+func (pl *pricelistV0) OnDeleteActor() GasCharge {
+ return newGasCharge("OnDeleteActor", 0, pl.deleteActor)
}
// OnVerifySignature
-func (pl *pricelistV0) OnVerifySignature(sigType crypto.SigType, planTextSize int) (int64, error) {
+func (pl *pricelistV0) OnVerifySignature(sigType crypto.SigType, planTextSize int) (GasCharge, error) {
costFn, ok := pl.verifySignature[sigType]
if !ok {
- return 0, fmt.Errorf("cost function for signature type %d not supported", sigType)
+ return GasCharge{}, fmt.Errorf("cost function for signature type %d not supported", sigType)
}
- return costFn(int64(planTextSize)), nil
+ sigName, _ := sigType.Name()
+ return newGasCharge("OnVerifySignature/"+sigName, costFn(int64(planTextSize)), 0), nil
}
// OnHashing
-func (pl *pricelistV0) OnHashing(dataSize int) int64 {
- return pl.hashingBase + int64(dataSize)*pl.hashingPerByte
+func (pl *pricelistV0) OnHashing(dataSize int) GasCharge {
+ return newGasCharge("OnHashing", pl.hashingBase+int64(dataSize)*pl.hashingPerByte, 0)
}
// OnComputeUnsealedSectorCid
-func (pl *pricelistV0) OnComputeUnsealedSectorCid(proofType abi.RegisteredProof, pieces []abi.PieceInfo) int64 {
+func (pl *pricelistV0) OnComputeUnsealedSectorCid(proofType abi.RegisteredProof, pieces []abi.PieceInfo) GasCharge {
// TODO: this needs more cost tunning, check with @lotus
- return pl.computeUnsealedSectorCidBase
+ return newGasCharge("OnComputeUnsealedSectorCid", pl.computeUnsealedSectorCidBase, 0)
}
// OnVerifySeal
-func (pl *pricelistV0) OnVerifySeal(info abi.SealVerifyInfo) int64 {
+func (pl *pricelistV0) OnVerifySeal(info abi.SealVerifyInfo) GasCharge {
// TODO: this needs more cost tunning, check with @lotus
- return pl.verifySealBase
+ return newGasCharge("OnVerifySeal", pl.verifySealBase, 0)
}
// OnVerifyPost
-func (pl *pricelistV0) OnVerifyPost(info abi.WindowPoStVerifyInfo) int64 {
+func (pl *pricelistV0) OnVerifyPost(info abi.WindowPoStVerifyInfo) GasCharge {
// TODO: this needs more cost tunning, check with @lotus
- return pl.verifyPostBase
+ return newGasCharge("OnVerifyPost", pl.verifyPostBase, 0)
}
// OnVerifyConsensusFault
-func (pl *pricelistV0) OnVerifyConsensusFault() int64 {
- return pl.verifyConsensusFault
+func (pl *pricelistV0) OnVerifyConsensusFault() GasCharge {
+ return newGasCharge("OnVerifyConsensusFault", pl.verifyConsensusFault, 0)
}
diff --git a/chain/vm/runtime.go b/chain/vm/runtime.go
index 70583e47f..902ef94bb 100644
--- a/chain/vm/runtime.go
+++ b/chain/vm/runtime.go
@@ -5,6 +5,8 @@ import (
"context"
"encoding/binary"
"fmt"
+ gruntime "runtime"
+ "time"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/specs-actors/actors/abi"
@@ -50,10 +52,12 @@ type Runtime struct {
origin address.Address
originNonce uint64
- executionTrace types.ExecutionTrace
- numActorsCreated uint64
- allowInternal bool
- callerValidated bool
+ executionTrace types.ExecutionTrace
+ numActorsCreated uint64
+ allowInternal bool
+ callerValidated bool
+ lastGasChargeTime time.Time
+ lastGasCharge *types.GasTrace
}
func (rt *Runtime) TotalFilCircSupply() abi.TokenAmount {
@@ -392,7 +396,7 @@ func (rt *Runtime) internalSend(from, to address.Address, method abi.MethodNum,
}
defer st.ClearSnapshot()
- ret, errSend, subrt := rt.vm.send(ctx, msg, rt, 0)
+ ret, errSend, subrt := rt.vm.send(ctx, msg, rt, nil)
if errSend != nil {
if errRevert := st.Revert(); errRevert != nil {
return nil, aerrors.Escalate(errRevert, "failed to revert state tree after failed subcall")
@@ -484,14 +488,39 @@ func (rt *Runtime) stateCommit(oldh, newh cid.Cid) aerrors.ActorError {
return nil
}
-func (rt *Runtime) ChargeGas(toUse int64) {
- err := rt.chargeGasInternal(toUse)
+func (rt *Runtime) ChargeGas(gas GasCharge) {
+ err := rt.chargeGasInternal(gas)
if err != nil {
panic(err)
}
}
+func (rt *Runtime) finilizeGasTracing() {
+ if rt.lastGasCharge != nil {
+ rt.lastGasCharge.TimeTaken = time.Since(rt.lastGasChargeTime)
+ }
+}
+
+func (rt *Runtime) chargeGasInternal(gas GasCharge) aerrors.ActorError {
+ toUse := gas.Total()
+ var callers [3]uintptr
+ cout := gruntime.Callers(3, callers[:])
+
+ now := time.Now()
+ if rt.lastGasCharge != nil {
+ rt.lastGasCharge.TimeTaken = now.Sub(rt.lastGasChargeTime)
+ }
+
+ gasTrace := types.GasTrace{
+ Name: gas.Name,
+ TotalGas: toUse,
+ ComputeGas: gas.ComputeGas,
+ StorageGas: gas.StorageGas,
+ Callers: callers[:cout],
+ }
+ rt.executionTrace.GasCharges = append(rt.executionTrace.GasCharges, &gasTrace)
+ rt.lastGasChargeTime = now
+ rt.lastGasCharge = &gasTrace
-func (rt *Runtime) chargeGasInternal(toUse int64) aerrors.ActorError {
if rt.gasUsed+toUse > rt.gasAvailable {
rt.gasUsed = rt.gasAvailable
return aerrors.Newf(exitcode.SysErrOutOfGas, "not enough gas: used=%d, available=%d", rt.gasUsed, rt.gasAvailable)
@@ -500,8 +529,8 @@ func (rt *Runtime) chargeGasInternal(toUse int64) aerrors.ActorError {
return nil
}
-func (rt *Runtime) chargeGasSafe(toUse int64) aerrors.ActorError {
- return rt.chargeGasInternal(toUse)
+func (rt *Runtime) chargeGasSafe(gas GasCharge) aerrors.ActorError {
+ return rt.chargeGasInternal(gas)
}
func (rt *Runtime) Pricelist() Pricelist {
diff --git a/chain/vm/vm.go b/chain/vm/vm.go
index 42d6d373e..75bbdfedb 100644
--- a/chain/vm/vm.go
+++ b/chain/vm/vm.go
@@ -62,7 +62,7 @@ func ResolveToKeyAddr(state types.StateTree, cst cbor.IpldStore, addr address.Ad
var _ cbor.IpldBlockstore = (*gasChargingBlocks)(nil)
type gasChargingBlocks struct {
- chargeGas func(int64)
+ chargeGas func(GasCharge)
pricelist Pricelist
under cbor.IpldBlockstore
}
@@ -96,13 +96,14 @@ func (vm *VM) makeRuntime(ctx context.Context, msg *types.Message, origin addres
originNonce: originNonce,
height: vm.blockHeight,
- gasUsed: usedGas,
- gasAvailable: msg.GasLimit,
- numActorsCreated: nac,
- pricelist: PricelistByEpoch(vm.blockHeight),
- allowInternal: true,
- callerValidated: false,
- executionTrace: types.ExecutionTrace{Msg: msg},
+ gasUsed: usedGas,
+ gasAvailable: msg.GasLimit,
+ numActorsCreated: nac,
+ pricelist: PricelistByEpoch(vm.blockHeight),
+ allowInternal: true,
+ callerValidated: false,
+ executionTrace: types.ExecutionTrace{Msg: msg},
+ lastGasChargeTime: time.Now(),
}
rt.cst = &cbor.BasicIpldStore{
@@ -171,17 +172,17 @@ type ApplyRet struct {
}
func (vm *VM) send(ctx context.Context, msg *types.Message, parent *Runtime,
- gasCharge int64) ([]byte, aerrors.ActorError, *Runtime) {
+ gasCharge *GasCharge) ([]byte, aerrors.ActorError, *Runtime) {
start := time.Now()
st := vm.cstate
- gasUsed := gasCharge
origin := msg.From
on := msg.Nonce
var nac uint64 = 0
+ var gasUsed int64
if parent != nil {
- gasUsed = parent.gasUsed + gasUsed
+ gasUsed = parent.gasUsed
origin = parent.origin
on = parent.originNonce
nac = parent.numActorsCreated
@@ -189,11 +190,22 @@ func (vm *VM) send(ctx context.Context, msg *types.Message, parent *Runtime,
rt := vm.makeRuntime(ctx, msg, origin, on, gasUsed, nac)
if parent != nil {
+ rt.lastGasChargeTime = parent.lastGasChargeTime
+ rt.lastGasCharge = parent.lastGasCharge
defer func() {
parent.gasUsed = rt.gasUsed
+ parent.lastGasChargeTime = rt.lastGasChargeTime
+ parent.lastGasCharge = rt.lastGasCharge
}()
}
+ if gasCharge != nil {
+ if err := rt.chargeGasSafe(*gasCharge); err != nil {
+ // this should never happen
+ return nil, aerrors.Wrap(err, "not enough gas for initial message charge, this should not happen"), rt
+ }
+ }
+
ret, err := func() ([]byte, aerrors.ActorError) {
if aerr := rt.chargeGasSafe(rt.Pricelist().OnMethodInvocation(msg.Value, msg.Method)); aerr != nil {
return nil, aerrors.Wrap(aerr, "not enough gas for method invocation")
@@ -261,7 +273,7 @@ func checkMessage(msg *types.Message) error {
func (vm *VM) ApplyImplicitMessage(ctx context.Context, msg *types.Message) (*ApplyRet, error) {
start := time.Now()
- ret, actorErr, rt := vm.send(ctx, msg, nil, 0)
+ ret, actorErr, rt := vm.send(ctx, msg, nil, nil)
return &ApplyRet{
MessageReceipt: types.MessageReceipt{
ExitCode: aerrors.RetCode(actorErr),
@@ -294,7 +306,8 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
pl := PricelistByEpoch(vm.blockHeight)
- msgGasCost := pl.OnChainMessage(cmsg.ChainLength())
+ msgGas := pl.OnChainMessage(cmsg.ChainLength())
+ msgGasCost := msgGas.Total()
// this should never happen, but is currently still exercised by some tests
if msgGasCost > msg.GasLimit {
return &ApplyRet{
@@ -377,7 +390,8 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
}
defer st.ClearSnapshot()
- ret, actorErr, rt := vm.send(ctx, msg, nil, msgGasCost)
+ ret, actorErr, rt := vm.send(ctx, msg, nil, &msgGas)
+ rt.finilizeGasTracing()
if aerrors.IsFatal(actorErr) {
return nil, xerrors.Errorf("[from=%s,to=%s,n=%d,m=%d,h=%d] fatal error: %w", msg.From, msg.To, msg.Nonce, msg.Method, vm.blockHeight, actorErr)
}
diff --git a/cli/state.go b/cli/state.go
index 1b6f676f2..acc648c23 100644
--- a/cli/state.go
+++ b/cli/state.go
@@ -975,6 +975,11 @@ func computeStateHtml(ts *types.TipSet, o *api.ComputeStateOutput, getCode func(
}
.slow-true-false { color: #660; }
.slow-true-true { color: #f80; }
+ table {
+ font-size: 12px;
+ border-collapse: collapse;
+ }
+ tr.sum { border-top: 1px solid black; }
@@ -1026,6 +1031,23 @@ func computeStateHtml(ts *types.TipSet, o *api.ComputeStateOutput, getCode func(
if ir.MsgRct.ExitCode != 0 {
fmt.Printf(``, ir.Error)
}
+ fmt.Printf("\nGas Trace
" +
+ "| Name | Total/Compute/Storage | Time Taken | Location |
")
+
+ var sumTotal, sumCompute, sumStorage int64
+ var sumTime time.Duration
+ for _, gc := range ir.ExecutionTrace.GasCharges {
+ fmt.Printf("| %s | %d/%d/%d | %s | %s |
",
+ gc.Name, gc.TotalGas, gc.ComputeGas, gc.StorageGas, gc.TimeTaken, gc.Location)
+ sumTotal += gc.TotalGas
+ sumCompute += gc.ComputeGas
+ sumStorage += gc.StorageGas
+ sumTime += gc.TimeTaken
+ }
+ fmt.Printf("| %s | %d/%d/%d | %s | %s |
",
+ "Sum", sumTotal, sumCompute, sumStorage, sumTime, "")
+
+ fmt.Printf("
\n")
fmt.Println("Execution trace:
")
if err := printInternalExecutionsHtml(cid.String(), ir.ExecutionTrace.Subcalls, getCode); err != nil {
@@ -1082,6 +1104,21 @@ func printInternalExecutionsHtml(hashName string, trace []types.ExecutionTrace,
if im.MsgRct.ExitCode != 0 {
fmt.Printf(``, im.Error)
}
+ fmt.Printf("\nGas Trace
" +
+ "| Name | Total/Compute/Storage | Time Taken | Location |
")
+ var sumTotal, sumCompute, sumStorage int64
+ var sumTime time.Duration
+ for _, gc := range im.GasCharges {
+ fmt.Printf("| %s | %d/%d/%d | %s | %s |
",
+ gc.Name, gc.TotalGas, gc.ComputeGas, gc.StorageGas, gc.TimeTaken, gc.Location)
+ sumTotal += gc.TotalGas
+ sumCompute += gc.ComputeGas
+ sumStorage += gc.StorageGas
+ sumTime += gc.TimeTaken
+ }
+ fmt.Printf("| %s | %d/%d/%d | %s | %s |
",
+ "Sum", sumTotal, sumCompute, sumStorage, sumTime, "")
+ fmt.Printf("
\n")
if len(im.Subcalls) > 0 {
fmt.Println("Subcalls:
")
if err := printInternalExecutionsHtml(hashName, im.Subcalls, getCode); err != nil {
diff --git a/miner/miner.go b/miner/miner.go
index f2059ef7f..23f40338d 100644
--- a/miner/miner.go
+++ b/miner/miner.go
@@ -485,7 +485,7 @@ func SelectMessages(ctx context.Context, al ActorLookup, ts *types.TipSet, msgs
vmstart := time.Now()
minGas := vm.PricelistByEpoch(ts.Height()).OnChainMessage(msg.ChainLength()) // TODO: really should be doing just msg.ChainLength() but the sync side of this code doesnt seem to have access to that
- if err := msg.VMMessage().ValidForBlockInclusion(minGas); err != nil {
+ if err := msg.VMMessage().ValidForBlockInclusion(minGas.Total()); err != nil {
log.Warnf("invalid message in message pool: %s", err)
continue
}