From a3592a71b1be23a18f45a0b5ad3d0972acb8b401 Mon Sep 17 00:00:00 2001 From: Gabriel-Trintinalia Date: Fri, 6 Dec 2024 19:07:50 +1100 Subject: [PATCH] Allow frontier simulation when Base Fee is present (#7965) Signed-off-by: Gabriel-Trintinalia --- CHANGELOG.md | 1 + .../london/EthEstimateGasIntegrationTest.java | 1 + .../transaction/TransactionSimulator.java | 61 +++++++++++++++---- .../transaction/TransactionSimulatorTest.java | 39 ++++++++++++ 4 files changed, 89 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36ff4aa9bba..6aca416dbfc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ ### Additions and Improvements ### Bug fixes +- Correct default parameters for frontier transactions in `eth_call` and `eth_estimateGas` [#7965](https://github.com/hyperledger/besu/pull/7965) ## 24.12.0 diff --git a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/london/EthEstimateGasIntegrationTest.java b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/london/EthEstimateGasIntegrationTest.java index 5fde9ccecbd..7c8cb8a08d7 100644 --- a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/london/EthEstimateGasIntegrationTest.java +++ b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/london/EthEstimateGasIntegrationTest.java @@ -137,6 +137,7 @@ public void shouldReturnErrorWithInvalidChainId() { .withChainId(BLOCKCHAIN.getChainId().add(BigInteger.ONE)) .withFrom(Address.fromHexString("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")) .withTo(Address.fromHexString("0x8888f1f195afa192cfee860698584c030f4c9db1")) + .withMaxFeePerGas(Wei.ONE) .withValue(Wei.ONE) .build(); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java index 1c6140f1d1f..e4db6aafb15 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java @@ -296,9 +296,6 @@ public Optional processWithWorldUpdater( final long simulationGasCap = calculateSimulationGasCap(callParams.getGasLimit(), blockHeaderToProcess.getGasLimit()); - final Wei value = callParams.getValue() != null ? callParams.getValue() : Wei.ZERO; - final Bytes payload = callParams.getPayload() != null ? callParams.getPayload() : Bytes.EMPTY; - final MainnetTransactionProcessor transactionProcessor = protocolSchedule.getByBlockHeader(blockHeaderToProcess).getTransactionProcessor(); @@ -322,8 +319,6 @@ public Optional processWithWorldUpdater( senderAddress, nonce, simulationGasCap, - value, - payload, blobGasPrice); if (maybeTransaction.isEmpty()) { return Optional.empty(); @@ -404,9 +399,11 @@ private Optional buildTransaction( final Address senderAddress, final long nonce, final long gasLimit, - final Wei value, - final Bytes payload, final Wei blobGasPrice) { + + final Wei value = callParams.getValue() != null ? callParams.getValue() : Wei.ZERO; + final Bytes payload = callParams.getPayload() != null ? callParams.getPayload() : Bytes.EMPTY; + final Transaction.Builder transactionBuilder = Transaction.builder() .nonce(nonce) @@ -437,18 +434,21 @@ private Optional buildTransaction( maxPriorityFeePerGas = callParams.getMaxPriorityFeePerGas().orElse(gasPrice); maxFeePerBlobGas = callParams.getMaxFeePerBlobGas().orElse(blobGasPrice); } - if (header.getBaseFee().isEmpty()) { + + if (shouldSetGasPrice(callParams, header)) { transactionBuilder.gasPrice(gasPrice); - } else if (protocolSchedule.getChainId().isPresent()) { + } + + if (shouldSetMaxFeePerGas(callParams, header)) { transactionBuilder.maxFeePerGas(maxFeePerGas).maxPriorityFeePerGas(maxPriorityFeePerGas); - } else { - return Optional.empty(); } - transactionBuilder.guessType(); - if (transactionBuilder.getTransactionType().supportsBlob()) { + if (shouldSetBlobGasPrice(callParams)) { transactionBuilder.maxFeePerBlobGas(maxFeePerBlobGas); } + + transactionBuilder.guessType(); + if (transactionBuilder.getTransactionType().requiresChainId()) { callParams .getChainId() @@ -489,4 +489,39 @@ public Optional doesAddressExist( return Optional.of(worldState.get(address) != null); } + + private boolean shouldSetGasPrice(final CallParameter callParams, final BlockHeader header) { + if (header.getBaseFee().isEmpty()) { + return true; + } + + // if maxPriorityFeePerGas and maxFeePerGas are not set, use gasPrice + return callParams.getMaxPriorityFeePerGas().isEmpty() && callParams.getMaxFeePerGas().isEmpty(); + } + + private boolean shouldSetMaxFeePerGas(final CallParameter callParams, final BlockHeader header) { + if (protocolSchedule.getChainId().isEmpty()) { + return false; + } + + if (header.getBaseFee().isEmpty()) { + return false; + } + + if (shouldSetBlobGasPrice(callParams)) { + return true; + } + + // only set maxFeePerGas and maxPriorityFeePerGas if they are present, otherwise transaction + // will be considered EIP-1559 transaction even if the simulation is for a legacy transaction + return callParams.getMaxPriorityFeePerGas().isPresent() + || callParams.getMaxFeePerGas().isPresent(); + } + + private boolean shouldSetBlobGasPrice(final CallParameter callParams) { + if (protocolSchedule.getChainId().isEmpty()) { + return false; + } + return callParams.getBlobVersionedHashes().isPresent(); + } } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java index e0715e58477..8406d1e4c31 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java @@ -912,4 +912,43 @@ private CallParameter blobTransactionCallParameter( Optional.of(maxFeePerBlobGas), Optional.of(bwc.getVersionedHashes())); } + + @Test + public void shouldSimulateLegacyTransactionWhenBaseFeeNotZero() { + // tests that the transaction simulator will simulate a legacy transaction when the base fee is + // not zero + // and the transaction is a legacy transaction + + final CallParameter callParameter = legacyTransactionCallParameter(); + + final BlockHeader blockHeader = + blockHeaderTestFixture + .number(1L) + .stateRoot(Hash.ZERO) + .baseFeePerGas(Wei.of(7)) + .buildHeader(); + + mockBlockchainForBlockHeader(blockHeader); + mockWorldStateForAccount(blockHeader, callParameter.getFrom(), 1L); + + final Transaction expectedTransaction = + Transaction.builder() + .type(TransactionType.FRONTIER) + .nonce(1L) + .gasPrice(callParameter.getGasPrice()) + .gasLimit(blockHeader.getGasLimit()) + .to(callParameter.getTo()) + .sender(callParameter.getFrom()) + .value(callParameter.getValue()) + .payload(callParameter.getPayload()) + .signature(FAKE_SIGNATURE) + .build(); + mockProcessorStatusForTransaction(expectedTransaction, Status.SUCCESSFUL); + + final Optional result = + transactionSimulator.process(callParameter, 1L); + + verifyTransactionWasProcessed(expectedTransaction); + assertThat(result.get().isSuccessful()).isTrue(); + } }