From 7a37179a61a99bf718af1df38b193cb70299efe8 Mon Sep 17 00:00:00 2001
From: Fabio Di Fabio <fabio.difabio@consensys.net>
Date: Mon, 27 Jan 2025 15:24:58 +0100
Subject: [PATCH] Extend simulate transaction on pending block plugin API

Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
---
 .../TransactionSimulationServiceImpl.java     | 68 +++++++++------
 .../mainnet/TransactionValidationParams.java  | 14 ++++
 .../transaction/TransactionSimulator.java     |  2 +-
 plugin-api/build.gradle                       |  2 +-
 .../TransactionSimulationService.java         | 82 +++++--------------
 5 files changed, 79 insertions(+), 89 deletions(-)

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 d8e12a2be7f5..bac48cb47db6 100644
--- a/besu/src/main/java/org/hyperledger/besu/services/TransactionSimulationServiceImpl.java
+++ b/besu/src/main/java/org/hyperledger/besu/services/TransactionSimulationServiceImpl.java
@@ -26,11 +26,17 @@
 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;
 
 import java.util.Optional;
 
+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;
+
 /** TransactionSimulationServiceImpl */
 @Unstable
 public class TransactionSimulationServiceImpl implements TransactionSimulationService {
@@ -51,50 +57,62 @@ public void init(final Blockchain blockchain, final TransactionSimulator transac
     this.transactionSimulator = transactionSimulator;
   }
 
+  @Override
+  public ProcessableBlockHeader simulatePendingBlockHeader() {
+    return transactionSimulator.simulatePendingBlockHeader();
+  }
+
   @Override
   public Optional<TransactionSimulationResult> simulate(
       final Transaction transaction,
       final Optional<StateOverrideMap> maybeStateOverrides,
-      final Optional<Hash> 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<TransactionSimulationResult> simulate(
+      final Transaction transaction,
+      final Optional<StateOverrideMap> 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 78d6497335ab..c0ca00f8b9c1 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 bf8c966f8c9f..7c089c5aa8be 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<TransactionSimulatorResult> 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 3844f76480ec..a2e9d320e5d2 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 = 'iy+e0qmPmjT+hD0LI39QnlXJzreSUjKgCj1tlNqIUzQ='
 }
 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 508e6eeff7d1..6b60f9c6c24d 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,26 @@ public interface TransactionSimulationService extends BesuService {
   Optional<TransactionSimulationResult> simulate(
       Transaction transaction,
       Optional<StateOverrideMap> stateOverrides,
-      Optional<Hash> 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<TransactionSimulationResult> simulate(
-      final Transaction transaction,
-      final Optional<Hash> 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<TransactionSimulationResult> 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<TransactionSimulationResult> simulate(
-      final Transaction transaction,
-      final Optional<StateOverrideMap> stateOverrides,
-      final Hash blockHash,
-      final OperationTracer operationTracer,
-      final boolean isAllowExceedingBalance) {
-    return simulate(
-        transaction,
-        stateOverrides,
-        Optional.of(blockHash),
-        operationTracer,
-        isAllowExceedingBalance);
-  }
+  Optional<TransactionSimulationResult> simulate(
+      Transaction transaction,
+      Optional<StateOverrideMap> stateOverrides,
+      ProcessableBlockHeader processableBlockHeader,
+      OperationTracer operationTracer,
+      boolean isAllowExceedingBalance, boolean isAllowFutureNonce);
 }