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
// message no matter the execution itself is successful or not.
type ExecutionResult struct {
UsedGas uint64 // Total used gas but include the refunded gas
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)
UsedGas uint64 // Total used gas, not including the refunded gas
RefundedGas uint64 // Total gas refunded after execution
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
@ -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)
}
var gasRefund uint64
if !rules.IsLondon {
// Before EIP-3529: refunds were capped to gasUsed / 2
st.refundGas(params.RefundQuotient)
gasRefund = st.refundGas(params.RefundQuotient)
} else {
// After EIP-3529: refunds are capped to gasUsed / 5
st.refundGas(params.RefundQuotientEIP3529)
gasRefund = st.refundGas(params.RefundQuotientEIP3529)
}
effectiveTip := msg.GasPrice
if rules.IsLondon {
@ -442,13 +444,14 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
}
return &ExecutionResult{
UsedGas: st.gasUsed(),
Err: vmerr,
ReturnData: ret,
UsedGas: st.gasUsed(),
RefundedGas: gasRefund,
Err: vmerr,
ReturnData: ret,
}, nil
}
func (st *StateTransition) refundGas(refundQuotient uint64) {
func (st *StateTransition) refundGas(refundQuotient uint64) uint64 {
// Apply refund counter, capped to a refund quotient
refund := st.gasUsed() / refundQuotient
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
// available for the next transaction.
st.gp.AddGas(st.gasRemaining)
return refund
}
// gasUsed returns the amount of gas used up by the state transition.

View file

@ -129,6 +129,24 @@ func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uin
// limit for these cases anyway.
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.
for lo+1 < hi {
if opts.ErrorRatio > 0 {