besu
besu copied to clipboard
Besu doesn't apply EIP-7623 during gas estimation
We received the following report:
EIP-7623 increases calldata costs for data-heavy transactions. The issue arises from the fact that while Besu applies EIP-7623 during block processing, it does not apply it during gas estimation.
This happens due to passing gasUsedByTransaction to TransactionProcessResult in the MainnetTransactionProcessor.processTransaction function. One such occurrence is demonstrated in the referenced code. The gasUsedByTransaction value is computed as:
final long gasUsedByTransaction = transaction.getGasLimit() - initialFrame.getRemainingGas();
This value does not account for EIP-7623 and, therefore, might be off by a significant margin. The POC demonstrates a case where Besu returns a gas estimate of 659116, while the actual value is approximately 1630000.
This issue could result in highly inaccurate responses to eth_estimateGas calls, potentially leading to fund losses for users relying on Besu nodes to set transaction gas limit values.
Proof of Concept
Run the following test with Besu and other execution clients:
package main
import (
"context"
"fmt"
"testing"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/stretchr/testify/require"
)
func TestPocBesuEstimateGasEip7623(t *testing.T) {
// 1. Generate huge calldata
data := make([]byte, 40000)
for i := range data {
data[i] = byte(i)
}
// 2. Prepare message
msg := ethereum.CallMsg{
To: &common.Address{},
From: common.Address{},
Data: data,
}
// 3. Check the gas estimate for all RPCs. The RPCs correspond to Geth, Besu, and Nethermind nodes, respectively.
rpcs := []string{"http://127.0.0.1:32913", "http://127.0.0.1:32918", "http://127.0.0.1:32923"}
for _, rpc := range rpcs {
client, err := ethclient.Dial(rpc)
require.NoError(t, err)
gas, err := client.EstimateGas(context.Background(), msg)
require.NoError(t, err)
fmt.Println(gas)
}
}
In my case, the result looks as follows:
=== RUN TestPocBesuEstimateGasEip7623
1630285
659116
1629578
--- PASS: TestPocBesuEstimateGasEip7623 (0.01s)
PASS
ok github.com/alexfilippov314/gop 0.027s
Recommendation
I believe the historical reason for using this value as the gas estimate was to exclude gas refunds, which makes sense since refunds can only decrease the required gas limit. However, after implementing EIP-7623 in the PragueGasCalculator.calculateGasRefund function, this logic now seems problematic, as the required gas limit could be significantly greater than the estimate. Consider refactoring this logic to account for EIP-7623 in the gas estimate.
https://github.com/hyperledger/besu/blob/7e5cf71a053c1768a8d6391a6c377c0c37b798e3/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java#L540