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 <macfarla.github@gmail.com>

---------

Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com>
pull/7898/head
Sally MacFarlane 4 days ago committed by GitHub
parent c127f9c1d7
commit 55920e002a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 13
      besu/src/main/java/org/hyperledger/besu/services/TransactionSimulationServiceImpl.java
  2. 66
      datatypes/src/main/java/org/hyperledger/besu/datatypes/AccountOverride.java
  3. 6
      datatypes/src/main/java/org/hyperledger/besu/datatypes/AccountOverrideMap.java
  4. 2
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCall.java
  5. 2
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGas.java
  6. 4
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCallTest.java
  7. 4
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGasTest.java
  8. 6
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java
  9. 2
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java
  10. 2
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/util/AccountOverrideParameterTest.java
  11. 2
      plugin-api/build.gradle
  12. 18
      plugin-api/src/main/java/org/hyperledger/besu/plugin/services/TransactionSimulationService.java

@ -14,6 +14,7 @@
*/ */
package org.hyperledger.besu.services; package org.hyperledger.besu.services;
import org.hyperledger.besu.datatypes.AccountOverrideMap;
import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Transaction; import org.hyperledger.besu.datatypes.Transaction;
import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.chain.Blockchain;
@ -62,6 +63,17 @@ public class TransactionSimulationServiceImpl implements TransactionSimulationSe
final Hash blockHash, final Hash blockHash,
final OperationTracer operationTracer, final OperationTracer operationTracer,
final boolean isAllowExceedingBalance) { final boolean isAllowExceedingBalance) {
return simulate(
transaction, Optional.empty(), blockHash, operationTracer, isAllowExceedingBalance);
}
@Override
public Optional<TransactionSimulationResult> simulate(
final Transaction transaction,
final Optional<AccountOverrideMap> maybeAccountOverrides,
final Hash blockHash,
final OperationTracer operationTracer,
final boolean isAllowExceedingBalance) {
final CallParameter callParameter = CallParameter.fromTransaction(transaction); final CallParameter callParameter = CallParameter.fromTransaction(transaction);
@ -79,6 +91,7 @@ public class TransactionSimulationServiceImpl implements TransactionSimulationSe
return transactionSimulator return transactionSimulator
.process( .process(
callParameter, callParameter,
maybeAccountOverrides,
isAllowExceedingBalance isAllowExceedingBalance
? SIMULATOR_ALLOWING_EXCEEDING_BALANCE ? SIMULATOR_ALLOWING_EXCEEDING_BALANCE
: TransactionValidationParams.transactionSimulator(), : TransactionValidationParams.transactionSimulator(),

@ -12,9 +12,7 @@
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
package org.hyperledger.besu.ethereum.util; package org.hyperledger.besu.datatypes;
import org.hyperledger.besu.datatypes.Wei;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@ -26,11 +24,7 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
// similar to AccountDiff /** Account Override parameter class */
// BUT
// there are more fields that need to be added
// stateDiff
// movePrecompileToAddress
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@JsonDeserialize(builder = AccountOverride.Builder.class) @JsonDeserialize(builder = AccountOverride.Builder.class)
public class AccountOverride { public class AccountOverride {
@ -52,22 +46,43 @@ public class AccountOverride {
this.stateDiff = stateDiff; this.stateDiff = stateDiff;
} }
/**
* Gets the balance override
*
* @return the balance if present
*/
public Optional<Wei> getBalance() { public Optional<Wei> getBalance() {
return balance; return balance;
} }
/**
* Gets the nonce override
*
* @return the nonce if present
*/
public Optional<Long> getNonce() { public Optional<Long> getNonce() {
return nonce; return nonce;
} }
/**
* Gets the code override
*
* @return the code if present
*/
public Optional<String> getCode() { public Optional<String> getCode() {
return code; return code;
} }
/**
* Gets the state override map
*
* @return the state override map if present
*/
public Optional<Map<String, String>> getStateDiff() { public Optional<Map<String, String>> getStateDiff() {
return stateDiff; return stateDiff;
} }
/** Builder class for Account overrides */
public static class Builder { public static class Builder {
private Optional<Wei> balance = Optional.empty(); private Optional<Wei> balance = Optional.empty();
private Optional<Long> nonce = Optional.empty(); private Optional<Long> nonce = Optional.empty();
@ -77,31 +92,66 @@ public class AccountOverride {
/** Default constructor. */ /** Default constructor. */
public Builder() {} public Builder() {}
/**
* Sets the balance override
*
* @param balance the balance override
* @return the builder
*/
public Builder withBalance(final Wei balance) { public Builder withBalance(final Wei balance) {
this.balance = Optional.ofNullable(balance); this.balance = Optional.ofNullable(balance);
return this; return this;
} }
/**
* Sets the nonce override
*
* @param nonce the nonce override
* @return the builder
*/
public Builder withNonce(final Long nonce) { public Builder withNonce(final Long nonce) {
this.nonce = Optional.ofNullable(nonce); this.nonce = Optional.ofNullable(nonce);
return this; return this;
} }
/**
* Sets the code override
*
* @param code the code override
* @return the builder
*/
public Builder withCode(final String code) { public Builder withCode(final String code) {
this.code = Optional.ofNullable(code); this.code = Optional.ofNullable(code);
return this; return this;
} }
/**
* Sets the state diff override
*
* @param stateDiff the map of state overrides
* @return the builder
*/
public Builder withStateDiff(final Map<String, String> stateDiff) { public Builder withStateDiff(final Map<String, String> stateDiff) {
this.stateDiff = Optional.ofNullable(stateDiff); this.stateDiff = Optional.ofNullable(stateDiff);
return this; return this;
} }
/**
* build the account override from the builder
*
* @return account override
*/
public AccountOverride build() { public AccountOverride build() {
return new AccountOverride(balance, nonce, code, stateDiff); 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 @JsonAnySetter
public void withUnknownProperties(final String key, final Object value) { public void withUnknownProperties(final String key, final Object value) {
LOG.debug( LOG.debug(

@ -12,16 +12,16 @@
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
package org.hyperledger.besu.ethereum.util; package org.hyperledger.besu.datatypes;
import org.hyperledger.besu.datatypes.Address;
import java.util.HashMap; import java.util.HashMap;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/** Map of account overrides, indexed by address */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class AccountOverrideMap extends HashMap<Address, AccountOverride> { public class AccountOverrideMap extends HashMap<Address, AccountOverride> {
/** Default constructor */
public AccountOverrideMap() {} public AccountOverrideMap() {}
} }

@ -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.BLOCK_NOT_FOUND;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType.INTERNAL_ERROR; 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.Hash;
import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcErrorConverter; 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.TransactionInvalidReason;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator; import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult; import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult;
import org.hyperledger.besu.ethereum.util.AccountOverrideMap;
import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.evm.tracing.OperationTracer;
import java.util.Optional; import java.util.Optional;

@ -14,6 +14,7 @@
*/ */
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods; 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.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcRequestException; 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.CallParameter;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator; import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult; import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult;
import org.hyperledger.besu.ethereum.util.AccountOverrideMap;
import org.hyperledger.besu.evm.tracing.EstimateGasOperationTracer; import org.hyperledger.besu.evm.tracing.EstimateGasOperationTracer;
import java.util.Optional; import java.util.Optional;

@ -27,6 +27,8 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when; 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.Address;
import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei; 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.PreCloseStateHandler;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator; import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult; 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; import java.util.Optional;

@ -21,6 +21,8 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; 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.Address;
import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; 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.TransactionInvalidReason;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator; import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult; 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.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.evm.tracing.OperationTracer;

@ -19,6 +19,8 @@ import static org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessBlobGasCalcu
import org.hyperledger.besu.crypto.SECPSignature; import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithm; import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; 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.Address;
import org.hyperledger.besu.datatypes.BlobGas; import org.hyperledger.besu.datatypes.BlobGas;
import org.hyperledger.besu.datatypes.Hash; 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.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; 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.CachingBlockHashLookup;
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer; import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
@ -105,6 +105,7 @@ public class TransactionSimulator {
final BlockHeader header = blockchain.getBlockHeader(blockNumber).orElse(null); final BlockHeader header = blockchain.getBlockHeader(blockNumber).orElse(null);
return process( return process(
callParams, callParams,
Optional.empty(),
transactionValidationParams, transactionValidationParams,
operationTracer, operationTracer,
(mutableWorldState, transactionSimulatorResult) -> transactionSimulatorResult, (mutableWorldState, transactionSimulatorResult) -> transactionSimulatorResult,
@ -118,6 +119,7 @@ public class TransactionSimulator {
final BlockHeader blockHeader) { final BlockHeader blockHeader) {
return process( return process(
callParams, callParams,
Optional.empty(),
transactionValidationParams, transactionValidationParams,
operationTracer, operationTracer,
(mutableWorldState, transactionSimulatorResult) -> transactionSimulatorResult, (mutableWorldState, transactionSimulatorResult) -> transactionSimulatorResult,

@ -26,6 +26,7 @@ import static org.mockito.Mockito.when;
import org.hyperledger.besu.crypto.SECPSignature; import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithm; import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.AccountOverride;
import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.BlobsWithCommitments; import org.hyperledger.besu.datatypes.BlobsWithCommitments;
import org.hyperledger.besu.datatypes.Hash; 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.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult.Status; 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.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.account.MutableAccount;

@ -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.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue; 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.Address;
import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;

@ -71,7 +71,7 @@ Calculated : ${currentHash}
tasks.register('checkAPIChanges', FileStateChecker) { tasks.register('checkAPIChanges', FileStateChecker) {
description = "Checks that the API for the Plugin-API project does not change without deliberate thought" description = "Checks that the API for the Plugin-API project does not change without deliberate thought"
files = sourceSets.main.allJava.files files = sourceSets.main.allJava.files
knownHash = 'wOjR3/uPElPs1GonuLMBWStn1zukFMyy/GfQ9OYChpI=' knownHash = 'IPpTJJxjDbjW08c3Cm8GbBhULYFy0jq9m3BzliGzrf8='
} }
check.dependsOn('checkAPIChanges') check.dependsOn('checkAPIChanges')

@ -14,6 +14,7 @@
*/ */
package org.hyperledger.besu.plugin.services; package org.hyperledger.besu.plugin.services;
import org.hyperledger.besu.datatypes.AccountOverrideMap;
import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Transaction; import org.hyperledger.besu.datatypes.Transaction;
import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.evm.tracing.OperationTracer;
@ -39,4 +40,21 @@ public interface TransactionSimulationService extends BesuService {
Hash blockHash, Hash blockHash,
OperationTracer operationTracer, OperationTracer operationTracer,
boolean isAllowExceedingBalance); 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<TransactionSimulationResult> simulate(
Transaction transaction,
Optional<AccountOverrideMap> accountOverrides,
Hash blockHash,
OperationTracer operationTracer,
boolean isAllowExceedingBalance);
} }

Loading…
Cancel
Save