From b5d507d659cdfdbcf9d6effdb228fb222a4b4a10 Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Tue, 28 Jan 2025 19:03:59 +0100 Subject: [PATCH] Extend simulate transaction on pending block plugin API (#8174) Signed-off-by: Fabio Di Fabio --- CHANGELOG.md | 2 + .../TransactionSimulationServiceImpl.java | 74 +++++++++++------ .../mainnet/TransactionValidationParams.java | 14 ++++ .../transaction/TransactionSimulator.java | 2 +- plugin-api/build.gradle | 2 +- .../TransactionSimulationService.java | 83 +++++-------------- 6 files changed, 87 insertions(+), 90 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c654200034..5d5fcdbbb94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ ### Upcoming Breaking Changes ### Additions and Improvements - Adds timestamps to enable Prague hardfork on Sepolia and Holesky test networks. [#8163](https://github.com/hyperledger/besu/pull/8163) +- Extend simulate transaction on pending block plugin API [#8174](https://github.com/hyperledger/besu/pull/8174) + ### Bug fixes diff --git a/besu/src/main/java/org/hyperledger/besu/services/TransactionSimulationServiceImpl.java b/besu/src/main/java/org/hyperledger/besu/services/TransactionSimulationServiceImpl.java index d8e12a2be7f..48c85875be8 100644 --- a/besu/src/main/java/org/hyperledger/besu/services/TransactionSimulationServiceImpl.java +++ b/besu/src/main/java/org/hyperledger/besu/services/TransactionSimulationServiceImpl.java @@ -14,11 +14,15 @@ */ package org.hyperledger.besu.services; +import static org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams.transactionSimulator; +import static org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams.transactionSimulatorAllowExceedingBalance; +import static org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams.transactionSimulatorAllowExceedingBalanceAndFutureNonce; +import static org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams.transactionSimulatorAllowFutureNonce; + import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.StateOverrideMap; import org.hyperledger.besu.datatypes.Transaction; import org.hyperledger.besu.ethereum.chain.Blockchain; -import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.ethereum.transaction.CallParameter; @@ -26,6 +30,7 @@ import org.hyperledger.besu.ethereum.transaction.TransactionSimulator; import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.plugin.Unstable; +import org.hyperledger.besu.plugin.data.ProcessableBlockHeader; import org.hyperledger.besu.plugin.data.TransactionSimulationResult; import org.hyperledger.besu.plugin.services.TransactionSimulationService; @@ -51,50 +56,67 @@ public void init(final Blockchain blockchain, final TransactionSimulator transac this.transactionSimulator = transactionSimulator; } + @Override + public ProcessableBlockHeader simulatePendingBlockHeader() { + return transactionSimulator.simulatePendingBlockHeader(); + } + @Override public Optional simulate( final Transaction transaction, final Optional maybeStateOverrides, - final Optional maybeBlockHash, + final Hash blockHash, final OperationTracer operationTracer, final boolean isAllowExceedingBalance) { final CallParameter callParameter = CallParameter.fromTransaction(transaction); - if (maybeBlockHash.isPresent()) { - final Hash blockHash = maybeBlockHash.get(); + final var maybeBlockHeader = + blockchain.getBlockHeader(blockHash).or(() -> blockchain.getBlockHeaderSafe(blockHash)); - final var maybeBlockHeader = - blockchain.getBlockHeader(blockHash).or(() -> blockchain.getBlockHeaderSafe(blockHash)); + if (maybeBlockHeader.isEmpty()) { + return Optional.of( + new TransactionSimulationResult( + transaction, + TransactionProcessingResult.invalid( + ValidationResult.invalid(TransactionInvalidReason.BLOCK_NOT_FOUND)))); + } - if (maybeBlockHeader.isEmpty()) { - return Optional.of( - new TransactionSimulationResult( - transaction, - TransactionProcessingResult.invalid( - ValidationResult.invalid(TransactionInvalidReason.BLOCK_NOT_FOUND)))); - } + return transactionSimulator + .process( + callParameter, + isAllowExceedingBalance + ? transactionSimulatorAllowExceedingBalance() + : transactionSimulator(), + operationTracer, + maybeBlockHeader.get()) + .map(res -> new TransactionSimulationResult(transaction, res.result())); + } - return transactionSimulator - .process( - callParameter, - isAllowExceedingBalance - ? TransactionValidationParams.transactionSimulatorAllowExceedingBalance() - : TransactionValidationParams.transactionSimulator(), - operationTracer, - maybeBlockHeader.get()) - .map(res -> new TransactionSimulationResult(transaction, res.result())); - } + @Override + public Optional simulate( + final Transaction transaction, + final Optional maybeStateOverrides, + final ProcessableBlockHeader pendingBlockHeader, + final OperationTracer operationTracer, + final boolean isAllowExceedingBalance, + final boolean isAllowFutureNonce) { + + final CallParameter callParameter = CallParameter.fromTransaction(transaction); return transactionSimulator .processOnPending( callParameter, maybeStateOverrides, isAllowExceedingBalance - ? TransactionValidationParams.transactionSimulatorAllowExceedingBalance() - : TransactionValidationParams.transactionSimulator(), + ? isAllowFutureNonce + ? transactionSimulatorAllowExceedingBalanceAndFutureNonce() + : transactionSimulatorAllowExceedingBalance() + : isAllowFutureNonce + ? transactionSimulatorAllowFutureNonce() + : transactionSimulator(), operationTracer, - transactionSimulator.simulatePendingBlockHeader()) + (org.hyperledger.besu.ethereum.core.ProcessableBlockHeader) pendingBlockHeader) .map(res -> new TransactionSimulationResult(transaction, res.result())); } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidationParams.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidationParams.java index 78d6497335a..c0ca00f8b9c 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidationParams.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidationParams.java @@ -35,9 +35,15 @@ public interface TransactionValidationParams { TransactionValidationParams transactionSimulatorParams = ImmutableTransactionValidationParams.of(false, false, false, false, false, true); + TransactionValidationParams transactionSimulatorParamsAllowFutureNonce = + ImmutableTransactionValidationParams.of(true, false, false, false, false, true); + TransactionValidationParams transactionSimulatorAllowExceedingBalanceParams = ImmutableTransactionValidationParams.of(false, true, false, false, false, true); + TransactionValidationParams transactionSimulatorAllowExceedingBalanceAndFutureNonceParams = + ImmutableTransactionValidationParams.of(true, true, false, false, false, true); + @Value.Default default boolean isAllowFutureNonce() { return false; @@ -72,10 +78,18 @@ static TransactionValidationParams transactionSimulator() { return transactionSimulatorParams; } + static TransactionValidationParams transactionSimulatorAllowFutureNonce() { + return transactionSimulatorParamsAllowFutureNonce; + } + static TransactionValidationParams transactionSimulatorAllowExceedingBalance() { return transactionSimulatorAllowExceedingBalanceParams; } + static TransactionValidationParams transactionSimulatorAllowExceedingBalanceAndFutureNonce() { + return transactionSimulatorAllowExceedingBalanceAndFutureNonceParams; + } + static TransactionValidationParams processingBlock() { return processingBlockParams; } 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 bf8c966f8c9..7c089c5aa8b 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 @@ -174,7 +174,7 @@ public Optional processOnPending( operationTracer, pendingBlockHeader, updater, - Address.ZERO); + pendingBlockHeader.getCoinbase()); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/plugin-api/build.gradle b/plugin-api/build.gradle index 3844f76480e..5dcbf8f1a48 100644 --- a/plugin-api/build.gradle +++ b/plugin-api/build.gradle @@ -71,7 +71,7 @@ Calculated : ${currentHash} tasks.register('checkAPIChanges', FileStateChecker) { description = "Checks that the API for the Plugin-API project does not change without deliberate thought" files = sourceSets.main.allJava.files - knownHash = 'E2b/W+IKnNxo6L7cHuijBMBUewHHRrkQ8dEVlcql5KE=' + knownHash = 'gZIQWjSStog+2qxsbGPPp0OwpYjl8QDM5pv9QWdtADA=' } check.dependsOn('checkAPIChanges') diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/TransactionSimulationService.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/TransactionSimulationService.java index 508e6eeff7d..14eaf11408d 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/TransactionSimulationService.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/TransactionSimulationService.java @@ -19,6 +19,7 @@ import org.hyperledger.besu.datatypes.Transaction; import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.plugin.Unstable; +import org.hyperledger.besu.plugin.data.ProcessableBlockHeader; import org.hyperledger.besu.plugin.data.TransactionSimulationResult; import java.util.Optional; @@ -27,13 +28,21 @@ @Unstable public interface TransactionSimulationService extends BesuService { + /** + * Return a simulation of what could be current pending block, it can also be passed to {@link + * #simulate(Transaction, Optional, ProcessableBlockHeader, OperationTracer, boolean, boolean)} + * + * @return the simulated pending block header + */ + ProcessableBlockHeader simulatePendingBlockHeader(); + /** * Simulate transaction execution at the block identified by the hash if present, otherwise on the * pending block, with optional state overrides that can be applied before the simulation. * * @param transaction tx * @param stateOverrides state overrides to apply to this simulation - * @param maybeBlockHash optional hash of the block, empty to simulate on pending block + * @param blockHash hash of the block * @param operationTracer the tracer * @param isAllowExceedingBalance should ignore the sender balance during the simulation? * @return the result of the simulation @@ -41,77 +50,27 @@ public interface TransactionSimulationService extends BesuService { Optional simulate( Transaction transaction, Optional stateOverrides, - Optional maybeBlockHash, + Hash blockHash, OperationTracer operationTracer, boolean isAllowExceedingBalance); /** * Simulate transaction execution at the block identified by the hash if present, otherwise on the - * pending block - * - * @param transaction tx - * @param maybeBlockHash optional hash of the block, empty to simulate on pending block - * @param operationTracer the tracer - * @param isAllowExceedingBalance should ignore the sender balance during the simulation? - * @return the result of the simulation - */ - default Optional simulate( - final Transaction transaction, - final Optional maybeBlockHash, - final OperationTracer operationTracer, - final boolean isAllowExceedingBalance) { - return simulate( - transaction, Optional.empty(), maybeBlockHash, operationTracer, isAllowExceedingBalance); - } - - /** - * Simulate transaction execution at the block identified by the hash - * - * @param transaction tx - * @param blockHash then hash of the block - * @param operationTracer the tracer - * @param isAllowExceedingBalance should ignore the sender balance during the simulation? - * @return the result of the simulation - * @deprecated use {@link #simulate(Transaction, Optional, OperationTracer, boolean)} - */ - @Deprecated(since = "24.12", forRemoval = true) - default Optional simulate( - final Transaction transaction, - final Hash blockHash, - final OperationTracer operationTracer, - final boolean isAllowExceedingBalance) { - return simulate( - transaction, - Optional.empty(), - Optional.of(blockHash), - operationTracer, - isAllowExceedingBalance); - } - - /** - * Simulate transaction execution at the block identified by the hash, with optional state - * overrides that can be applied before the simulation. + * pending block, with optional state overrides that can be applied before the simulation. * * @param transaction tx * @param stateOverrides state overrides to apply to this simulation - * @param blockHash the hash of the block + * @param processableBlockHeader block header to simulate on pending block * @param operationTracer the tracer * @param isAllowExceedingBalance should ignore the sender balance during the simulation? + * @param isAllowFutureNonce should skip strict check on sequential nonce? * @return the result of the simulation - * @deprecated use {@link #simulate(Transaction, Optional, Optional, OperationTracer, boolean)} */ - @Deprecated(since = "24.12", forRemoval = true) - default Optional simulate( - final Transaction transaction, - final Optional stateOverrides, - final Hash blockHash, - final OperationTracer operationTracer, - final boolean isAllowExceedingBalance) { - return simulate( - transaction, - stateOverrides, - Optional.of(blockHash), - operationTracer, - isAllowExceedingBalance); - } + Optional simulate( + Transaction transaction, + Optional stateOverrides, + ProcessableBlockHeader processableBlockHeader, + OperationTracer operationTracer, + boolean isAllowExceedingBalance, + boolean isAllowFutureNonce); }