Skip to content

Commit

Permalink
Fix gas fee calculation for debug calls (#10825)
Browse files Browse the repository at this point in the history
Hi guys, we had an issue come up on our fork of erigon regarding some
gas fee calculation:
testinprod-io#183

We thought this would apply to upstream erigon as well, so brought the
changes here too.

I'd appreciate any feedbacks or concerns for this 😄 

## Problem
This PR fixes gas fee calculation for rpc calls. It mostly borrows
changes from geth and resolves previous issues.

```bash
curl "https://localhost:8545 " -X POST -H "Content-Type: application/json" -d  ' {"jsonrpc": "2.0", "id": "1", "method": "eth_estimateGas", "params": [{"from": "0xa5B8492D8223D255dB279C7c3ebdA34Be5eC9D85","data": "0x00", "to": "0xa5B8492D8223D255dB279C7c3ebdA34Be5eC9D85" }, "pending"]}'   
```
Results in:
```
"code":-32000,"message":"insufficient funds for gas * price + value: address 0xa5B8492D8223D255dB279C7c3ebdA34Be5eC9D85 have 0 want 56956314575938201239799312"
```

Instead, it should return a valid RPC response.

## Detail
Currently, erigon sets the gasFeeCap to header's baseFee by default.
Setting gasFeeCap to baseFee will prevent calling eth_estimateGas with
0-balance accounts because the account's balance has to be bigger than
the baseFee.

However, in practice, many debug calls and estimateGas calls are called
on 0-balance accounts, and without any gas-related fields set. Previous
version of erigon and Geth allowed debug calls to be called on 0-balance
accounts without setting gas parameters, so this PR attempts to bring
back such behavior.

## Solution

There is `evm.Config().NoBaseFee` which is used to signify that base fee
is not required for this evm call. When this field is set AND gasPrice
is not assigned, we set the block context's baseFee to 0. Because the
baseFee is configured to 0 for these debug rpc calls, the feeCap will be
always bigger than baseFee which passes the eip1599 validation. Also,
0-account balances will be able to call rpcs since the required gasFee
is 0.

This allows any debug calls without explicit gasPrice to also ignore the
baseFee when calculating account balance/eip1559 check/etc.

Also, currently, `NoBaseFee` is set to `true` for calls like
`estimateGas`/`createAccessList`/`eth_call`, but not for
`debug_traceCall`. This PR also sets `NoBaseFee` to `true` for
`debug_traceCall` so that they can also ignore the base fee rule when
needed.

This PR from geth addresses similar concern:
ethereum/go-ethereum#23027
  • Loading branch information
mininny authored Jun 24, 2024
1 parent d2e5c05 commit 15d9223
Show file tree
Hide file tree
Showing 5 changed files with 11 additions and 6 deletions.
5 changes: 3 additions & 2 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,8 @@ func (st *StateTransition) preCheck(gasBailout bool) error {
// Make sure the transaction gasFeeCap is greater than the block's baseFee.
if st.evm.ChainRules().IsLondon {
// Skip the checks if gas fields are zero and baseFee was explicitly disabled (eth_call)
if !st.evm.Config().NoBaseFee || !st.gasFeeCap.IsZero() || !st.tip.IsZero() {
skipCheck := st.evm.Config().NoBaseFee && st.gasFeeCap.IsZero() && st.tip.IsZero()
if !skipCheck {
if err := CheckEip1559TxGasFeeCap(st.msg.From(), st.gasFeeCap, st.tip, st.evm.Context.BaseFee, st.msg.IsFree()); err != nil {
return err
}
Expand All @@ -280,7 +281,7 @@ func (st *StateTransition) preCheck(gasBailout bool) error {
return fmt.Errorf("%w: Cancun is active but ExcessBlobGas is nil", ErrInternalFailure)
}
maxFeePerBlobGas := st.msg.MaxFeePerBlobGas()
if blobGasPrice.Cmp(maxFeePerBlobGas) > 0 {
if !st.evm.Config().NoBaseFee && blobGasPrice.Cmp(maxFeePerBlobGas) > 0 {
return fmt.Errorf("%w: address %v, maxFeePerBlobGas: %v < blobGasPrice: %v",
ErrMaxFeePerBlobGas, st.msg.From().Hex(), st.msg.MaxFeePerBlobGas(), blobGasPrice)
}
Expand Down
6 changes: 6 additions & 0 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ type EVM struct {
// NewEVM returns a new EVM. The returned EVM is not thread safe and should
// only ever be used *once*.
func NewEVM(blockCtx evmtypes.BlockContext, txCtx evmtypes.TxContext, state evmtypes.IntraBlockState, chainConfig *chain.Config, vmConfig Config) *EVM {
if vmConfig.NoBaseFee {
if txCtx.GasPrice.IsZero() {
blockCtx.BaseFee = new(uint256.Int)
}
}

evm := &EVM{
Context: blockCtx,
TxContext: txCtx,
Expand Down
2 changes: 1 addition & 1 deletion turbo/adapter/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func (args *CallArgs) ToMessage(globalGasCap uint64, baseFee *uint256.Int) (type
gasFeeCap, gasTipCap = gasPrice, gasPrice
} else {
// User specified 1559 gas fields (or none), use those
gasFeeCap = new(uint256.Int).Set(baseFee)
gasFeeCap = new(uint256.Int)
if args.MaxFeePerGas != nil {
overflow := gasFeeCap.SetFromBig(args.MaxFeePerGas.ToInt())
if overflow {
Expand Down
2 changes: 0 additions & 2 deletions turbo/jsonrpc/eth_call.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,6 @@ func (api *APIImpl) EstimateGas(ctx context.Context, argsOrNil *ethapi2.CallArgs
feeCap = args.GasPrice.ToInt()
} else if args.MaxFeePerGas != nil {
feeCap = args.MaxFeePerGas.ToInt()
} else if header.BaseFee != nil {
feeCap = new(big.Int).Set(header.BaseFee)
} else {
feeCap = libcommon.Big0
}
Expand Down
2 changes: 1 addition & 1 deletion turbo/transactions/tracing.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ func ExecuteTraceTx(
execCb func(evm *vm.EVM, refunds bool) (*evmtypes.ExecutionResult, error),
) error {
// Run the transaction with tracing enabled.
evm := vm.NewEVM(blockCtx, txCtx, ibs, chainConfig, vm.Config{Debug: true, Tracer: tracer})
evm := vm.NewEVM(blockCtx, txCtx, ibs, chainConfig, vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true})

var refunds = true
if config != nil && config.NoRefunds != nil && *config.NoRefunds {
Expand Down

0 comments on commit 15d9223

Please sign in to comment.