eth_call overrides (#7801)

* add state and account overrides

Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com>

---------

Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com>
Co-authored-by: Fabio Di Fabio <fabio.difabio@consensys.net>
pull/7800/merge
Sally MacFarlane 3 days ago committed by GitHub
parent 2384c09a82
commit 8b7ca20176
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 20
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCall.java
  3. 7
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceCallMany.java
  4. 81
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCallTest.java
  5. 24
      ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/eth/eth_call_overrides_empty.json
  6. 32
      ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/eth/eth_call_stateOverride.json
  7. 34
      ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/eth/eth_call_stateOverride_insufficientBalance.json
  8. 66
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java
  9. 147
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/util/AccountOverride.java
  10. 27
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/util/AccountOverrideMap.java
  11. 41
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java
  12. 188
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/util/AccountOverrideParameterTest.java

@ -15,6 +15,7 @@
- Update Java dependencies [#7786](https://github.com/hyperledger/besu/pull/7786) - Update Java dependencies [#7786](https://github.com/hyperledger/besu/pull/7786)
- Add a method to get all the transaction in the pool, to the `TransactionPoolService`, to easily access the transaction pool content from plugins [#7813](https://github.com/hyperledger/besu/pull/7813) - Add a method to get all the transaction in the pool, to the `TransactionPoolService`, to easily access the transaction pool content from plugins [#7813](https://github.com/hyperledger/besu/pull/7813)
- Add a method to check if a metric category is enabled to the plugin API [#7832](https://github.com/hyperledger/besu/pull/7832) - Add a method to check if a metric category is enabled to the plugin API [#7832](https://github.com/hyperledger/besu/pull/7832)
- Add account and state overrides to `eth_call` and `eth_estimateGas` [#7801](https://github.com/hyperledger/besu/pull/7801)
### Bug fixes ### Bug fixes
- Fix registering new metric categories from plugins [#7825](https://github.com/hyperledger/besu/pull/7825) - Fix registering new metric categories from plugins [#7825](https://github.com/hyperledger/besu/pull/7825)

@ -23,6 +23,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcErrorConverter;
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.InvalidJsonRpcParameters; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcParameters;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcRequestException;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameterOrBlockHash; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameterOrBlockHash;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonCallParameter; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonCallParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter.JsonRpcParameterException; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter.JsonRpcParameterException;
@ -40,8 +41,13 @@ 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 com.google.common.annotations.VisibleForTesting;
public class EthCall extends AbstractBlockParameterOrBlockHashMethod { public class EthCall extends AbstractBlockParameterOrBlockHashMethod {
private final TransactionSimulator transactionSimulator; private final TransactionSimulator transactionSimulator;
@ -81,10 +87,13 @@ public class EthCall extends AbstractBlockParameterOrBlockHashMethod {
protected Object resultByBlockHeader( protected Object resultByBlockHeader(
final JsonRpcRequestContext request, final BlockHeader header) { final JsonRpcRequestContext request, final BlockHeader header) {
JsonCallParameter callParams = JsonCallParameterUtil.validateAndGetCallParams(request); JsonCallParameter callParams = JsonCallParameterUtil.validateAndGetCallParams(request);
Optional<AccountOverrideMap> maybeStateOverrides = getAddressAccountOverrideMap(request);
// TODO implement for block overrides
return transactionSimulator return transactionSimulator
.process( .process(
callParams, callParams,
maybeStateOverrides,
buildTransactionValidationParams(header, callParams), buildTransactionValidationParams(header, callParams),
OperationTracer.NO_TRACING, OperationTracer.NO_TRACING,
(mutableWorldState, transactionSimulatorResult) -> (mutableWorldState, transactionSimulatorResult) ->
@ -108,6 +117,17 @@ public class EthCall extends AbstractBlockParameterOrBlockHashMethod {
.orElse(errorResponse(request, INTERNAL_ERROR)); .orElse(errorResponse(request, INTERNAL_ERROR));
} }
@VisibleForTesting
protected Optional<AccountOverrideMap> getAddressAccountOverrideMap(
final JsonRpcRequestContext request) {
try {
return request.getOptionalParameter(2, AccountOverrideMap.class);
} catch (JsonRpcParameterException e) {
throw new InvalidJsonRpcRequestException(
"Invalid account overrides parameter (index 2)", RpcErrorType.INVALID_CALL_PARAMS, e);
}
}
@Override @Override
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
return (JsonRpcResponse) handleParamTypes(requestContext); return (JsonRpcResponse) handleParamTypes(requestContext);

@ -160,7 +160,12 @@ public class TraceCallMany extends TraceCall implements JsonRpcMethod {
new DebugOperationTracer(buildTraceOptions(traceTypes), false); new DebugOperationTracer(buildTraceOptions(traceTypes), false);
final Optional<TransactionSimulatorResult> maybeSimulatorResult = final Optional<TransactionSimulatorResult> maybeSimulatorResult =
transactionSimulator.processWithWorldUpdater( transactionSimulator.processWithWorldUpdater(
callParameter, buildTransactionValidationParams(), tracer, header, worldUpdater); callParameter,
Optional.empty(),
buildTransactionValidationParams(),
tracer,
header,
worldUpdater);
LOG.trace("Executing {} call for transaction {}", traceTypeParameter, callParameter); LOG.trace("Executing {} call for transaction {}", traceTypeParameter, callParameter);
if (maybeSimulatorResult.isEmpty()) { if (maybeSimulatorResult.isEmpty()) {

@ -51,6 +51,8 @@ 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;
@ -92,6 +94,33 @@ public class EthCallTest {
assertThat(method.getName()).isEqualTo("eth_call"); assertThat(method.getName()).isEqualTo("eth_call");
} }
@Test
public void noAccountOverrides() {
final JsonRpcRequestContext request = ethCallRequest(callParameter(), "latest");
Optional<AccountOverrideMap> overrideMap = method.getAddressAccountOverrideMap(request);
assertThat(overrideMap.isPresent()).isFalse();
}
@Test
public void someAccountOverrides() {
AccountOverrideMap expectedOverrides = new AccountOverrideMap();
AccountOverride override = new AccountOverride.Builder().withNonce(88L).build();
final Address address = Address.fromHexString("0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3");
expectedOverrides.put(address, override);
final JsonRpcRequestContext request =
ethCallRequestWithStateOverrides(callParameter(), "latest", expectedOverrides);
Optional<AccountOverrideMap> maybeOverrideMap = method.getAddressAccountOverrideMap(request);
assertThat(maybeOverrideMap.isPresent()).isTrue();
AccountOverrideMap overrideMap = maybeOverrideMap.get();
assertThat(overrideMap.keySet()).hasSize(1);
assertThat(overrideMap.values()).hasSize(1);
assertThat(overrideMap).containsKey(address);
assertThat(overrideMap).containsValue(override);
}
@Test @Test
public void shouldReturnInternalErrorWhenProcessorReturnsEmpty() { public void shouldReturnInternalErrorWhenProcessorReturnsEmpty() {
final JsonRpcRequestContext request = ethCallRequest(callParameter(), "latest"); final JsonRpcRequestContext request = ethCallRequest(callParameter(), "latest");
@ -99,7 +128,7 @@ public class EthCallTest {
when(blockchainQueries.getBlockchain()).thenReturn(blockchain); when(blockchainQueries.getBlockchain()).thenReturn(blockchain);
when(blockchain.getChainHead()).thenReturn(chainHead); when(blockchain.getChainHead()).thenReturn(chainHead);
when(transactionSimulator.process(any(), any(), any(), any(), any())) when(transactionSimulator.process(any(), any(), any(), any(), any(), any()))
.thenReturn(Optional.empty()); .thenReturn(Optional.empty());
final BlockHeader blockHeader = mock(BlockHeader.class); final BlockHeader blockHeader = mock(BlockHeader.class);
@ -109,7 +138,7 @@ public class EthCallTest {
final JsonRpcResponse response = method.response(request); final JsonRpcResponse response = method.response(request);
assertThat(response).usingRecursiveComparison().isEqualTo(expectedResponse); assertThat(response).usingRecursiveComparison().isEqualTo(expectedResponse);
verify(transactionSimulator).process(any(), any(), any(), any(), any()); verify(transactionSimulator).process(any(), any(), any(), any(), any(), any());
} }
@Test @Test
@ -130,12 +159,13 @@ public class EthCallTest {
when(result.isSuccessful()).thenReturn(true); when(result.isSuccessful()).thenReturn(true);
when(result.getValidationResult()).thenReturn(ValidationResult.valid()); when(result.getValidationResult()).thenReturn(ValidationResult.valid());
when(result.getOutput()).thenReturn(Bytes.of()); when(result.getOutput()).thenReturn(Bytes.of());
verify(transactionSimulator).process(any(), any(), any(), mapperCaptor.capture(), any()); verify(transactionSimulator)
.process(
eq(callParameter), eq(Optional.empty()), any(), any(), mapperCaptor.capture(), any());
assertThat(mapperCaptor.getValue().apply(mock(MutableWorldState.class), Optional.of(result))) assertThat(mapperCaptor.getValue().apply(mock(MutableWorldState.class), Optional.of(result)))
.isEqualTo(Optional.of(expectedResponse)); .isEqualTo(Optional.of(expectedResponse));
assertThat(response).usingRecursiveComparison().isEqualTo(expectedResponse); assertThat(response).usingRecursiveComparison().isEqualTo(expectedResponse);
verify(transactionSimulator).process(eq(callParameter), any(), any(), any(), any());
} }
@Test @Test
@ -158,7 +188,8 @@ public class EthCallTest {
when(result.getValidationResult()).thenReturn(ValidationResult.valid()); when(result.getValidationResult()).thenReturn(ValidationResult.valid());
when(result.getOutput()).thenReturn(Bytes.of(1)); when(result.getOutput()).thenReturn(Bytes.of(1));
verify(transactionSimulator) verify(transactionSimulator)
.process(eq(callParameter()), any(), any(), mapperCaptor.capture(), any()); .process(
eq(callParameter()), eq(Optional.empty()), any(), any(), mapperCaptor.capture(), any());
assertThat(mapperCaptor.getValue().apply(mock(MutableWorldState.class), Optional.of(result))) assertThat(mapperCaptor.getValue().apply(mock(MutableWorldState.class), Optional.of(result)))
.isEqualTo(Optional.of(expectedResponse)); .isEqualTo(Optional.of(expectedResponse));
@ -196,7 +227,8 @@ public class EthCallTest {
when(result.isSuccessful()).thenReturn(false); when(result.isSuccessful()).thenReturn(false);
when(result.getValidationResult()).thenReturn(ValidationResult.valid()); when(result.getValidationResult()).thenReturn(ValidationResult.valid());
when(result.result()).thenReturn(processingResult); when(result.result()).thenReturn(processingResult);
verify(transactionSimulator).process(any(), any(), any(), mapperCaptor.capture(), any()); verify(transactionSimulator)
.process(any(), eq(Optional.empty()), any(), any(), mapperCaptor.capture(), any());
assertThat(mapperCaptor.getValue().apply(mock(MutableWorldState.class), Optional.of(result))) assertThat(mapperCaptor.getValue().apply(mock(MutableWorldState.class), Optional.of(result)))
.isEqualTo(Optional.of(expectedResponse)); .isEqualTo(Optional.of(expectedResponse));
@ -235,7 +267,8 @@ public class EthCallTest {
when(result.isSuccessful()).thenReturn(false); when(result.isSuccessful()).thenReturn(false);
when(result.getValidationResult()).thenReturn(ValidationResult.valid()); when(result.getValidationResult()).thenReturn(ValidationResult.valid());
when(result.result()).thenReturn(processingResult); when(result.result()).thenReturn(processingResult);
verify(transactionSimulator).process(any(), any(), any(), mapperCaptor.capture(), any()); verify(transactionSimulator)
.process(any(), eq(Optional.empty()), any(), any(), mapperCaptor.capture(), any());
assertThat(mapperCaptor.getValue().apply(mock(MutableWorldState.class), Optional.of(result))) assertThat(mapperCaptor.getValue().apply(mock(MutableWorldState.class), Optional.of(result)))
.isEqualTo(Optional.of(expectedResponse)); .isEqualTo(Optional.of(expectedResponse));
@ -277,7 +310,8 @@ public class EthCallTest {
when(result.getValidationResult()).thenReturn(ValidationResult.valid()); when(result.getValidationResult()).thenReturn(ValidationResult.valid());
when(result.result()).thenReturn(processingResult); when(result.result()).thenReturn(processingResult);
verify(transactionSimulator).process(any(), any(), any(), mapperCaptor.capture(), any()); verify(transactionSimulator)
.process(any(), eq(Optional.empty()), any(), any(), mapperCaptor.capture(), any());
assertThat(mapperCaptor.getValue().apply(mock(MutableWorldState.class), Optional.of(result))) assertThat(mapperCaptor.getValue().apply(mock(MutableWorldState.class), Optional.of(result)))
.isEqualTo(Optional.of(expectedResponse)); .isEqualTo(Optional.of(expectedResponse));
@ -291,7 +325,7 @@ public class EthCallTest {
final JsonRpcRequestContext request = ethCallRequest(callParameter(), "latest"); final JsonRpcRequestContext request = ethCallRequest(callParameter(), "latest");
when(blockchainQueries.getBlockchain()).thenReturn(blockchain); when(blockchainQueries.getBlockchain()).thenReturn(blockchain);
when(blockchain.getChainHead()).thenReturn(chainHead); when(blockchain.getChainHead()).thenReturn(chainHead);
when(transactionSimulator.process(any(), any(), any(), any(), any())) when(transactionSimulator.process(any(), eq(Optional.empty()), any(), any(), any(), any()))
.thenReturn(Optional.empty()); .thenReturn(Optional.empty());
final BlockHeader blockHeader = mock(BlockHeader.class); final BlockHeader blockHeader = mock(BlockHeader.class);
@ -301,7 +335,7 @@ public class EthCallTest {
method.response(request); method.response(request);
verify(blockchainQueries, atLeastOnce()).getBlockchain(); verify(blockchainQueries, atLeastOnce()).getBlockchain();
verify(transactionSimulator).process(any(), any(), any(), any(), any()); verify(transactionSimulator).process(any(), eq(Optional.empty()), any(), any(), any(), any());
} }
@Test @Test
@ -315,7 +349,7 @@ public class EthCallTest {
method.response(request); method.response(request);
verify(blockchainQueries).getBlockHeaderByHash(eq(Hash.ZERO)); verify(blockchainQueries).getBlockHeaderByHash(eq(Hash.ZERO));
verify(transactionSimulator).process(any(), any(), any(), any(), any()); verify(transactionSimulator).process(any(), eq(Optional.empty()), any(), any(), any(), any());
} }
@Test @Test
@ -323,13 +357,13 @@ public class EthCallTest {
final JsonRpcRequestContext request = ethCallRequest(callParameter(), "safe"); final JsonRpcRequestContext request = ethCallRequest(callParameter(), "safe");
when(blockchainQueries.getBlockHeaderByHash(Hash.ZERO)).thenReturn(Optional.of(blockHeader)); when(blockchainQueries.getBlockHeaderByHash(Hash.ZERO)).thenReturn(Optional.of(blockHeader));
when(blockchainQueries.safeBlockHeader()).thenReturn(Optional.of(blockHeader)); when(blockchainQueries.safeBlockHeader()).thenReturn(Optional.of(blockHeader));
when(transactionSimulator.process(any(), any(), any(), any(), any())) when(transactionSimulator.process(any(), eq(Optional.empty()), any(), any(), any(), any()))
.thenReturn(Optional.empty()); .thenReturn(Optional.empty());
method.response(request); method.response(request);
verify(blockchainQueries).getBlockHeaderByHash(Hash.ZERO); verify(blockchainQueries).getBlockHeaderByHash(Hash.ZERO);
verify(blockchainQueries).safeBlockHeader(); verify(blockchainQueries).safeBlockHeader();
verify(transactionSimulator).process(any(), any(), any(), any(), any()); verify(transactionSimulator).process(any(), eq(Optional.empty()), any(), any(), any(), any());
} }
@Test @Test
@ -337,13 +371,13 @@ public class EthCallTest {
final JsonRpcRequestContext request = ethCallRequest(callParameter(), "finalized"); final JsonRpcRequestContext request = ethCallRequest(callParameter(), "finalized");
when(blockchainQueries.getBlockHeaderByHash(Hash.ZERO)).thenReturn(Optional.of(blockHeader)); when(blockchainQueries.getBlockHeaderByHash(Hash.ZERO)).thenReturn(Optional.of(blockHeader));
when(blockchainQueries.finalizedBlockHeader()).thenReturn(Optional.of(blockHeader)); when(blockchainQueries.finalizedBlockHeader()).thenReturn(Optional.of(blockHeader));
when(transactionSimulator.process(any(), any(), any(), any(), any())) when(transactionSimulator.process(any(), eq(Optional.empty()), any(), any(), any(), any()))
.thenReturn(Optional.empty()); .thenReturn(Optional.empty());
method.response(request); method.response(request);
verify(blockchainQueries).getBlockHeaderByHash(Hash.ZERO); verify(blockchainQueries).getBlockHeaderByHash(Hash.ZERO);
verify(blockchainQueries).finalizedBlockHeader(); verify(blockchainQueries).finalizedBlockHeader();
verify(transactionSimulator).process(any(), any(), any(), any(), any()); verify(transactionSimulator).process(any(), eq(Optional.empty()), any(), any(), any(), any());
} }
@Test @Test
@ -353,13 +387,13 @@ public class EthCallTest {
when(blockchainQueries.getBlockHashByNumber(anyLong())).thenReturn(Optional.of(Hash.ZERO)); when(blockchainQueries.getBlockHashByNumber(anyLong())).thenReturn(Optional.of(Hash.ZERO));
when(blockchainQueries.getBlockHeaderByHash(Hash.ZERO)) when(blockchainQueries.getBlockHeaderByHash(Hash.ZERO))
.thenReturn(Optional.of(mock(BlockHeader.class))); .thenReturn(Optional.of(mock(BlockHeader.class)));
when(transactionSimulator.process(any(), any(), any(), any(), any())) when(transactionSimulator.process(any(), eq(Optional.empty()), any(), any(), any(), any()))
.thenReturn(Optional.empty()); .thenReturn(Optional.empty());
method.response(request); method.response(request);
verify(blockchainQueries).getBlockHeaderByHash(eq(Hash.ZERO)); verify(blockchainQueries).getBlockHeaderByHash(eq(Hash.ZERO));
verify(transactionSimulator).process(any(), any(), any(), any(), any()); verify(transactionSimulator).process(any(), eq(Optional.empty()), any(), any(), any(), any());
} }
@Test @Test
@ -431,7 +465,7 @@ public class EthCallTest {
.build(); .build();
verify(transactionSimulator) verify(transactionSimulator)
.process(any(), eq(transactionValidationParams), any(), any(), any()); .process(any(), eq(Optional.empty()), eq(transactionValidationParams), any(), any(), any());
} }
private JsonCallParameter callParameter() { private JsonCallParameter callParameter() {
@ -458,8 +492,17 @@ public class EthCallTest {
new JsonRpcRequest("2.0", "eth_call", new Object[] {callParameter, blockNumberInHex})); new JsonRpcRequest("2.0", "eth_call", new Object[] {callParameter, blockNumberInHex}));
} }
private JsonRpcRequestContext ethCallRequestWithStateOverrides(
final CallParameter callParameter,
final String blockNumberInHex,
final AccountOverrideMap overrides) {
return new JsonRpcRequestContext(
new JsonRpcRequest(
"2.0", "eth_call", new Object[] {callParameter, blockNumberInHex, overrides}));
}
private void mockTransactionProcessorSuccessResult(final JsonRpcResponse jsonRpcResponse) { private void mockTransactionProcessorSuccessResult(final JsonRpcResponse jsonRpcResponse) {
when(transactionSimulator.process(any(), any(), any(), any(), any())) when(transactionSimulator.process(any(), eq(Optional.empty()), any(), any(), any(), any()))
.thenReturn(Optional.of(jsonRpcResponse)); .thenReturn(Optional.of(jsonRpcResponse));
} }
} }

@ -0,0 +1,24 @@
{
"request": {
"id": 3,
"jsonrpc": "2.0",
"method": "eth_call",
"params": [
{
"to": "0x6295ee1b4f6dd65047762f924ecd367c17eabf8f",
"from": "a94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"data": "0x12a7b914"
},
"latest",
{
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b": {}
}
]
},
"response": {
"jsonrpc": "2.0",
"id": 3,
"result": "0x0000000000000000000000000000000000000000000000000000000000000001"
},
"statusCode": 200
}

@ -0,0 +1,32 @@
{
"request": {
"id": 3,
"jsonrpc": "2.0",
"method": "eth_call",
"params": [
{
"to": "0x6295ee1b4f6dd65047762f924ecd367c17eabf8f",
"from": "a94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"data": "0x12a7b914"
},
"latest",
{
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
"balance": "0xde0b6b3a7640000",
"nonce": 88
},
"0xb9741079a300Cb3B8f324CdDB847c0d1d273a05E": {
"stateDiff": {
"0x1cf7945003fc5b59d2f6736f0704557aa805c4f2844084ccd1173b8d56946962": "0x000000000000000000000000000000000000000000000000000000110ed03bf7"
}
}
}
]
},
"response": {
"jsonrpc": "2.0",
"id": 3,
"result": "0x0000000000000000000000000000000000000000000000000000000000000001"
},
"statusCode": 200
}

@ -0,0 +1,34 @@
{
"request": {
"id": 3,
"jsonrpc": "2.0",
"method": "eth_call",
"params": [
{
"to": "0x6295ee1b4f6dd65047762f924ecd367c17eabf8f",
"from": "a94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"value": "0x000002"
},
"latest",
{
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
"balance": "0x000001"
},
"0xb9741079a300Cb3B8f324CdDB847c0d1d273a05E": {
"stateDiff": {
"0x1cf7945003fc5b59d2f6736f0704557aa805c4f2844084ccd1173b8d56946962": "0x000000000000000000000000000000000000000000000000000000110ed03bf7"
}
}
}
]
},
"response": {
"jsonrpc": "2.0",
"id": 3,
"error" : {
"code" : -32004,
"message" : "Upfront cost exceeds account balance"
}
},
"statusCode": 200
}

@ -34,10 +34,13 @@ 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;
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.tracing.OperationTracer; import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.worldstate.WorldUpdater; import org.hyperledger.besu.evm.worldstate.WorldUpdater;
@ -46,8 +49,10 @@ import java.util.Optional;
import java.util.function.Supplier; import java.util.function.Supplier;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Suppliers; import com.google.common.base.Suppliers;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -152,6 +157,35 @@ public class TransactionSimulator {
final OperationTracer operationTracer, final OperationTracer operationTracer,
final PreCloseStateHandler<U> preWorldStateCloseGuard, final PreCloseStateHandler<U> preWorldStateCloseGuard,
final BlockHeader header) { final BlockHeader header) {
return process(
callParams,
Optional.empty(),
transactionValidationParams,
operationTracer,
preWorldStateCloseGuard,
header);
}
/**
* Processes a transaction simulation with the provided parameters and executes pre-worldstate
* close actions.
*
* @param callParams The call parameters for the transaction.
* @param maybeStateOverrides The map of state overrides to apply to the state for this
* transaction.
* @param transactionValidationParams The validation parameters for the transaction.
* @param operationTracer The tracer for capturing operations during processing.
* @param preWorldStateCloseGuard The pre-worldstate close guard for executing pre-close actions.
* @param header The block header.
* @return An Optional containing the result of the processing.
*/
public <U> Optional<U> process(
final CallParameter callParams,
final Optional<AccountOverrideMap> maybeStateOverrides,
final TransactionValidationParams transactionValidationParams,
final OperationTracer operationTracer,
final PreCloseStateHandler<U> preWorldStateCloseGuard,
final BlockHeader header) {
if (header == null) { if (header == null) {
return Optional.empty(); return Optional.empty();
} }
@ -169,7 +203,12 @@ public class TransactionSimulator {
return preWorldStateCloseGuard.apply( return preWorldStateCloseGuard.apply(
ws, ws,
processWithWorldUpdater( processWithWorldUpdater(
callParams, transactionValidationParams, operationTracer, header, updater)); callParams,
maybeStateOverrides,
transactionValidationParams,
operationTracer,
header,
updater));
} catch (final Exception e) { } catch (final Exception e) {
return Optional.empty(); return Optional.empty();
@ -208,6 +247,7 @@ public class TransactionSimulator {
@Nonnull @Nonnull
public Optional<TransactionSimulatorResult> processWithWorldUpdater( public Optional<TransactionSimulatorResult> processWithWorldUpdater(
final CallParameter callParams, final CallParameter callParams,
final Optional<AccountOverrideMap> maybeStateOverrides,
final TransactionValidationParams transactionValidationParams, final TransactionValidationParams transactionValidationParams,
final OperationTracer operationTracer, final OperationTracer operationTracer,
final BlockHeader header, final BlockHeader header,
@ -226,6 +266,12 @@ public class TransactionSimulator {
.blockHeaderFunctions(protocolSpec.getBlockHeaderFunctions()) .blockHeaderFunctions(protocolSpec.getBlockHeaderFunctions())
.buildBlockHeader(); .buildBlockHeader();
} }
if (maybeStateOverrides.isPresent()) {
for (Address accountToOverride : maybeStateOverrides.get().keySet()) {
final AccountOverride overrides = maybeStateOverrides.get().get(accountToOverride);
applyOverrides(updater.getOrCreate(accountToOverride), overrides);
}
}
final Account sender = updater.get(senderAddress); final Account sender = updater.get(senderAddress);
final long nonce = sender != null ? sender.getNonce() : 0L; final long nonce = sender != null ? sender.getNonce() : 0L;
@ -284,6 +330,24 @@ public class TransactionSimulator {
return Optional.of(new TransactionSimulatorResult(transaction, result)); return Optional.of(new TransactionSimulatorResult(transaction, result));
} }
@VisibleForTesting
protected void applyOverrides(final MutableAccount account, final AccountOverride override) {
LOG.debug("applying overrides to state for account {}", account.getAddress());
override.getNonce().ifPresent(account::setNonce);
if (override.getBalance().isPresent()) {
account.setBalance(override.getBalance().get());
}
override.getCode().ifPresent(n -> account.setCode(Bytes.fromHexString(n)));
override
.getStateDiff()
.ifPresent(
d ->
d.forEach(
(key, value) ->
account.setStorageValue(
UInt256.fromHexString(key), UInt256.fromHexString(value))));
}
private long calculateSimulationGasCap( private long calculateSimulationGasCap(
final long userProvidedGasLimit, final long blockGasLimit) { final long userProvidedGasLimit, final long blockGasLimit) {
final long simulationGasCap; final long simulationGasCap;

@ -0,0 +1,147 @@
/*
* Copyright contributors to Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.util;
import org.hyperledger.besu.datatypes.Wei;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
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
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonDeserialize(builder = AccountOverride.Builder.class)
public class AccountOverride {
private static final Logger LOG = LoggerFactory.getLogger(AccountOverride.class);
private final Optional<Wei> balance;
private final Optional<Long> nonce;
private final Optional<String> code;
private final Optional<Map<String, String>> stateDiff;
private AccountOverride(
final Optional<Wei> balance,
final Optional<Long> nonce,
final Optional<String> code,
final Optional<Map<String, String>> stateDiff) {
this.balance = balance;
this.nonce = nonce;
this.code = code;
this.stateDiff = stateDiff;
}
public Optional<Wei> getBalance() {
return balance;
}
public Optional<Long> getNonce() {
return nonce;
}
public Optional<String> getCode() {
return code;
}
public Optional<Map<String, String>> getStateDiff() {
return stateDiff;
}
public static class Builder {
private Optional<Wei> balance = Optional.empty();
private Optional<Long> nonce = Optional.empty();
private Optional<String> code = Optional.empty();
private Optional<Map<String, String>> stateDiff = Optional.empty();
/** Default constructor. */
public Builder() {}
public Builder withBalance(final Wei balance) {
this.balance = Optional.ofNullable(balance);
return this;
}
public Builder withNonce(final Long nonce) {
this.nonce = Optional.ofNullable(nonce);
return this;
}
public Builder withCode(final String code) {
this.code = Optional.ofNullable(code);
return this;
}
public Builder withStateDiff(final Map<String, String> stateDiff) {
this.stateDiff = Optional.ofNullable(stateDiff);
return this;
}
public AccountOverride build() {
return new AccountOverride(balance, nonce, code, stateDiff);
}
}
@JsonAnySetter
public void withUnknownProperties(final String key, final Object value) {
LOG.debug(
"unknown property - {} with value - {} and type - {} caught during serialization",
key,
value,
value != null ? value.getClass() : "NULL");
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final AccountOverride accountOverride = (AccountOverride) o;
return balance.equals(accountOverride.balance)
&& nonce.equals(accountOverride.nonce)
&& code.equals(accountOverride.code)
&& stateDiff.equals(accountOverride.stateDiff);
}
@Override
public int hashCode() {
return Objects.hash(balance, nonce, code, stateDiff);
}
@Override
public String toString() {
return "AccountOverride{"
+ "balance="
+ balance
+ ", nonce="
+ nonce
+ ", code="
+ code
+ ", stateDiff="
+ stateDiff
+ '}';
}
}

@ -0,0 +1,27 @@
/*
* Copyright contributors to Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.util;
import org.hyperledger.besu.datatypes.Address;
import java.util.HashMap;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonIgnoreProperties(ignoreUnknown = true)
public class AccountOverrideMap extends HashMap<Address, AccountOverride> {
public AccountOverrideMap() {}
}

@ -20,6 +20,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import org.hyperledger.besu.crypto.SECPSignature; import org.hyperledger.besu.crypto.SECPSignature;
@ -47,17 +48,21 @@ 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.tracing.OperationTracer; import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.worldstate.WorldUpdater; import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.base.Suppliers; import com.google.common.base.Suppliers;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
@ -100,6 +105,42 @@ public class TransactionSimulatorTest {
new TransactionSimulator(blockchain, worldStateArchive, protocolSchedule, GAS_CAP); new TransactionSimulator(blockchain, worldStateArchive, protocolSchedule, GAS_CAP);
} }
@Test
public void testOverrides_whenNoOverrides_noUpdates() {
MutableAccount mutableAccount = mock(MutableAccount.class);
when(mutableAccount.getAddress()).thenReturn(DEFAULT_FROM); // called from logging
AccountOverride.Builder builder = new AccountOverride.Builder();
AccountOverride override = builder.build();
transactionSimulator.applyOverrides(mutableAccount, override);
verify(mutableAccount).getAddress();
verifyNoMoreInteractions(mutableAccount);
}
@Test
public void testOverrides_whenBalanceOverrides_balanceIsUpdated() {
MutableAccount mutableAccount = mock(MutableAccount.class);
when(mutableAccount.getAddress()).thenReturn(DEFAULT_FROM);
AccountOverride.Builder builder = new AccountOverride.Builder().withBalance(Wei.of(99));
AccountOverride override = builder.build();
transactionSimulator.applyOverrides(mutableAccount, override);
verify(mutableAccount).setBalance(eq(Wei.of(99)));
}
@Test
public void testOverrides_whenStateDiffOverrides_stateIsUpdated() {
MutableAccount mutableAccount = mock(MutableAccount.class);
when(mutableAccount.getAddress()).thenReturn(DEFAULT_FROM);
final String storageKey = "0x01a2";
final String storageValue = "0x00ff";
AccountOverride.Builder builder =
new AccountOverride.Builder().withStateDiff(Map.of(storageKey, storageValue));
AccountOverride override = builder.build();
transactionSimulator.applyOverrides(mutableAccount, override);
verify(mutableAccount)
.setStorageValue(
eq(UInt256.fromHexString(storageKey)), eq(UInt256.fromHexString(storageValue)));
}
@Test @Test
public void shouldReturnEmptyWhenBlockDoesNotExist() { public void shouldReturnEmptyWhenBlockDoesNotExist() {
when(blockchain.getBlockHeader(eq(1L))).thenReturn(Optional.empty()); when(blockchain.getBlockHeader(eq(1L))).thenReturn(Optional.empty());

@ -0,0 +1,188 @@
/*
* Copyright contributors to Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.util;
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.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import java.util.Optional;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
public class AccountOverrideParameterTest {
private static final String ADDRESS_HEX1 = "0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3";
private static final String ADDRESS_HEX2 = "0xd5E23607D5d73ff2293152f464C3caB005f87696";
private static final String STORAGE_KEY =
"0x1cf7945003fc5b59d2f6736f0704557aa805c4f2844084ccd1173b8d56946962";
private static final String STORAGE_VALUE =
"0x000000000000000000000000000000000000000000000000000000110ed03bf7";
private static final String CODE_STRING =
"0xdbf4257000000000000000000000000000000000000000000000000000000000";
@Test
public void jsonDeserializesCorrectly() throws Exception {
final String json =
"{\"jsonrpc\":\"2.0\",\"method\":\"eth_call\",\"params\":[{"
+ "\"from\":\"0x0\", \"to\": \"0x0\"}, "
+ "\"latest\","
+ "{\""
+ ADDRESS_HEX1
+ "\":"
+ "{"
+ "\"balance\": \"0x01\","
+ "\"nonce\": 88"
+ "}}],\"id\":1}";
final JsonRpcRequestContext request = new JsonRpcRequestContext(readJsonAsJsonRpcRequest(json));
final AccountOverrideMap accountOverrideParam =
request.getRequiredParameter(2, AccountOverrideMap.class);
final AccountOverride accountOverride =
accountOverrideParam.get(Address.fromHexString(ADDRESS_HEX1));
assertThat(accountOverride.getNonce()).isEqualTo(Optional.of(88L));
assertThat(accountOverride.getBalance()).isEqualTo(Optional.of(Wei.of(1)));
assertFalse(accountOverride.getStateDiff().isPresent());
}
@Test
public void jsonWithCodeDeserializesCorrectly() throws Exception {
final String json =
"{\"jsonrpc\":\"2.0\",\"method\":\"eth_call\",\"params\":[{"
+ "\"from\":\"0x0\", \"to\": \"0x0\"}, "
+ "\"latest\","
+ "{\""
+ ADDRESS_HEX1
+ "\":"
+ "{"
+ "\"balance\": \"0x01\","
+ "\"code\": \""
+ CODE_STRING
+ "\""
+ "}}],\"id\":1}";
final JsonRpcRequestContext request = new JsonRpcRequestContext(readJsonAsJsonRpcRequest(json));
final AccountOverrideMap accountOverrideParam =
request.getRequiredParameter(2, AccountOverrideMap.class);
final AccountOverride accountOverride =
accountOverrideParam.get(Address.fromHexString(ADDRESS_HEX1));
assertFalse(accountOverride.getNonce().isPresent());
assertThat(accountOverride.getBalance()).isEqualTo(Optional.of(Wei.of(1)));
assertThat(accountOverride.getCode()).isEqualTo(Optional.of(CODE_STRING));
assertFalse(accountOverride.getStateDiff().isPresent());
}
@Test
public void jsonWithStorageOverridesDeserializesCorrectly() throws Exception {
final String json =
"{\"jsonrpc\":\"2.0\",\"method\":\"eth_call\",\"params\":[{"
+ "\"from\":\"0x0\", \"to\": \"0x0\"}, "
+ "\"latest\","
+ "{\""
+ ADDRESS_HEX1
+ "\":"
+ "{"
+ "\"balance\": \"0x01\","
+ "\"nonce\": 88,"
+ "\"stateDiff\": {"
+ "\""
+ STORAGE_KEY
+ "\": \""
+ STORAGE_VALUE
+ "\""
+ "}}}],\"id\":1}";
final JsonRpcRequestContext request = new JsonRpcRequestContext(readJsonAsJsonRpcRequest(json));
final AccountOverrideMap accountOverrideParam =
request.getRequiredParameter(2, AccountOverrideMap.class);
assertThat(accountOverrideParam.size()).isEqualTo(1);
final AccountOverride accountOverride =
accountOverrideParam.get(Address.fromHexString(ADDRESS_HEX1));
assertThat(accountOverride.getNonce()).isEqualTo(Optional.of(88L));
assertTrue(accountOverride.getStateDiff().isPresent());
assertThat(accountOverride.getStateDiff().get().get(STORAGE_KEY)).isEqualTo(STORAGE_VALUE);
}
@Test
public void jsonWithMultipleAccountOverridesDeserializesCorrectly() throws Exception {
final String json =
"{\"jsonrpc\":\"2.0\",\"method\":\"eth_call\",\"params\":[{"
+ "\"from\":\"0x0\", \"to\": \"0x0\"}, "
+ "\"latest\","
+ "{\""
+ ADDRESS_HEX1
+ "\":"
+ "{"
+ "\"balance\": \"0x01\","
+ "\"nonce\": 88,"
+ "\"stateDiff\": {"
+ "\""
+ STORAGE_KEY
+ "\": \""
+ STORAGE_VALUE
+ "\""
+ "}},"
+ "\""
+ ADDRESS_HEX2
+ "\":"
+ "{"
+ "\"balance\": \"0xFF\","
+ "\"nonce\": 99,"
+ "\"stateDiff\": {"
+ "\""
+ STORAGE_KEY
+ "\": \""
+ STORAGE_VALUE
+ "\""
+ "}}}],\"id\":1}";
final JsonRpcRequestContext request = new JsonRpcRequestContext(readJsonAsJsonRpcRequest(json));
final AccountOverrideMap accountOverrideParam =
request.getRequiredParameter(2, AccountOverrideMap.class);
assertThat(accountOverrideParam.size()).isEqualTo(2);
final AccountOverride accountOverride1 =
accountOverrideParam.get(Address.fromHexString(ADDRESS_HEX1));
assertThat(accountOverride1.getNonce()).isEqualTo(Optional.of(88L));
assertThat(accountOverride1.getBalance()).isEqualTo(Optional.of(Wei.fromHexString("0x01")));
assertTrue(accountOverride1.getStateDiff().isPresent());
assertThat(accountOverride1.getStateDiff().get().get(STORAGE_KEY)).isEqualTo(STORAGE_VALUE);
final AccountOverride accountOverride2 =
accountOverrideParam.get(Address.fromHexString(ADDRESS_HEX2));
assertThat(accountOverride2.getNonce()).isEqualTo(Optional.of(99L));
assertThat(accountOverride2.getBalance()).isEqualTo(Optional.of(Wei.fromHexString("0xFF")));
assertTrue(accountOverride2.getStateDiff().isPresent());
assertThat(accountOverride2.getStateDiff().get().get(STORAGE_KEY)).isEqualTo(STORAGE_VALUE);
}
private JsonRpcRequest readJsonAsJsonRpcRequest(final String json) throws java.io.IOException {
return new ObjectMapper().readValue(json, JsonRpcRequest.class);
}
}
Loading…
Cancel
Save