core, eth/gasestimator: hard guess at a possible required gas

This commit is contained in:
Oren 2023-11-28 15:38:32 +02:00 committed by Péter Szilágyi
parent bfcb350f36
commit a0b41b670f
2 changed files with 33 additions and 10 deletions

View file

@ -32,9 +32,10 @@ import (
// ExecutionResult includes all output after executing given evm // ExecutionResult includes all output after executing given evm
// message no matter the execution itself is successful or not. // message no matter the execution itself is successful or not.
type ExecutionResult struct { type ExecutionResult struct {
UsedGas uint64 // Total used gas but include the refunded gas UsedGas uint64 // Total used gas, not including the refunded gas
Err error // Any error encountered during the execution(listed in core/vm/errors.go) RefundedGas uint64 // Total gas refunded after execution
ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode) Err error // Any error encountered during the execution(listed in core/vm/errors.go)
ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode)
} }
// Unwrap returns the internal evm error which allows us for further // Unwrap returns the internal evm error which allows us for further
@ -419,12 +420,13 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data, st.gasRemaining, msg.Value) ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data, st.gasRemaining, msg.Value)
} }
var gasRefund uint64
if !rules.IsLondon { if !rules.IsLondon {
// Before EIP-3529: refunds were capped to gasUsed / 2 // Before EIP-3529: refunds were capped to gasUsed / 2
st.refundGas(params.RefundQuotient) gasRefund = st.refundGas(params.RefundQuotient)
} else { } else {
// After EIP-3529: refunds are capped to gasUsed / 5 // After EIP-3529: refunds are capped to gasUsed / 5
st.refundGas(params.RefundQuotientEIP3529) gasRefund = st.refundGas(params.RefundQuotientEIP3529)
} }
effectiveTip := msg.GasPrice effectiveTip := msg.GasPrice
if rules.IsLondon { if rules.IsLondon {
@ -442,13 +444,14 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
} }
return &ExecutionResult{ return &ExecutionResult{
UsedGas: st.gasUsed(), UsedGas: st.gasUsed(),
Err: vmerr, RefundedGas: gasRefund,
ReturnData: ret, Err: vmerr,
ReturnData: ret,
}, nil }, nil
} }
func (st *StateTransition) refundGas(refundQuotient uint64) { func (st *StateTransition) refundGas(refundQuotient uint64) uint64 {
// Apply refund counter, capped to a refund quotient // Apply refund counter, capped to a refund quotient
refund := st.gasUsed() / refundQuotient refund := st.gasUsed() / refundQuotient
if refund > st.state.GetRefund() { if refund > st.state.GetRefund() {
@ -463,6 +466,8 @@ func (st *StateTransition) refundGas(refundQuotient uint64) {
// Also return remaining gas to the block gas counter so it is // Also return remaining gas to the block gas counter so it is
// available for the next transaction. // available for the next transaction.
st.gp.AddGas(st.gasRemaining) st.gp.AddGas(st.gasRemaining)
return refund
} }
// gasUsed returns the amount of gas used up by the state transition. // gasUsed returns the amount of gas used up by the state transition.

View file

@ -128,7 +128,25 @@ func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uin
// given limit, but we probably don't want to return the lowest possible gas // given limit, but we probably don't want to return the lowest possible gas
// limit for these cases anyway. // limit for these cases anyway.
lo = result.UsedGas - 1 lo = result.UsedGas - 1
// There's a fairly high chance for the transaction to execute successfully
// with gasLimit set to the first execution's usedGas + gasRefund. Explicitly
// check that gas amount and use as a limit for the binary search.
optimisticGasLimit := (result.UsedGas + result.RefundedGas + params.CallStipend) * 64 / 63
if optimisticGasLimit < hi {
failed, _, err = execute(ctx, call, opts, optimisticGasLimit)
if err != nil {
// This should not happen under normal conditions since if we make it this far the
// transaction had run without error at least once before.
log.Error("Execution error in estimate gas", "err", err)
return 0, nil, err
}
if failed {
lo = optimisticGasLimit
} else {
hi = optimisticGasLimit
}
}
// Binary search for the smallest gas limit that allows the tx to execute successfully. // Binary search for the smallest gas limit that allows the tx to execute successfully.
for lo+1 < hi { for lo+1 < hi {
if opts.ErrorRatio > 0 { if opts.ErrorRatio > 0 {