Honor block number or tag parameter in eth_estimateGas and eth_createAccessList (#7502)

Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
pull/7586/head
Fabio Di Fabio 3 months ago committed by GitHub
parent dc6324c8d0
commit dad05d407e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 68
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AbstractEstimateGas.java
  3. 72
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCreateAccessList.java
  4. 111
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGas.java
  5. 142
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCreateAccessListTest.java
  6. 180
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGasTest.java
  7. 21
      ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/eth/eth_estimateGas_from_contract_withBlockNum.json
  8. 21
      ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/eth/eth_estimateGas_from_contract_withBlockTag.json
  9. 21
      ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/eth/eth_estimateGas_insufficientGas_withBlockNum.json
  10. 21
      ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/eth/eth_estimateGas_transfer_withBlockTag.json

@ -12,6 +12,7 @@
### Bug fixes
- Layered txpool: do not send notifications when moving tx between layers [#7539](https://github.com/hyperledger/besu/pull/7539)
- Layered txpool: fix for unsent drop notifications on remove [#7538](https://github.com/hyperledger/besu/pull/7538)
- Honor block number or tag parameter in eth_estimateGas and eth_createAccessList [#7502](https://github.com/hyperledger/besu/pull/7502)
## 24.9.0

@ -14,15 +14,19 @@
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonCallParameterUtil.validateAndGetCallParams;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcErrorConverter;
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.parameters.BlockParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonCallParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
@ -34,32 +38,68 @@ import org.hyperledger.besu.evm.tracing.EstimateGasOperationTracer;
import java.util.Optional;
public abstract class AbstractEstimateGas implements JsonRpcMethod {
public abstract class AbstractEstimateGas extends AbstractBlockParameterMethod {
private static final double SUB_CALL_REMAINING_GAS_RATIO = 65D / 64D;
protected final BlockchainQueries blockchainQueries;
protected final TransactionSimulator transactionSimulator;
public AbstractEstimateGas(
final BlockchainQueries blockchainQueries, final TransactionSimulator transactionSimulator) {
this.blockchainQueries = blockchainQueries;
super(blockchainQueries);
this.transactionSimulator = transactionSimulator;
}
protected BlockHeader blockHeader() {
final Blockchain theChain = blockchainQueries.getBlockchain();
@Override
protected BlockParameter blockParameter(final JsonRpcRequestContext request) {
try {
return request.getOptionalParameter(1, BlockParameter.class).orElse(BlockParameter.LATEST);
} catch (JsonRpcParameter.JsonRpcParameterException e) {
throw new InvalidJsonRpcParameters(
"Invalid block parameter (index 1)", RpcErrorType.INVALID_BLOCK_PARAMS, e);
}
}
// Optimistically get the block header for the chain head without taking a lock,
// but revert to the safe implementation if it returns an empty optional. (It's
// possible the chain head has been updated but the block is still being persisted
// to storage/cache under the lock).
return theChain
.getBlockHeader(theChain.getChainHeadHash())
.or(() -> theChain.getBlockHeaderSafe(theChain.getChainHeadHash()))
.orElse(null);
protected Optional<BlockHeader> blockHeader(final long blockNumber) {
if (getBlockchainQueries().headBlockNumber() == blockNumber) {
// chain head header if cached, and we can return it form memory
return Optional.of(getBlockchainQueries().getBlockchain().getChainHeadHeader());
}
return getBlockchainQueries().getBlockHeaderByNumber(blockNumber);
}
protected Optional<RpcErrorType> validateBlockHeader(
final Optional<BlockHeader> maybeBlockHeader) {
if (maybeBlockHeader.isEmpty()) {
return Optional.of(RpcErrorType.BLOCK_NOT_FOUND);
}
final var blockHeader = maybeBlockHeader.get();
if (!getBlockchainQueries()
.getWorldStateArchive()
.isWorldStateAvailable(blockHeader.getStateRoot(), blockHeader.getHash())) {
return Optional.of(RpcErrorType.WORLD_STATE_UNAVAILABLE);
}
return Optional.empty();
}
@Override
protected Object resultByBlockNumber(
final JsonRpcRequestContext requestContext, final long blockNumber) {
final JsonCallParameter jsonCallParameter = validateAndGetCallParams(requestContext);
final Optional<BlockHeader> maybeBlockHeader = blockHeader(blockNumber);
final Optional<RpcErrorType> jsonRpcError = validateBlockHeader(maybeBlockHeader);
if (jsonRpcError.isPresent()) {
return errorResponse(requestContext, jsonRpcError.get());
}
return resultByBlockHeader(requestContext, jsonCallParameter, maybeBlockHeader.get());
}
protected abstract Object resultByBlockHeader(
final JsonRpcRequestContext requestContext,
final JsonCallParameter jsonCallParameter,
final BlockHeader blockHeader);
protected CallParameter overrideGasLimitAndPrice(
final JsonCallParameter callParams, final long gasLimit) {
return new CallParameter(

@ -14,15 +14,11 @@
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonCallParameterUtil.validateAndGetCallParams;
import org.hyperledger.besu.datatypes.AccessListEntry;
import org.hyperledger.besu.datatypes.Wei;
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.parameters.JsonCallParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.CreateAccessListResult;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
@ -52,44 +48,29 @@ public class EthCreateAccessList extends AbstractEstimateGas {
}
@Override
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
final JsonCallParameter jsonCallParameter = validateAndGetCallParams(requestContext);
final BlockHeader blockHeader = blockHeader();
final Optional<RpcErrorType> jsonRpcError = validateBlockHeader(blockHeader);
if (jsonRpcError.isPresent()) {
return errorResponse(requestContext, jsonRpcError.get());
}
protected Object resultByBlockHeader(
final JsonRpcRequestContext requestContext,
final JsonCallParameter jsonCallParameter,
final BlockHeader blockHeader) {
final AccessListSimulatorResult maybeResult =
processTransaction(jsonCallParameter, blockHeader);
// if the call accessList is different from the simulation result, calculate gas and return
if (shouldProcessWithAccessListOverride(jsonCallParameter, maybeResult.getTracer())) {
if (shouldProcessWithAccessListOverride(jsonCallParameter, maybeResult.tracer())) {
final AccessListSimulatorResult result =
processTransactionWithAccessListOverride(
jsonCallParameter, blockHeader, maybeResult.getTracer().getAccessList());
jsonCallParameter, blockHeader, maybeResult.tracer().getAccessList());
return createResponse(requestContext, result);
} else {
return createResponse(requestContext, maybeResult);
}
}
private Optional<RpcErrorType> validateBlockHeader(final BlockHeader blockHeader) {
if (blockHeader == null) {
return Optional.of(RpcErrorType.INTERNAL_ERROR);
}
if (!blockchainQueries
.getWorldStateArchive()
.isWorldStateAvailable(blockHeader.getStateRoot(), blockHeader.getHash())) {
return Optional.of(RpcErrorType.WORLD_STATE_UNAVAILABLE);
}
return Optional.empty();
}
private JsonRpcResponse createResponse(
private Object createResponse(
final JsonRpcRequestContext requestContext, final AccessListSimulatorResult result) {
return result
.getResult()
.map(createResponse(requestContext, result.getTracer()))
.orElse(errorResponse(requestContext, RpcErrorType.INTERNAL_ERROR));
.result()
.map(createResponse(requestContext, result.tracer()))
.orElseGet(() -> errorResponse(requestContext, RpcErrorType.INTERNAL_ERROR));
}
private TransactionValidationParams transactionValidationParams(
@ -117,14 +98,12 @@ public class EthCreateAccessList extends AbstractEstimateGas {
return !Objects.equals(tracer.getAccessList(), parameters.getAccessList().get());
}
private Function<TransactionSimulatorResult, JsonRpcResponse> createResponse(
private Function<TransactionSimulatorResult, Object> createResponse(
final JsonRpcRequestContext request, final AccessListOperationTracer operationTracer) {
return result ->
result.isSuccessful()
? new JsonRpcSuccessResponse(
request.getRequest().getId(),
new CreateAccessListResult(
operationTracer.getAccessList(), processEstimateGas(result, operationTracer)))
? new CreateAccessListResult(
operationTracer.getAccessList(), processEstimateGas(result, operationTracer))
: errorResponse(request, result);
}
@ -138,8 +117,7 @@ public class EthCreateAccessList extends AbstractEstimateGas {
final AccessListOperationTracer tracer = AccessListOperationTracer.create();
final Optional<TransactionSimulatorResult> result =
transactionSimulator.process(
callParams, transactionValidationParams, tracer, blockHeader.getNumber());
transactionSimulator.process(callParams, transactionValidationParams, tracer, blockHeader);
return new AccessListSimulatorResult(result, tracer);
}
@ -156,7 +134,7 @@ public class EthCreateAccessList extends AbstractEstimateGas {
final Optional<TransactionSimulatorResult> result =
transactionSimulator.process(
callParameter, transactionValidationParams, tracer, blockHeader.getNumber());
callParameter, transactionValidationParams, tracer, blockHeader);
return new AccessListSimulatorResult(result, tracer);
}
@ -176,22 +154,6 @@ public class EthCreateAccessList extends AbstractEstimateGas {
Optional.ofNullable(accessListEntries));
}
private static class AccessListSimulatorResult {
final Optional<TransactionSimulatorResult> result;
final AccessListOperationTracer tracer;
public AccessListSimulatorResult(
final Optional<TransactionSimulatorResult> result, final AccessListOperationTracer tracer) {
this.result = result;
this.tracer = tracer;
}
public Optional<TransactionSimulatorResult> getResult() {
return result;
}
public AccessListOperationTracer getTracer() {
return tracer;
}
}
private record AccessListSimulatorResult(
Optional<TransactionSimulatorResult> result, AccessListOperationTracer tracer) {}
}

@ -1,5 +1,5 @@
/*
* Copyright ConsenSys AG.
* Copyright contributors to Hyperledger 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
@ -14,13 +14,10 @@
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonCallParameterUtil.validateAndGetCallParams;
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.parameters.JsonCallParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
@ -38,7 +35,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EthEstimateGas extends AbstractEstimateGas {
private static final Logger LOG = LoggerFactory.getLogger(EthEstimateGas.class);
public EthEstimateGas(
@ -52,19 +48,10 @@ public class EthEstimateGas extends AbstractEstimateGas {
}
@Override
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
final JsonCallParameter callParams = validateAndGetCallParams(requestContext);
final BlockHeader blockHeader = blockHeader();
if (blockHeader == null) {
LOG.error("Chain head block not found");
return errorResponse(requestContext, RpcErrorType.INTERNAL_ERROR);
}
if (!blockchainQueries
.getWorldStateArchive()
.isWorldStateAvailable(blockHeader.getStateRoot(), blockHeader.getHash())) {
return errorResponse(requestContext, RpcErrorType.WORLD_STATE_UNAVAILABLE);
}
protected Object resultByBlockHeader(
final JsonRpcRequestContext requestContext,
final JsonCallParameter callParams,
final BlockHeader blockHeader) {
final CallParameter modifiedCallParams =
overrideGasLimitAndPrice(callParams, blockHeader.getGasLimit());
@ -72,44 +59,48 @@ public class EthEstimateGas extends AbstractEstimateGas {
final boolean isAllowExceedingBalance = !callParams.isMaybeStrict().orElse(Boolean.FALSE);
final EstimateGasOperationTracer operationTracer = new EstimateGasOperationTracer();
var gasUsed =
executeSimulation(
blockHeader, modifiedCallParams, operationTracer, isAllowExceedingBalance);
if (gasUsed.isEmpty()) {
LOG.error("gasUsed is empty after simulating transaction.");
return errorResponse(requestContext, RpcErrorType.INTERNAL_ERROR);
}
// if the transaction is invalid or doesn't have enough gas with the max it never will!
if (gasUsed.get().isInvalid() || !gasUsed.get().isSuccessful()) {
return errorResponse(requestContext, gasUsed.get());
final var transactionValidationParams =
ImmutableTransactionValidationParams.builder()
.from(TransactionValidationParams.transactionSimulator())
.isAllowExceedingBalance(isAllowExceedingBalance)
.build();
LOG.debug("Processing transaction with params: {}", modifiedCallParams);
final var maybeResult =
transactionSimulator.process(
modifiedCallParams, transactionValidationParams, operationTracer, blockHeader);
final Optional<JsonRpcErrorResponse> maybeErrorResponse =
validateSimulationResult(requestContext, maybeResult);
if (maybeErrorResponse.isPresent()) {
return maybeErrorResponse.get();
}
var low = gasUsed.get().result().getEstimateGasUsedByTransaction();
var lowResult =
executeSimulation(
blockHeader,
final var result = maybeResult.get();
long low = result.result().getEstimateGasUsedByTransaction();
final var lowResult =
transactionSimulator.process(
overrideGasLimitAndPrice(callParams, low),
transactionValidationParams,
operationTracer,
isAllowExceedingBalance);
blockHeader);
if (lowResult.isPresent() && lowResult.get().isSuccessful()) {
return new JsonRpcSuccessResponse(requestContext.getRequest().getId(), Quantity.create(low));
return Quantity.create(low);
}
var high = processEstimateGas(gasUsed.get(), operationTracer);
var mid = high;
while (low + 1 < high) {
mid = (high + low) / 2;
long high = processEstimateGas(result, operationTracer);
long mid;
while (low + 1 < high) {
mid = (low + high) / 2;
var binarySearchResult =
executeSimulation(
blockHeader,
transactionSimulator.process(
overrideGasLimitAndPrice(callParams, mid),
transactionValidationParams,
operationTracer,
isAllowExceedingBalance);
blockHeader);
if (binarySearchResult.isEmpty() || !binarySearchResult.get().isSuccessful()) {
low = mid;
} else {
@ -117,21 +108,23 @@ public class EthEstimateGas extends AbstractEstimateGas {
}
}
return new JsonRpcSuccessResponse(requestContext.getRequest().getId(), Quantity.create(high));
return Quantity.create(high);
}
private Optional<TransactionSimulatorResult> executeSimulation(
final BlockHeader blockHeader,
final CallParameter modifiedCallParams,
final EstimateGasOperationTracer operationTracer,
final boolean allowExceedingBalance) {
return transactionSimulator.process(
modifiedCallParams,
ImmutableTransactionValidationParams.builder()
.from(TransactionValidationParams.transactionSimulator())
.isAllowExceedingBalance(allowExceedingBalance)
.build(),
operationTracer,
blockHeader.getNumber());
private Optional<JsonRpcErrorResponse> validateSimulationResult(
final JsonRpcRequestContext requestContext,
final Optional<TransactionSimulatorResult> maybeResult) {
if (maybeResult.isEmpty()) {
LOG.error("No result after simulating transaction.");
return Optional.of(
new JsonRpcErrorResponse(
requestContext.getRequest().getId(), RpcErrorType.INTERNAL_ERROR));
}
// if the transaction is invalid or doesn't have enough gas with the max it never will!
if (maybeResult.get().isInvalid() || !maybeResult.get().isSuccessful()) {
return Optional.of(errorResponse(requestContext, maybeResult.get()));
}
return Optional.empty();
}
}

@ -16,7 +16,7 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@ -24,7 +24,6 @@ import static org.mockito.Mockito.when;
import org.hyperledger.besu.datatypes.AccessListEntry;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
@ -70,7 +69,9 @@ public class EthCreateAccessListTest {
private final String METHOD = "eth_createAccessList";
private EthCreateAccessList method;
@Mock private BlockHeader blockHeader;
@Mock private BlockHeader latestBlockHeader;
@Mock private BlockHeader finalizedBlockHeader;
@Mock private BlockHeader genesisBlockHeader;
@Mock private Blockchain blockchain;
@Mock private BlockchainQueries blockchainQueries;
@Mock private TransactionSimulator transactionSimulator;
@ -80,16 +81,18 @@ public class EthCreateAccessListTest {
public void setUp() {
when(blockchainQueries.getBlockchain()).thenReturn(blockchain);
when(blockchainQueries.getWorldStateArchive()).thenReturn(worldStateArchive);
when(blockchain.getChainHeadHash())
.thenReturn(
Hash.fromHexString(
"0x3f07a9c83155594c000642e7d60e8a8a00038d03e9849171a05ed0e2d47acbb3"));
when(blockchain.getBlockHeader(
Hash.fromHexString(
"0x3f07a9c83155594c000642e7d60e8a8a00038d03e9849171a05ed0e2d47acbb3")))
.thenReturn(Optional.of(blockHeader));
when(blockHeader.getGasLimit()).thenReturn(Long.MAX_VALUE);
when(blockHeader.getNumber()).thenReturn(1L);
when(blockchainQueries.headBlockNumber()).thenReturn(2L);
when(blockchainQueries.getBlockHeaderByNumber(0L)).thenReturn(Optional.of(genesisBlockHeader));
when(blockchainQueries.finalizedBlockHeader()).thenReturn(Optional.of(finalizedBlockHeader));
when(blockchainQueries.getBlockHeaderByNumber(1L))
.thenReturn(Optional.of(finalizedBlockHeader));
when(genesisBlockHeader.getGasLimit()).thenReturn(Long.MAX_VALUE);
when(genesisBlockHeader.getNumber()).thenReturn(0L);
when(finalizedBlockHeader.getGasLimit()).thenReturn(Long.MAX_VALUE);
when(finalizedBlockHeader.getNumber()).thenReturn(1L);
when(blockchain.getChainHeadHeader()).thenReturn(latestBlockHeader);
when(latestBlockHeader.getGasLimit()).thenReturn(Long.MAX_VALUE);
when(latestBlockHeader.getNumber()).thenReturn(2L);
when(worldStateArchive.isWorldStateAvailable(any(), any())).thenReturn(true);
method = new EthCreateAccessList(blockchainQueries, transactionSimulator);
@ -105,18 +108,22 @@ public class EthCreateAccessListTest {
new JsonRpcRequest("2.0", METHOD, new Object[] {callParameter}));
}
private JsonRpcRequestContext ethCreateAccessListRequest(
final CallParameter callParameter, final String blockParam) {
return new JsonRpcRequestContext(
new JsonRpcRequest("2.0", METHOD, new Object[] {callParameter, blockParam}));
}
@Test
public void shouldReturnGasEstimateWhenTransientLegacyTransactionProcessorReturnsResultSuccess() {
final JsonRpcRequestContext request =
ethCreateAccessListRequest(legacyTransactionCallParameter(Wei.ZERO));
mockTransactionSimulatorResult(true, false, 1L);
mockTransactionSimulatorResult(true, false, 1L, latestBlockHeader);
final JsonRpcResponse expectedResponse =
new JsonRpcSuccessResponse(null, new CreateAccessListResult(new ArrayList<>(), 1L));
Assertions.assertThat(method.response(request))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
@ -124,21 +131,19 @@ public class EthCreateAccessListTest {
final Wei gasPrice = Wei.of(1000);
final JsonRpcRequestContext request =
ethCreateAccessListRequest(legacyTransactionCallParameter(gasPrice));
mockTransactionSimulatorResult(true, false, 1L);
mockTransactionSimulatorResult(true, false, 1L, latestBlockHeader);
final JsonRpcResponse expectedResponse =
new JsonRpcSuccessResponse(null, new CreateAccessListResult(new ArrayList<>(), 1L));
Assertions.assertThat(method.response(request))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void shouldReturnGasEstimateErrorWhenGasPricePresentForEip1559Transaction() {
final JsonRpcRequestContext request =
ethCreateAccessListRequest(eip1559TransactionCallParameter(Optional.of(Wei.of(10))));
mockTransactionSimulatorResult(false, false, 1L);
mockTransactionSimulatorResult(false, false, 1L, latestBlockHeader);
Assertions.assertThatThrownBy(() -> method.response(request))
.isInstanceOf(InvalidJsonRpcParameters.class)
@ -150,29 +155,25 @@ public class EthCreateAccessListTest {
when(worldStateArchive.isWorldStateAvailable(any(), any())).thenReturn(false);
final JsonRpcRequestContext request =
ethCreateAccessListRequest(legacyTransactionCallParameter(Wei.ZERO));
mockTransactionSimulatorResult(false, false, 1L);
mockTransactionSimulatorResult(false, false, 1L, latestBlockHeader);
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(null, RpcErrorType.WORLD_STATE_UNAVAILABLE);
Assertions.assertThat(method.response(request))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void shouldReturnErrorWhenTransactionReverted() {
final JsonRpcRequestContext request =
ethCreateAccessListRequest(legacyTransactionCallParameter(Wei.ZERO));
mockTransactionSimulatorResult(false, true, 1L);
mockTransactionSimulatorResult(false, true, 1L, latestBlockHeader);
final String errorReason = "0x00";
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(null, new JsonRpcError(RpcErrorType.REVERT_ERROR, errorReason));
Assertions.assertThat(method.response(request))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
@ -182,12 +183,10 @@ public class EthCreateAccessListTest {
new JsonRpcSuccessResponse(null, new CreateAccessListResult(expectedAccessList, 1L));
final JsonRpcRequestContext request =
ethCreateAccessListRequest(eip1559TransactionCallParameter());
mockTransactionSimulatorResult(true, false, 1L);
mockTransactionSimulatorResult(true, false, 1L, latestBlockHeader);
Assertions.assertThat(method.response(request))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
verify(transactionSimulator, times(1)).process(any(), any(), any(), anyLong());
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
verify(transactionSimulator, times(1)).process(any(), any(), any(), eq(latestBlockHeader));
}
@Test
@ -204,11 +203,11 @@ public class EthCreateAccessListTest {
final AccessListOperationTracer tracer = createMockTracer(expectedAccessList);
// Set TransactionSimulator.process response
mockTransactionSimulatorResult(true, false, 1L);
Assertions.assertThat(responseWithMockTracer(request, tracer))
mockTransactionSimulatorResult(true, false, 1L, latestBlockHeader);
assertThat(responseWithMockTracer(request, tracer))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
verify(transactionSimulator, times(2)).process(any(), any(), any(), anyLong());
verify(transactionSimulator, times(2)).process(any(), any(), any(), eq(latestBlockHeader));
}
@Test
@ -223,11 +222,9 @@ public class EthCreateAccessListTest {
ethCreateAccessListRequest(eip1559TransactionCallParameter(accessListParam));
// Set TransactionSimulator.process response
mockTransactionSimulatorResult(true, false, 1L);
Assertions.assertThat(method.response(request))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
verify(transactionSimulator, times(1)).process(any(), any(), any(), anyLong());
mockTransactionSimulatorResult(true, false, 1L, latestBlockHeader);
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
verify(transactionSimulator, times(1)).process(any(), any(), any(), eq(latestBlockHeader));
}
@Test
@ -244,11 +241,11 @@ public class EthCreateAccessListTest {
final AccessListOperationTracer tracer = createMockTracer(expectedAccessList);
// Set TransactionSimulator.process response
mockTransactionSimulatorResult(true, false, 1L);
Assertions.assertThat(responseWithMockTracer(request, tracer))
mockTransactionSimulatorResult(true, false, 1L, latestBlockHeader);
assertThat(responseWithMockTracer(request, tracer))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
verify(transactionSimulator, times(1)).process(any(), any(), any(), anyLong());
verify(transactionSimulator, times(1)).process(any(), any(), any(), eq(latestBlockHeader));
}
@Test
@ -268,11 +265,51 @@ public class EthCreateAccessListTest {
final AccessListOperationTracer tracer = createMockTracer(expectedAccessList);
// Set TransactionSimulator.process response
mockTransactionSimulatorResult(true, false, 1L);
Assertions.assertThat(responseWithMockTracer(request, tracer))
mockTransactionSimulatorResult(true, false, 1L, latestBlockHeader);
assertThat(responseWithMockTracer(request, tracer))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
verify(transactionSimulator, times(2)).process(any(), any(), any(), eq(latestBlockHeader));
}
@Test
public void shouldReturnAccessListWhenBlockTagParamIsPresent() {
final JsonRpcRequestContext request =
ethCreateAccessListRequest(eip1559TransactionCallParameter(), "finalized");
// Create a list with one access list entry
final List<AccessListEntry> expectedAccessList = createAccessList();
// expect a list with the mocked access list
final JsonRpcResponse expectedResponse =
new JsonRpcSuccessResponse(null, new CreateAccessListResult(expectedAccessList, 1L));
final AccessListOperationTracer tracer = createMockTracer(expectedAccessList);
// Set TransactionSimulator.process response
mockTransactionSimulatorResult(true, false, 1L, finalizedBlockHeader);
assertThat(responseWithMockTracer(request, tracer))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
verify(transactionSimulator, times(2)).process(any(), any(), any(), eq(finalizedBlockHeader));
}
@Test
public void shouldReturnAccessListWhenBlockNumberParamIsPresent() {
final JsonRpcRequestContext request =
ethCreateAccessListRequest(eip1559TransactionCallParameter(), "0x0");
// Create a list with one access list entry
final List<AccessListEntry> expectedAccessList = createAccessList();
// expect a list with the mocked access list
final JsonRpcResponse expectedResponse =
new JsonRpcSuccessResponse(null, new CreateAccessListResult(expectedAccessList, 1L));
final AccessListOperationTracer tracer = createMockTracer(expectedAccessList);
// Set TransactionSimulator.process response
mockTransactionSimulatorResult(true, false, 1L, genesisBlockHeader);
assertThat(responseWithMockTracer(request, tracer))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
verify(transactionSimulator, times(2)).process(any(), any(), any(), anyLong());
verify(transactionSimulator, times(2)).process(any(), any(), any(), eq(genesisBlockHeader));
}
private JsonRpcResponse responseWithMockTracer(
@ -292,9 +329,12 @@ public class EthCreateAccessListTest {
}
private void mockTransactionSimulatorResult(
final boolean isSuccessful, final boolean isReverted, final long estimateGas) {
final boolean isSuccessful,
final boolean isReverted,
final long estimateGas,
final BlockHeader blockHeader) {
final TransactionSimulatorResult mockTxSimResult = mock(TransactionSimulatorResult.class);
when(transactionSimulator.process(any(), any(), any(), anyLong()))
when(transactionSimulator.process(any(), any(), any(), eq(blockHeader)))
.thenReturn(Optional.of(mockTxSimResult));
final TransactionProcessingResult mockResult = mock(TransactionProcessingResult.class);
when(mockResult.getEstimateGasUsedByTransaction()).thenReturn(estimateGas);

@ -22,7 +22,6 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
@ -66,7 +65,9 @@ public class EthEstimateGasTest {
private EthEstimateGas method;
@Mock private BlockHeader blockHeader;
@Mock private BlockHeader latestBlockHeader;
@Mock private BlockHeader finalizedBlockHeader;
@Mock private BlockHeader genesisBlockHeader;
@Mock private Blockchain blockchain;
@Mock private BlockchainQueries blockchainQueries;
@Mock private TransactionSimulator transactionSimulator;
@ -76,16 +77,18 @@ public class EthEstimateGasTest {
public void setUp() {
when(blockchainQueries.getBlockchain()).thenReturn(blockchain);
when(blockchainQueries.getWorldStateArchive()).thenReturn(worldStateArchive);
when(blockchain.getChainHeadHash())
.thenReturn(
Hash.fromHexString(
"0x3f07a9c83155594c000642e7d60e8a8a00038d03e9849171a05ed0e2d47acbb3"));
when(blockchain.getBlockHeader(
Hash.fromHexString(
"0x3f07a9c83155594c000642e7d60e8a8a00038d03e9849171a05ed0e2d47acbb3")))
.thenReturn(Optional.of(blockHeader));
when(blockHeader.getGasLimit()).thenReturn(Long.MAX_VALUE);
when(blockHeader.getNumber()).thenReturn(1L);
when(blockchainQueries.headBlockNumber()).thenReturn(2L);
when(blockchainQueries.getBlockHeaderByNumber(0L)).thenReturn(Optional.of(genesisBlockHeader));
when(blockchainQueries.finalizedBlockHeader()).thenReturn(Optional.of(finalizedBlockHeader));
when(blockchainQueries.getBlockHeaderByNumber(1L))
.thenReturn(Optional.of(finalizedBlockHeader));
when(genesisBlockHeader.getGasLimit()).thenReturn(Long.MAX_VALUE);
when(genesisBlockHeader.getNumber()).thenReturn(0L);
when(finalizedBlockHeader.getGasLimit()).thenReturn(Long.MAX_VALUE);
when(finalizedBlockHeader.getNumber()).thenReturn(1L);
when(blockchain.getChainHeadHeader()).thenReturn(latestBlockHeader);
when(latestBlockHeader.getGasLimit()).thenReturn(Long.MAX_VALUE);
when(latestBlockHeader.getNumber()).thenReturn(2L);
when(worldStateArchive.isWorldStateAvailable(any(), any())).thenReturn(true);
method = new EthEstimateGas(blockchainQueries, transactionSimulator);
@ -104,15 +107,13 @@ public class EthEstimateGasTest {
eq(modifiedLegacyTransactionCallParameter(Wei.ZERO)),
any(TransactionValidationParams.class),
any(OperationTracer.class),
eq(1L)))
eq(latestBlockHeader)))
.thenReturn(Optional.empty());
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(null, RpcErrorType.INTERNAL_ERROR);
Assertions.assertThat(method.response(request))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
@ -122,28 +123,24 @@ public class EthEstimateGasTest {
eq(modifiedEip1559TransactionCallParameter()),
any(TransactionValidationParams.class),
any(OperationTracer.class),
eq(1L)))
eq(latestBlockHeader)))
.thenReturn(Optional.empty());
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(null, RpcErrorType.INTERNAL_ERROR);
Assertions.assertThat(method.response(request))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void shouldReturnGasEstimateWhenTransientLegacyTransactionProcessorReturnsResultSuccess() {
final JsonRpcRequestContext request =
ethEstimateGasRequest(defaultLegacyTransactionCallParameter(Wei.ZERO));
mockTransientProcessorResultGasEstimate(1L, true, false);
mockTransientProcessorResultGasEstimate(1L, true, false, latestBlockHeader);
final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(null, Quantity.create(1L));
Assertions.assertThat(method.response(request))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
@ -151,20 +148,19 @@ public class EthEstimateGasTest {
final Wei gasPrice = Wei.of(1000);
final JsonRpcRequestContext request =
ethEstimateGasRequest(defaultLegacyTransactionCallParameter(gasPrice));
mockTransientProcessorResultGasEstimate(1L, true, gasPrice, Optional.empty());
mockTransientProcessorResultGasEstimate(
1L, true, gasPrice, Optional.empty(), latestBlockHeader);
final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(null, Quantity.create(1L));
Assertions.assertThat(method.response(request))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void shouldReturnGasEstimateErrorWhenGasPricePresentForEip1559Transaction() {
final JsonRpcRequestContext request =
ethEstimateGasRequest(eip1559TransactionCallParameter(Optional.of(Wei.of(10))));
mockTransientProcessorResultGasEstimate(1L, false, false);
mockTransientProcessorResultGasEstimate(1L, false, false, latestBlockHeader);
Assertions.assertThatThrownBy(() -> method.response(request))
.isInstanceOf(InvalidJsonRpcParameters.class)
.hasMessageContaining("gasPrice cannot be used with maxFeePerGas or maxPriorityFeePerGas");
@ -174,12 +170,10 @@ public class EthEstimateGasTest {
public void
shouldReturnGasEstimateWhenTransientEip1559TransactionProcessorReturnsResultSuccess() {
final JsonRpcRequestContext request = ethEstimateGasRequest(eip1559TransactionCallParameter());
mockTransientProcessorResultGasEstimate(1L, true, false);
mockTransientProcessorResultGasEstimate(1L, true, false, latestBlockHeader);
final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(null, Quantity.create(1L));
Assertions.assertThat(method.response(request))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
@ -187,28 +181,24 @@ public class EthEstimateGasTest {
shouldReturnGasEstimateErrorWhenTransientLegacyTransactionProcessorReturnsResultFailure() {
final JsonRpcRequestContext request =
ethEstimateGasRequest(defaultLegacyTransactionCallParameter(Wei.ZERO));
mockTransientProcessorResultGasEstimate(1L, false, false);
mockTransientProcessorResultGasEstimate(1L, false, false, latestBlockHeader);
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(null, RpcErrorType.INTERNAL_ERROR);
Assertions.assertThat(method.response(request))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void
shouldReturnGasEstimateErrorWhenTransientEip1559TransactionProcessorReturnsResultFailure() {
final JsonRpcRequestContext request = ethEstimateGasRequest(eip1559TransactionCallParameter());
mockTransientProcessorResultGasEstimate(1L, false, false);
mockTransientProcessorResultGasEstimate(1L, false, false, latestBlockHeader);
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(null, RpcErrorType.INTERNAL_ERROR);
Assertions.assertThat(method.response(request))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
@ -217,7 +207,8 @@ public class EthEstimateGasTest {
ethEstimateGasRequest(defaultLegacyTransactionCallParameter(Wei.ZERO));
mockTransientProcessorResultTxInvalidReason(
TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE,
"transaction up-front cost 10 exceeds transaction sender account balance 5");
"transaction up-front cost 10 exceeds transaction sender account balance 5",
latestBlockHeader);
final ValidationResult<TransactionInvalidReason> validationResult =
ValidationResult.invalid(
@ -226,9 +217,7 @@ public class EthEstimateGasTest {
final JsonRpcError rpcError = JsonRpcError.from(validationResult);
final JsonRpcResponse expectedResponse = new JsonRpcErrorResponse(null, rpcError);
Assertions.assertThat(method.response(request))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
@ -236,7 +225,8 @@ public class EthEstimateGasTest {
final JsonRpcRequestContext request = ethEstimateGasRequest(eip1559TransactionCallParameter());
mockTransientProcessorResultTxInvalidReason(
TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE,
"transaction up-front cost 10 exceeds transaction sender account balance 5");
"transaction up-front cost 10 exceeds transaction sender account balance 5",
latestBlockHeader);
final ValidationResult<TransactionInvalidReason> validationResult =
ValidationResult.invalid(
TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE,
@ -244,9 +234,7 @@ public class EthEstimateGasTest {
final JsonRpcError rpcError = JsonRpcError.from(validationResult);
final JsonRpcResponse expectedResponse = new JsonRpcErrorResponse(null, rpcError);
Assertions.assertThat(method.response(request))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
@ -254,21 +242,21 @@ public class EthEstimateGasTest {
when(worldStateArchive.isWorldStateAvailable(any(), any())).thenReturn(false);
final JsonRpcRequestContext request =
ethEstimateGasRequest(defaultLegacyTransactionCallParameter(Wei.ZERO));
mockTransientProcessorResultGasEstimate(1L, false, false);
mockTransientProcessorResultGasEstimate(1L, false, false, latestBlockHeader);
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(null, RpcErrorType.WORLD_STATE_UNAVAILABLE);
JsonRpcResponse theResponse = method.response(request);
Assertions.assertThat(theResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
assertThat(theResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void shouldReturnErrorWhenTransactionReverted() {
final JsonRpcRequestContext request =
ethEstimateGasRequest(defaultLegacyTransactionCallParameter(Wei.ZERO));
mockTransientProcessorResultGasEstimate(1L, false, true);
mockTransientProcessorResultGasEstimate(1L, false, true, latestBlockHeader);
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(null, new JsonRpcError(RpcErrorType.REVERT_ERROR, "0x00"));
@ -278,7 +266,7 @@ public class EthEstimateGasTest {
final JsonRpcResponse actualResponse = method.response(request);
Assertions.assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
assertThat(((JsonRpcErrorResponse) actualResponse).getError().getMessage())
.isEqualTo("Execution reverted");
@ -296,7 +284,8 @@ public class EthEstimateGasTest {
+ "00002545524332303a207472616e736665722066726f6d20746865207a65726f20"
+ "61646472657373000000000000000000000000000000000000000000000000000000";
mockTransientProcessorTxReverted(1L, false, Bytes.fromHexString(executionRevertedReason));
mockTransientProcessorTxReverted(
1L, false, Bytes.fromHexString(executionRevertedReason), latestBlockHeader);
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(
@ -307,7 +296,7 @@ public class EthEstimateGasTest {
final JsonRpcResponse actualResponse = method.response(request);
Assertions.assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
assertThat(((JsonRpcErrorResponse) actualResponse).getError().getMessage())
.isEqualTo("Execution reverted: ERC20: transfer from the zero address");
@ -323,7 +312,8 @@ public class EthEstimateGasTest {
"0x08c379a000000000000000000000000000000000000000000000000000000000"
+ "123451234512345123451234512345123451234512345123451234512345123451";
mockTransientProcessorTxReverted(1L, false, Bytes.fromHexString(invalidRevertReason));
mockTransientProcessorTxReverted(
1L, false, Bytes.fromHexString(invalidRevertReason), latestBlockHeader);
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(
@ -334,7 +324,7 @@ public class EthEstimateGasTest {
final JsonRpcResponse actualResponse = method.response(request);
Assertions.assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
assertThat(((JsonRpcErrorResponse) actualResponse).getError().getMessage())
.isEqualTo("Execution reverted: ABI decode error");
@ -344,7 +334,7 @@ public class EthEstimateGasTest {
public void shouldIgnoreSenderBalanceAccountWhenStrictModeDisabled() {
final JsonRpcRequestContext request =
ethEstimateGasRequest(defaultLegacyTransactionCallParameter(Wei.ZERO));
mockTransientProcessorResultGasEstimate(1L, false, true);
mockTransientProcessorResultGasEstimate(1L, false, true, latestBlockHeader);
method.response(request);
@ -357,14 +347,14 @@ public class EthEstimateGasTest {
.isAllowExceedingBalance(true)
.build()),
any(OperationTracer.class),
eq(1L));
eq(latestBlockHeader));
}
@Test
public void shouldNotIgnoreSenderBalanceAccountWhenStrictModeEnabled() {
final JsonRpcRequestContext request =
ethEstimateGasRequest(legacyTransactionCallParameter(Wei.ZERO, true));
mockTransientProcessorResultGasEstimate(1L, false, true);
mockTransientProcessorResultGasEstimate(1L, false, true, latestBlockHeader);
method.response(request);
@ -377,7 +367,7 @@ public class EthEstimateGasTest {
.isAllowExceedingBalance(false)
.build()),
any(OperationTracer.class),
eq(1L));
eq(latestBlockHeader));
}
@Test
@ -385,22 +375,44 @@ public class EthEstimateGasTest {
final JsonRpcRequestContext request =
ethEstimateGasRequest(defaultLegacyTransactionCallParameter(Wei.ZERO));
mockTransientProcessorResultTxInvalidReason(
TransactionInvalidReason.EXECUTION_HALTED, "INVALID_OPERATION");
TransactionInvalidReason.EXECUTION_HALTED, "INVALID_OPERATION", latestBlockHeader);
final ValidationResult<TransactionInvalidReason> validationResult =
ValidationResult.invalid(TransactionInvalidReason.EXECUTION_HALTED, "INVALID_OPERATION");
final JsonRpcError rpcError = JsonRpcError.from(validationResult);
final JsonRpcResponse expectedResponse = new JsonRpcErrorResponse(null, rpcError);
Assertions.assertThat(method.response(request))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void shouldUseBlockTagParamWhenPresent() {
final JsonRpcRequestContext request =
ethEstimateGasRequest(eip1559TransactionCallParameter(), "finalized");
mockTransientProcessorResultGasEstimate(1L, true, false, finalizedBlockHeader);
final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(null, Quantity.create(1L));
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void shouldUseBlockNumberParamWhenPresent() {
final JsonRpcRequestContext request =
ethEstimateGasRequest(eip1559TransactionCallParameter(), "0x0");
mockTransientProcessorResultGasEstimate(1L, true, false, genesisBlockHeader);
final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(null, Quantity.create(1L));
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
}
private void mockTransientProcessorResultTxInvalidReason(
final TransactionInvalidReason reason, final String validationFailedErrorMessage) {
final TransactionInvalidReason reason,
final String validationFailedErrorMessage,
final BlockHeader blockHeader) {
final TransactionSimulatorResult mockTxSimResult =
getMockTransactionSimulatorResult(false, 0, Wei.ZERO, Optional.empty());
getMockTransactionSimulatorResult(false, 0, Wei.ZERO, Optional.empty(), blockHeader);
when(mockTxSimResult.getValidationResult())
.thenReturn(
validationFailedErrorMessage == null
@ -409,45 +421,55 @@ public class EthEstimateGasTest {
}
private void mockTransientProcessorTxReverted(
final long estimateGas, final boolean isSuccessful, final Bytes revertReason) {
final long estimateGas,
final boolean isSuccessful,
final Bytes revertReason,
final BlockHeader blockHeader) {
mockTransientProcessorResultGasEstimate(
estimateGas, isSuccessful, Wei.ZERO, Optional.of(revertReason));
estimateGas, isSuccessful, Wei.ZERO, Optional.of(revertReason), blockHeader);
}
private void mockTransientProcessorResultGasEstimate(
final long estimateGas, final boolean isSuccessful, final boolean isReverted) {
final long estimateGas,
final boolean isSuccessful,
final boolean isReverted,
final BlockHeader blockHeader) {
mockTransientProcessorResultGasEstimate(
estimateGas,
isSuccessful,
Wei.ZERO,
isReverted ? Optional.of(Bytes.of(0)) : Optional.empty());
isReverted ? Optional.of(Bytes.of(0)) : Optional.empty(),
blockHeader);
}
private void mockTransientProcessorResultGasEstimate(
final long estimateGas,
final boolean isSuccessful,
final Wei gasPrice,
final Optional<Bytes> revertReason) {
getMockTransactionSimulatorResult(isSuccessful, estimateGas, gasPrice, revertReason);
final Optional<Bytes> revertReason,
final BlockHeader blockHeader) {
getMockTransactionSimulatorResult(
isSuccessful, estimateGas, gasPrice, revertReason, blockHeader);
}
private TransactionSimulatorResult getMockTransactionSimulatorResult(
final boolean isSuccessful,
final long estimateGas,
final Wei gasPrice,
final Optional<Bytes> revertReason) {
final Optional<Bytes> revertReason,
final BlockHeader blockHeader) {
final TransactionSimulatorResult mockTxSimResult = mock(TransactionSimulatorResult.class);
when(transactionSimulator.process(
eq(modifiedLegacyTransactionCallParameter(gasPrice)),
any(TransactionValidationParams.class),
any(OperationTracer.class),
eq(1L)))
eq(blockHeader)))
.thenReturn(Optional.of(mockTxSimResult));
when(transactionSimulator.process(
eq(modifiedEip1559TransactionCallParameter()),
any(TransactionValidationParams.class),
any(OperationTracer.class),
eq(1L)))
eq(blockHeader)))
.thenReturn(Optional.of(mockTxSimResult));
final TransactionProcessingResult mockResult = mock(TransactionProcessingResult.class);
@ -523,4 +545,10 @@ public class EthEstimateGasTest {
return new JsonRpcRequestContext(
new JsonRpcRequest("2.0", "eth_estimateGas", new Object[] {callParameter}));
}
private JsonRpcRequestContext ethEstimateGasRequest(
final CallParameter callParameter, final String blockParam) {
return new JsonRpcRequestContext(
new JsonRpcRequest("2.0", "eth_estimateGas", new Object[] {callParameter, blockParam}));
}
}

@ -0,0 +1,21 @@
{
"request": {
"id": 3,
"jsonrpc": "2.0",
"method": "eth_estimateGas",
"params": [
{
"to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"from": "0x6295ee1b4f6dd65047762f924ecd367c17eabf8f",
"data": "0x123456"
},
"0x1"
]
},
"response": {
"jsonrpc": "2.0",
"id": 3,
"result": "0x52d4"
},
"statusCode": 200
}

@ -0,0 +1,21 @@
{
"request": {
"id": 3,
"jsonrpc": "2.0",
"method": "eth_estimateGas",
"params": [
{
"to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"from": "0x6295ee1b4f6dd65047762f924ecd367c17eabf8f",
"data": "0x123456"
},
"latest"
]
},
"response": {
"jsonrpc": "2.0",
"id": 3,
"result": "0x5238"
},
"statusCode": 200
}

@ -0,0 +1,21 @@
{
"request": {
"id": 3,
"jsonrpc": "2.0",
"method": "eth_estimateGas",
"params": [
{
"to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"from": "0x6295ee1b4f6dd65047762f924ecd367c17eabf8f",
"gas": "0x1"
},
"0xa"
]
},
"response": {
"jsonrpc": "2.0",
"id": 3,
"result": "0x5208"
},
"statusCode": 200
}

@ -0,0 +1,21 @@
{
"request": {
"id": 3,
"jsonrpc": "2.0",
"method": "eth_estimateGas",
"params": [
{
"from": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"to": "0x8888f1f195afa192cfee860698584c030f4c9db1",
"value": "0x1"
},
"earliest"
]
},
"response": {
"jsonrpc": "2.0",
"id": 3,
"result": "0x5208"
},
"statusCode": 200
}
Loading…
Cancel
Save