Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,30 @@ func ApplyMessage(evm *vm.EVM, msg vm.Message, gp *GasPool) ([]byte, uint64, boo
return NewStateTransition(evm, msg, gp).TransitionDb()
}

// NewStateTransitionGasEstimator returns a special state transition for estimating gas consumption.
// Estimation runs the given message as if gas were free, which allows binary search under the
// assumption that the execution is not dependent on gas limit or gas price, with the exception of
// "out of gas" errors.
func NewStateTransitionGasEstimator(evm *vm.EVM, msg vm.Message, gp *GasPool) *StateTransition {
return &StateTransition{
gp: gp,
evm: evm,
msg: msg,
gasPrice: common.Big0,
value: msg.Value(),
data: msg.Data(),
state: evm.StateDB,
gasPriceMinimum: common.Big0,
}
}

// ApplyEstimatorMessage applies the given message in a way that allows for estimation of gas consumption.
// Returns the gas used (which does not include gas refunds) and an error if it failed.
func ApplyEstimatorMessage(evm *vm.EVM, msg vm.Message, gp *GasPool) ([]byte, uint64, bool, error) {
log.Trace("Estimating gas for message", "from", msg.From(), "nonce", msg.Nonce(), "to", msg.To(), "fee currency", msg.FeeCurrency(), "gateway fee recipient", msg.GatewayFeeRecipient(), "gateway fee", msg.GatewayFee(), "gas limit", msg.Gas(), "value", msg.Value(), "data", msg.Data())
return NewStateTransitionGasEstimator(evm, msg, gp).TransitionDb()
}

// to returns the recipient of the message.
func (st *StateTransition) to() common.Address {
if st.msg == nil || st.msg.To() == nil /* contract creation */ {
Expand Down
15 changes: 9 additions & 6 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,7 @@ type CallArgs struct {
Data hexutil.Bytes `json:"data"`
}

func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, timeout time.Duration) ([]byte, uint64, bool, error) {
func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, timeout time.Duration, estimate bool) (res []byte, gas uint64, failed bool, err error) {
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())

state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
Expand Down Expand Up @@ -738,10 +738,13 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
evm.Cancel()
}()

// Setup the gas pool (also for unmetered requests)
// and apply the message.
// Setup the gas pool (also for unmetered requests) and apply the message.
gp := new(core.GasPool).AddGas(math.MaxUint64)
res, gas, failed, err := core.ApplyMessage(evm, msg, gp)
if estimate {
res, gas, failed, err = core.ApplyEstimatorMessage(evm, msg, gp)
} else {
res, gas, failed, err = core.ApplyMessage(evm, msg, gp)
}

if err := vmError(); err != nil {
return nil, 0, false, err
Expand All @@ -752,7 +755,7 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
// Call executes the given transaction on the state for the given block number.
// It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values.
func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {
result, _, _, err := s.doCall(ctx, args, blockNr, 5*time.Second)
result, _, _, err := s.doCall(ctx, args, blockNr, 5*time.Second, false)
return (hexutil.Bytes)(result), err
}

Expand Down Expand Up @@ -781,7 +784,7 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (h
executable := func(gas uint64) bool {
args.Gas = hexutil.Uint64(gas)

_, _, failed, err := s.doCall(ctx, args, rpc.PendingBlockNumber, 0)
_, _, failed, err := s.doCall(ctx, args, rpc.PendingBlockNumber, 0, true)
if err != nil || failed {
return false
}
Expand Down