From 55920e002a28bdd553faa5c4f781bf4b029c05f6 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Thu, 21 Nov 2024 08:53:54 +1000 Subject: [PATCH] add Tx simulate method that accepts state override (#7892) * move AccountOverrides into plugin datatypes; add simulate method that accepts this param Signed-off-by: Sally MacFarlane --------- Signed-off-by: Sally MacFarlane --- .../TransactionSimulationServiceImpl.java | 13 ++++ .../besu/datatypes}/AccountOverride.java | 66 ++++++++++++++++--- .../besu/datatypes}/AccountOverrideMap.java | 6 +- .../api/jsonrpc/internal/methods/EthCall.java | 2 +- .../internal/methods/EthEstimateGas.java | 2 +- .../jsonrpc/internal/methods/EthCallTest.java | 4 +- .../internal/methods/EthEstimateGasTest.java | 4 +- .../transaction/TransactionSimulator.java | 6 +- .../transaction/TransactionSimulatorTest.java | 2 +- .../util/AccountOverrideParameterTest.java | 2 + plugin-api/build.gradle | 2 +- .../TransactionSimulationService.java | 18 +++++ 12 files changed, 106 insertions(+), 21 deletions(-) rename {ethereum/core/src/main/java/org/hyperledger/besu/ethereum/util => datatypes/src/main/java/org/hyperledger/besu/datatypes}/AccountOverride.java (76%) rename {ethereum/core/src/main/java/org/hyperledger/besu/ethereum/util => datatypes/src/main/java/org/hyperledger/besu/datatypes}/AccountOverrideMap.java (87%) 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 54cce205a3..4dc411ee0b 100644 --- a/besu/src/main/java/org/hyperledger/besu/services/TransactionSimulationServiceImpl.java +++ b/besu/src/main/java/org/hyperledger/besu/services/TransactionSimulationServiceImpl.java @@ -14,6 +14,7 @@ */ package org.hyperledger.besu.services; +import org.hyperledger.besu.datatypes.AccountOverrideMap; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Transaction; import org.hyperledger.besu.ethereum.chain.Blockchain; @@ -62,6 +63,17 @@ public class TransactionSimulationServiceImpl implements TransactionSimulationSe final Hash blockHash, final OperationTracer operationTracer, final boolean isAllowExceedingBalance) { + return simulate( + transaction, Optional.empty(), blockHash, operationTracer, isAllowExceedingBalance); + } + + @Override + public Optional simulate( + final Transaction transaction, + final Optional maybeAccountOverrides, + final Hash blockHash, + final OperationTracer operationTracer, + final boolean isAllowExceedingBalance) { final CallParameter callParameter = CallParameter.fromTransaction(transaction); @@ -79,6 +91,7 @@ public class TransactionSimulationServiceImpl implements TransactionSimulationSe return transactionSimulator .process( callParameter, + maybeAccountOverrides, isAllowExceedingBalance ? SIMULATOR_ALLOWING_EXCEEDING_BALANCE : TransactionValidationParams.transactionSimulator(), diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/util/AccountOverride.java b/datatypes/src/main/java/org/hyperledger/besu/datatypes/AccountOverride.java similarity index 76% rename from ethereum/core/src/main/java/org/hyperledger/besu/ethereum/util/AccountOverride.java rename to datatypes/src/main/java/org/hyperledger/besu/datatypes/AccountOverride.java index 3bae4af1d8..e414e97eb5 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/util/AccountOverride.java +++ b/datatypes/src/main/java/org/hyperledger/besu/datatypes/AccountOverride.java @@ -12,9 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.ethereum.util; - -import org.hyperledger.besu.datatypes.Wei; +package org.hyperledger.besu.datatypes; import java.util.Map; import java.util.Objects; @@ -26,11 +24,7 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -// similar to AccountDiff -// BUT -// there are more fields that need to be added -// stateDiff -// movePrecompileToAddress +/** Account Override parameter class */ @JsonIgnoreProperties(ignoreUnknown = true) @JsonDeserialize(builder = AccountOverride.Builder.class) public class AccountOverride { @@ -52,22 +46,43 @@ public class AccountOverride { this.stateDiff = stateDiff; } + /** + * Gets the balance override + * + * @return the balance if present + */ public Optional getBalance() { return balance; } + /** + * Gets the nonce override + * + * @return the nonce if present + */ public Optional getNonce() { return nonce; } + /** + * Gets the code override + * + * @return the code if present + */ public Optional getCode() { return code; } + /** + * Gets the state override map + * + * @return the state override map if present + */ public Optional> getStateDiff() { return stateDiff; } + /** Builder class for Account overrides */ public static class Builder { private Optional balance = Optional.empty(); private Optional nonce = Optional.empty(); @@ -77,31 +92,66 @@ public class AccountOverride { /** Default constructor. */ public Builder() {} + /** + * Sets the balance override + * + * @param balance the balance override + * @return the builder + */ public Builder withBalance(final Wei balance) { this.balance = Optional.ofNullable(balance); return this; } + /** + * Sets the nonce override + * + * @param nonce the nonce override + * @return the builder + */ public Builder withNonce(final Long nonce) { this.nonce = Optional.ofNullable(nonce); return this; } + /** + * Sets the code override + * + * @param code the code override + * @return the builder + */ public Builder withCode(final String code) { this.code = Optional.ofNullable(code); return this; } + /** + * Sets the state diff override + * + * @param stateDiff the map of state overrides + * @return the builder + */ public Builder withStateDiff(final Map stateDiff) { this.stateDiff = Optional.ofNullable(stateDiff); return this; } + /** + * build the account override from the builder + * + * @return account override + */ public AccountOverride build() { return new AccountOverride(balance, nonce, code, stateDiff); } } + /** + * utility method to log unknown properties + * + * @param key key for the unrecognized value + * @param value the unrecognized value + */ @JsonAnySetter public void withUnknownProperties(final String key, final Object value) { LOG.debug( diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/util/AccountOverrideMap.java b/datatypes/src/main/java/org/hyperledger/besu/datatypes/AccountOverrideMap.java similarity index 87% rename from ethereum/core/src/main/java/org/hyperledger/besu/ethereum/util/AccountOverrideMap.java rename to datatypes/src/main/java/org/hyperledger/besu/datatypes/AccountOverrideMap.java index 30fc808c9b..20aee46185 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/util/AccountOverrideMap.java +++ b/datatypes/src/main/java/org/hyperledger/besu/datatypes/AccountOverrideMap.java @@ -12,16 +12,16 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.ethereum.util; - -import org.hyperledger.besu.datatypes.Address; +package org.hyperledger.besu.datatypes; import java.util.HashMap; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +/** Map of account overrides, indexed by address */ @JsonIgnoreProperties(ignoreUnknown = true) public class AccountOverrideMap extends HashMap { + /** Default constructor */ public AccountOverrideMap() {} } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCall.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCall.java index 5f736e8d5a..ef5c8c7a6c 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCall.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCall.java @@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods; import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType.BLOCK_NOT_FOUND; import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType.INTERNAL_ERROR; +import org.hyperledger.besu.datatypes.AccountOverrideMap; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcErrorConverter; @@ -41,7 +42,6 @@ import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason; import org.hyperledger.besu.ethereum.transaction.TransactionSimulator; import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult; -import org.hyperledger.besu.ethereum.util.AccountOverrideMap; import org.hyperledger.besu.evm.tracing.OperationTracer; import java.util.Optional; diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGas.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGas.java index ab758dadec..2352a5a8c3 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGas.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGas.java @@ -14,6 +14,7 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods; +import org.hyperledger.besu.datatypes.AccountOverrideMap; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcRequestException; @@ -29,7 +30,6 @@ import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; import org.hyperledger.besu.ethereum.transaction.CallParameter; import org.hyperledger.besu.ethereum.transaction.TransactionSimulator; import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult; -import org.hyperledger.besu.ethereum.util.AccountOverrideMap; import org.hyperledger.besu.evm.tracing.EstimateGasOperationTracer; import java.util.Optional; diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCallTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCallTest.java index d16de7d994..7de6f65aae 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCallTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCallTest.java @@ -27,6 +27,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import org.hyperledger.besu.datatypes.AccountOverride; +import org.hyperledger.besu.datatypes.AccountOverrideMap; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Wei; @@ -51,8 +53,6 @@ import org.hyperledger.besu.ethereum.transaction.CallParameter; import org.hyperledger.besu.ethereum.transaction.PreCloseStateHandler; import org.hyperledger.besu.ethereum.transaction.TransactionSimulator; import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult; -import org.hyperledger.besu.ethereum.util.AccountOverride; -import org.hyperledger.besu.ethereum.util.AccountOverrideMap; import java.util.Optional; diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGasTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGasTest.java index 9f321e0050..7c770f08a2 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGasTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGasTest.java @@ -21,6 +21,8 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import org.hyperledger.besu.datatypes.AccountOverride; +import org.hyperledger.besu.datatypes.AccountOverrideMap; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; @@ -44,8 +46,6 @@ import org.hyperledger.besu.ethereum.transaction.CallParameter; import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason; import org.hyperledger.besu.ethereum.transaction.TransactionSimulator; import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult; -import org.hyperledger.besu.ethereum.util.AccountOverride; -import org.hyperledger.besu.ethereum.util.AccountOverrideMap; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.evm.tracing.OperationTracer; 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 3f35492b2b..1c6140f1d1 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 @@ -19,6 +19,8 @@ import static org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessBlobGasCalcu import org.hyperledger.besu.crypto.SECPSignature; import org.hyperledger.besu.crypto.SignatureAlgorithm; import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; +import org.hyperledger.besu.datatypes.AccountOverride; +import org.hyperledger.besu.datatypes.AccountOverrideMap; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.BlobGas; import org.hyperledger.besu.datatypes.Hash; @@ -34,8 +36,6 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; -import org.hyperledger.besu.ethereum.util.AccountOverride; -import org.hyperledger.besu.ethereum.util.AccountOverrideMap; import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; import org.hyperledger.besu.ethereum.vm.DebugOperationTracer; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; @@ -105,6 +105,7 @@ public class TransactionSimulator { final BlockHeader header = blockchain.getBlockHeader(blockNumber).orElse(null); return process( callParams, + Optional.empty(), transactionValidationParams, operationTracer, (mutableWorldState, transactionSimulatorResult) -> transactionSimulatorResult, @@ -118,6 +119,7 @@ public class TransactionSimulator { final BlockHeader blockHeader) { return process( callParams, + Optional.empty(), transactionValidationParams, operationTracer, (mutableWorldState, transactionSimulatorResult) -> transactionSimulatorResult, 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 0dfa7e924f..e0715e5847 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 @@ -26,6 +26,7 @@ import static org.mockito.Mockito.when; import org.hyperledger.besu.crypto.SECPSignature; import org.hyperledger.besu.crypto.SignatureAlgorithm; import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; +import org.hyperledger.besu.datatypes.AccountOverride; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.BlobsWithCommitments; import org.hyperledger.besu.datatypes.Hash; @@ -48,7 +49,6 @@ import org.hyperledger.besu.ethereum.mainnet.blockhash.BlockHashProcessor; import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult.Status; -import org.hyperledger.besu.ethereum.util.AccountOverride; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.account.MutableAccount; diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/util/AccountOverrideParameterTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/util/AccountOverrideParameterTest.java index 8e7b5c3f0e..1b6d8c1cbe 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/util/AccountOverrideParameterTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/util/AccountOverrideParameterTest.java @@ -18,6 +18,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import org.hyperledger.besu.datatypes.AccountOverride; +import org.hyperledger.besu.datatypes.AccountOverrideMap; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; diff --git a/plugin-api/build.gradle b/plugin-api/build.gradle index 333cac5926..dfdf20bff2 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 = 'wOjR3/uPElPs1GonuLMBWStn1zukFMyy/GfQ9OYChpI=' + knownHash = 'IPpTJJxjDbjW08c3Cm8GbBhULYFy0jq9m3BzliGzrf8=' } 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 dffdb0cfe5..d5fc0a3497 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 @@ -14,6 +14,7 @@ */ package org.hyperledger.besu.plugin.services; +import org.hyperledger.besu.datatypes.AccountOverrideMap; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Transaction; import org.hyperledger.besu.evm.tracing.OperationTracer; @@ -39,4 +40,21 @@ public interface TransactionSimulationService extends BesuService { Hash blockHash, OperationTracer operationTracer, boolean isAllowExceedingBalance); + + /** + * Simulate transaction execution at the block identified by the hash + * + * @param transaction tx + * @param accountOverrides state overrides to apply to this simulation + * @param blockHash the hash of the block + * @param operationTracer the tracer + * @param isAllowExceedingBalance should ignore the sender balance during the simulation? + * @return the result of the simulation + */ + Optional simulate( + Transaction transaction, + Optional accountOverrides, + Hash blockHash, + OperationTracer operationTracer, + boolean isAllowExceedingBalance); }