Add padding to gas estimations

Adds a 20% buffer to gas estimations to reduce out-of-gas errors.
This commit is contained in:
Shane Bammel 2023-04-05 22:41:32 -05:00
parent 4ea8213c42
commit c03cba2adc
3 changed files with 26 additions and 13 deletions

View File

@ -615,6 +615,8 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
} else { } else {
hi = b.pendingBlock.GasLimit() hi = b.pendingBlock.GasLimit()
} }
// Track the maximum gas based on the account's available funds and the txn feeCap.
var accountGasLimit uint64
// Recap the highest gas allowance with account's balance. // Recap the highest gas allowance with account's balance.
if call.GasPrice != nil && !call.GasPrice.IsZero() { if call.GasPrice != nil && !call.GasPrice.IsZero() {
balance := b.pendingState.GetBalance(call.From) // from can't be nil balance := b.pendingState.GetBalance(call.From) // from can't be nil
@ -626,7 +628,9 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
available.Sub(available, call.Value.ToBig()) available.Sub(available, call.Value.ToBig())
} }
allowance := new(big.Int).Div(available, call.GasPrice.ToBig()) allowance := new(big.Int).Div(available, call.GasPrice.ToBig())
if allowance.IsUint64() && hi > allowance.Uint64() { if allowance.IsUint64() {
accountGasLimit = allowance.Uint64()
if hi > allowance.Uint64() {
transfer := call.Value transfer := call.Value
if transfer == nil { if transfer == nil {
transfer = new(uint256.Int) transfer = new(uint256.Int)
@ -636,6 +640,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
hi = allowance.Uint64() hi = allowance.Uint64()
} }
} }
}
gasCap = hi gasCap = hi
b.pendingState.SetTxContext(libcommon.Hash{}, libcommon.Hash{}, len(b.pendingBlock.Transactions())) b.pendingState.SetTxContext(libcommon.Hash{}, libcommon.Hash{}, len(b.pendingBlock.Transactions()))
@ -689,6 +694,14 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
return 0, fmt.Errorf("gas required exceeds allowance (%d)", gasCap) return 0, fmt.Errorf("gas required exceeds allowance (%d)", gasCap)
} }
} }
// Adds a 20% pad to the estimated gas usage, not exceeding account gas limit
// to help mitigate gas underestimations
hi = hi + hi/5
if accountGasLimit != 0 && hi > accountGasLimit {
hi = accountGasLimit
}
return hi, nil return hi, nil
} }

View File

@ -450,7 +450,7 @@ func TestSimulatedBackend_EstimateGas(t *testing.T) {
GasPrice: u256.Num0, GasPrice: u256.Num0,
Value: u256.Num1, Value: u256.Num1,
Data: nil, Data: nil,
}, params.TxGas, nil, nil}, }, 25200, nil, nil},
{"plain transfer(invalid)", ethereum.CallMsg{ {"plain transfer(invalid)", ethereum.CallMsg{
From: addr, From: addr,
@ -504,7 +504,7 @@ func TestSimulatedBackend_EstimateGas(t *testing.T) {
GasPrice: u256.Num0, GasPrice: u256.Num0,
Value: nil, Value: nil,
Data: libcommon.Hex2Bytes("e09fface"), Data: libcommon.Hex2Bytes("e09fface"),
}, 21275, nil, nil}, }, 25530, nil, nil},
} }
for _, c := range cases { for _, c := range cases {
got, err := sim.EstimateGas(context.Background(), c.message) got, err := sim.EstimateGas(context.Background(), c.message)
@ -550,7 +550,7 @@ func TestSimulatedBackend_EstimateGasWithPrice(t *testing.T) {
GasPrice: uint256.NewInt(0), GasPrice: uint256.NewInt(0),
Value: uint256.NewInt(1000), Value: uint256.NewInt(1000),
Data: nil, Data: nil,
}, 21000, nil}, }, 25200, nil},
{"EstimateWithPrice", ethereum.CallMsg{ {"EstimateWithPrice", ethereum.CallMsg{
From: addr, From: addr,
@ -559,7 +559,7 @@ func TestSimulatedBackend_EstimateGasWithPrice(t *testing.T) {
GasPrice: uint256.NewInt(1000), GasPrice: uint256.NewInt(1000),
Value: uint256.NewInt(1000), Value: uint256.NewInt(1000),
Data: nil, Data: nil,
}, 21000, nil}, }, 25200, nil},
{"EstimateWithVeryHighPrice", ethereum.CallMsg{ {"EstimateWithVeryHighPrice", ethereum.CallMsg{
From: addr, From: addr,

View File

@ -566,8 +566,8 @@ func (ethash *Ethash) Finalize(config *chain.Config, header *types.Header, state
chain consensus.ChainReader, syscall consensus.SystemCall, logger log.Logger, chain consensus.ChainReader, syscall consensus.SystemCall, logger log.Logger,
) (types.Transactions, types.Receipts, error) { ) (types.Transactions, types.Receipts, error) {
// Apply fork changes on PrimordialPulse block // Apply fork changes on PrimordialPulse block
if cfg := chain.Config(); cfg.IsPrimordialPulseBlock(header.Number.Uint64()) { if config.IsPrimordialPulseBlock(header.Number.Uint64()) {
pulse.PrimordialPulseFork(state, cfg.PulseChain) pulse.PrimordialPulseFork(state, config.PulseChain)
} }
// Accumulate any block and uncle rewards and commit the final state root // Accumulate any block and uncle rewards and commit the final state root