Skip to content

Commit 11dcf81

Browse files
authored
Improve transaction simulation when no gas pricing is present (#8888)
Signed-off-by: Fabio Di Fabio <[email protected]>
1 parent a483287 commit 11dcf81

File tree

15 files changed

+393
-48
lines changed

15 files changed

+393
-48
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
## Unreleased
44

55
### Breaking Changes
6+
- Change in behavior for `eth_estimateGas`, to improve accuracy, when used on a network with a base fee market, the internal transaction simulation does not anymore underprice transactions, so if there are no gas pricing related fields specified in the request, then gas price for the transaction is set to the base fee value [#8888](https://github.com/hyperledger/besu/pull/8888)
67

78
### Upcoming Breaking Changes
89

910
### Additions and Improvements
10-
11+
- Improve transaction simulation and gas estimation when no gas pricing is present [#8888](https://github.com/hyperledger/besu/pull/8888)
1112
#### Fusaka
1213

1314
### Bug fixes

acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/eth/EthEstimateGasTransaction.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
public class EthEstimateGasTransaction implements Transaction<EthEstimateGas> {
2828
private final String contractAddress;
2929
private final String functionCall;
30-
private final String from = "";
30+
private final String from = "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73";
3131

3232
public EthEstimateGasTransaction(final String contractAddress, final String functionCall) {
3333
this.contractAddress = contractAddress;
@@ -45,7 +45,7 @@ public EthEstimateGas execute(final NodeRequests node) {
4545
new org.web3j.protocol.core.methods.request.Transaction(
4646
from,
4747
nonce,
48-
BigInteger.ZERO,
48+
null,
4949
BigInteger.ZERO,
5050
contractAddress,
5151
BigInteger.ZERO,

app/src/main/java/org/hyperledger/besu/services/TransactionSimulationServiceImpl.java

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,13 @@
1515
package org.hyperledger.besu.services;
1616

1717
import static org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams.transactionSimulator;
18-
import static org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams.transactionSimulatorAllowExceedingBalance;
19-
import static org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams.transactionSimulatorAllowExceedingBalanceAndFutureNonce;
20-
import static org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams.transactionSimulatorAllowFutureNonce;
2118

2219
import org.hyperledger.besu.datatypes.Hash;
2320
import org.hyperledger.besu.datatypes.StateOverrideMap;
2421
import org.hyperledger.besu.datatypes.Transaction;
2522
import org.hyperledger.besu.ethereum.chain.Blockchain;
23+
import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams;
24+
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
2625
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
2726
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
2827
import org.hyperledger.besu.ethereum.transaction.CallParameter;
@@ -34,6 +33,7 @@
3433
import org.hyperledger.besu.plugin.data.TransactionSimulationResult;
3534
import org.hyperledger.besu.plugin.services.TransactionSimulationService;
3635

36+
import java.util.EnumSet;
3737
import java.util.Optional;
3838

3939
/** TransactionSimulationServiceImpl */
@@ -67,7 +67,7 @@ public Optional<TransactionSimulationResult> simulate(
6767
final Optional<StateOverrideMap> maybeStateOverrides,
6868
final Hash blockHash,
6969
final OperationTracer operationTracer,
70-
final boolean isAllowExceedingBalance) {
70+
final EnumSet<SimulationParameters> simulationParameters) {
7171

7272
final CallParameter callParameter = CallParameter.fromTransaction(transaction);
7373

@@ -85,9 +85,7 @@ public Optional<TransactionSimulationResult> simulate(
8585
return transactionSimulator
8686
.process(
8787
callParameter,
88-
isAllowExceedingBalance
89-
? transactionSimulatorAllowExceedingBalance()
90-
: transactionSimulator(),
88+
simulationParameters2TransactionValidationParams(simulationParameters),
9189
operationTracer,
9290
maybeBlockHeader.get())
9391
.map(res -> new TransactionSimulationResult(transaction, res.result()));
@@ -99,24 +97,60 @@ public Optional<TransactionSimulationResult> simulate(
9997
final Optional<StateOverrideMap> maybeStateOverrides,
10098
final ProcessableBlockHeader pendingBlockHeader,
10199
final OperationTracer operationTracer,
102-
final boolean isAllowExceedingBalance,
103-
final boolean isAllowFutureNonce) {
100+
final EnumSet<SimulationParameters> simulationParameters) {
104101

105102
final CallParameter callParameter = CallParameter.fromTransaction(transaction);
106103

104+
return simulate(
105+
callParameter,
106+
maybeStateOverrides,
107+
pendingBlockHeader,
108+
operationTracer,
109+
simulationParameters);
110+
}
111+
112+
@Override
113+
public Optional<TransactionSimulationResult> simulate(
114+
final org.hyperledger.besu.datatypes.CallParameter callParameter,
115+
final Optional<StateOverrideMap> maybeStateOverrides,
116+
final ProcessableBlockHeader processableBlockHeader,
117+
final OperationTracer operationTracer,
118+
final EnumSet<SimulationParameters> simulationParameters) {
119+
120+
return simulate(
121+
callParameter,
122+
maybeStateOverrides,
123+
processableBlockHeader,
124+
operationTracer,
125+
simulationParameters2TransactionValidationParams(simulationParameters));
126+
}
127+
128+
private Optional<TransactionSimulationResult> simulate(
129+
final org.hyperledger.besu.datatypes.CallParameter callParameter,
130+
final Optional<StateOverrideMap> maybeStateOverrides,
131+
final ProcessableBlockHeader processableBlockHeader,
132+
final OperationTracer operationTracer,
133+
final TransactionValidationParams txValidationParams) {
134+
107135
return transactionSimulator
108136
.processOnPending(
109137
callParameter,
110138
maybeStateOverrides,
111-
isAllowExceedingBalance
112-
? isAllowFutureNonce
113-
? transactionSimulatorAllowExceedingBalanceAndFutureNonce()
114-
: transactionSimulatorAllowExceedingBalance()
115-
: isAllowFutureNonce
116-
? transactionSimulatorAllowFutureNonce()
117-
: transactionSimulator(),
139+
txValidationParams,
118140
operationTracer,
119-
(org.hyperledger.besu.ethereum.core.ProcessableBlockHeader) pendingBlockHeader)
120-
.map(res -> new TransactionSimulationResult(transaction, res.result()));
141+
(org.hyperledger.besu.ethereum.core.ProcessableBlockHeader) processableBlockHeader)
142+
.map(res -> new TransactionSimulationResult(res.transaction(), res.result()));
143+
}
144+
145+
private static TransactionValidationParams simulationParameters2TransactionValidationParams(
146+
final EnumSet<SimulationParameters> simulationParameters) {
147+
return ImmutableTransactionValidationParams.of(
148+
simulationParameters.contains(SimulationParameters.ALLOW_FUTURE_NONCE),
149+
simulationParameters.contains(SimulationParameters.ALLOW_EXCEEDING_BALANCE),
150+
simulationParameters.contains(SimulationParameters.ALLOW_UNDERPRICED),
151+
false,
152+
false,
153+
true,
154+
true);
121155
}
122156
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/*
2+
* Copyright contributors to Besu.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*
13+
* SPDX-License-Identifier: Apache-2.0
14+
*/
15+
package org.hyperledger.besu.datatypes;
16+
17+
import java.math.BigInteger;
18+
import java.util.List;
19+
import java.util.Optional;
20+
import java.util.OptionalLong;
21+
22+
import org.apache.tuweni.bytes.Bytes;
23+
24+
/**
25+
* Interface defining parameters for Ethereum transaction calls and simulations.
26+
*
27+
* <p>This interface provides access to all the parameters that can be specified when making calls
28+
* to the Ethereum network, including both legacy and EIP-1559 transaction parameters, as well as
29+
* EIP-4844 blob transaction parameters.
30+
*
31+
* <p>All parameters are optional to support various call scenarios where only a subset of
32+
* parameters may be specified.
33+
*/
34+
public interface CallParameter {
35+
36+
/**
37+
* Returns the chain ID for the transaction.
38+
*
39+
* @return an {@link Optional} containing the chain ID, or empty if not specified
40+
*/
41+
Optional<BigInteger> getChainId();
42+
43+
/**
44+
* Returns the sender address of the transaction.
45+
*
46+
* @return an {@link Optional} containing the sender address, or empty if not specified
47+
*/
48+
Optional<Address> getSender();
49+
50+
/**
51+
* Returns the recipient address of the transaction.
52+
*
53+
* @return an {@link Optional} containing the recipient address, or empty if not specified
54+
*/
55+
Optional<Address> getTo();
56+
57+
/**
58+
* Returns the gas limit for the transaction.
59+
*
60+
* @return an {@link OptionalLong} containing the gas limit, or empty if not specified
61+
*/
62+
OptionalLong getGas();
63+
64+
/**
65+
* Returns the maximum priority fee per gas (EIP-1559).
66+
*
67+
* <p>This represents the maximum fee per gas that the sender is willing to pay as a tip to the
68+
* block producer.
69+
*
70+
* @return an {@link Optional} containing the maximum priority fee per gas, or empty if not
71+
* specified
72+
*/
73+
Optional<Wei> getMaxPriorityFeePerGas();
74+
75+
/**
76+
* Returns the maximum fee per gas (EIP-1559).
77+
*
78+
* <p>This represents the maximum total fee per gas that the sender is willing to pay, including
79+
* both the base fee and the priority fee.
80+
*
81+
* @return an {@link Optional} containing the maximum fee per gas, or empty if not specified
82+
*/
83+
Optional<Wei> getMaxFeePerGas();
84+
85+
/**
86+
* Returns the maximum fee per blob gas (EIP-4844).
87+
*
88+
* <p>This parameter is used for blob transactions to specify the maximum fee the sender is
89+
* willing to pay per unit of blob gas.
90+
*
91+
* @return an {@link Optional} containing the maximum fee per blob gas, or empty if not specified
92+
*/
93+
Optional<Wei> getMaxFeePerBlobGas();
94+
95+
/**
96+
* Returns the gas price for legacy transactions.
97+
*
98+
* <p>This parameter is used in pre-EIP-1559 transactions to specify the price per unit of gas.
99+
*
100+
* @return an {@link Optional} containing the gas price, or empty if not specified
101+
*/
102+
Optional<Wei> getGasPrice();
103+
104+
/**
105+
* Returns the value (amount of Ether) to be transferred with the transaction.
106+
*
107+
* @return an {@link Optional} containing the transaction value, or empty if not specified
108+
*/
109+
Optional<Wei> getValue();
110+
111+
/**
112+
* Returns the access list for the transaction (EIP-2930).
113+
*
114+
* <p>The access list specifies addresses and storage keys that the transaction plans to access,
115+
* potentially reducing gas costs.
116+
*
117+
* @return an {@link Optional} containing the access list, or empty if not specified
118+
*/
119+
Optional<List<AccessListEntry>> getAccessList();
120+
121+
/**
122+
* Returns the blob versioned hashes for blob transactions (EIP-4844).
123+
*
124+
* <p>These hashes represent the blob data associated with the transaction and are used for blob
125+
* transaction validation.
126+
*
127+
* @return an {@link Optional} containing the list of blob versioned hashes, or empty if not
128+
* specified
129+
*/
130+
Optional<List<VersionedHash>> getBlobVersionedHashes();
131+
132+
/**
133+
* Returns the nonce of the transaction.
134+
*
135+
* <p>The nonce is a sequence number that prevents replay attacks and ensures transaction ordering
136+
* for a given sender.
137+
*
138+
* @return an {@link OptionalLong} containing the nonce, or empty if not specified
139+
*/
140+
OptionalLong getNonce();
141+
142+
/**
143+
* Returns whether strict validation should be applied.
144+
*
145+
* <p>When strict mode is enabled, additional validation rules may be applied during transaction
146+
* processing or simulation.
147+
*
148+
* @return an {@link Optional} containing the strict flag, or empty if not specified
149+
*/
150+
Optional<Boolean> getStrict();
151+
152+
/**
153+
* Returns the transaction payload (input data).
154+
*
155+
* <p>This contains the data sent along with the transaction, which may include contract method
156+
* calls, constructor parameters, or arbitrary data.
157+
*
158+
* @return an {@link Optional} containing the payload bytes, or empty if not specified
159+
*/
160+
Optional<Bytes> getPayload();
161+
}

ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/frontier/EthCreateAccessListIntegrationTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ public void shouldReturnExpectedValueForEmptyCallParameter() {
158158
public void shouldReturnExpectedValueForTransfer() {
159159
final CallParameter callParameter =
160160
ImmutableCallParameter.builder()
161-
.sender(Address.fromHexString("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"))
161+
.sender(Address.fromHexString("0x658bdf435d810c91414ec09147daa6db62406379"))
162162
.to(Address.fromHexString("0x8888f1f195afa192cfee860698584c030f4c9db1"))
163163
.value(Wei.ZERO)
164164
.build();
@@ -176,7 +176,7 @@ public void shouldReturnExpectedValueForTransfer() {
176176
public void shouldReturnExpectedValueForContractDeploy() {
177177
final CallParameter callParameter =
178178
ImmutableCallParameter.builder()
179-
.sender(Address.fromHexString("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"))
179+
.sender(Address.fromHexString("0x658bdf435d810c91414ec09147daa6db62406379"))
180180
.input(
181181
Bytes.fromHexString(
182182
"0x608060405234801561001057600080fd5b50610157806100206000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633bdab8bf146100515780639ae97baa14610068575b600080fd5b34801561005d57600080fd5b5061006661007f565b005b34801561007457600080fd5b5061007d6100b9565b005b7fa53887c1eed04528e23301f55ad49a91634ef5021aa83a97d07fd16ed71c039a60016040518082815260200191505060405180910390a1565b7fa53887c1eed04528e23301f55ad49a91634ef5021aa83a97d07fd16ed71c039a60026040518082815260200191505060405180910390a17fa53887c1eed04528e23301f55ad49a91634ef5021aa83a97d07fd16ed71c039a60036040518082815260200191505060405180910390a15600a165627a7a7230582010ddaa52e73a98c06dbcd22b234b97206c1d7ed64a7c048e10c2043a3d2309cb0029"))

ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AbstractEstimateGas.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ protected static TransactionValidationParams getTransactionValidationParams(
199199

200200
return isAllowExceedingBalance
201201
? TransactionValidationParams.transactionSimulatorAllowExceedingBalanceAndFutureNonce()
202-
: TransactionValidationParams.transactionSimulatorAllowUnderpricedAndFutureNonce();
202+
: TransactionValidationParams.transactionSimulatorAllowFutureNonce();
203203
}
204204

205205
@VisibleForTesting

ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGasTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ public void shouldNotIgnoreSenderBalanceByDefault() {
440440
modifiedLegacyTransactionCallParameter(
441441
BLOCK_GAS_LIMIT, Wei.ZERO, OptionalLong.empty(), Optional.empty())),
442442
eq(Optional.empty()), // no account overrides
443-
eq(TransactionValidationParams.transactionSimulatorAllowUnderpricedAndFutureNonce()),
443+
eq(TransactionValidationParams.transactionSimulatorAllowFutureNonce()),
444444
any(OperationTracer.class),
445445
eq(pendingBlockHeader));
446446
}
@@ -510,7 +510,7 @@ public void shouldUseTxGasLimitCapWhenLessThatBlockGasLimit() {
510510
.processOnPending(
511511
eq(modifiedEip1559TransactionCallParameter(TX_GAS_LIMIT_CAP, OptionalLong.empty())),
512512
eq(Optional.empty()), // no account overrides
513-
eq(TransactionValidationParams.transactionSimulatorAllowUnderpricedAndFutureNonce()),
513+
eq(TransactionValidationParams.transactionSimulatorAllowFutureNonce()),
514514
any(OperationTracer.class),
515515
eq(pendingBlockHeader));
516516
}

ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/eth/eth_estimateGas_from_contract.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
"response": {
1515
"jsonrpc": "2.0",
1616
"id": 3,
17-
"result": "0x52dd"
17+
"error":{
18+
"code":-32004,
19+
"message":"Upfront cost exceeds account balance (transaction up-front cost 0x1120ed6c5d23b0 exceeds transaction sender account balance 0x140)"}
1820
},
1921
"statusCode": 200
2022
}

ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/eth/eth_estimateGas_from_contract_withBlockTag.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"params": [
77
{
88
"to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
9-
"from": "0x6295ee1b4f6dd65047762f924ecd367c17eabf8f",
9+
"from": "0x8888f1f195afa192cfee860698584c030f4c9db1",
1010
"data": "0x123456"
1111
},
1212
"latest"

ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/eth/eth_estimateGas_insufficientGas.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"params": [
77
{
88
"to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
9-
"from": "0x6295ee1b4f6dd65047762f924ecd367c17eabf8f",
9+
"from": "0x8888f1f195afa192cfee860698584c030f4c9db1",
1010
"gas": "0x1"
1111
}
1212
]

0 commit comments

Comments
 (0)