eth_estimateGas state overrides (#7890)

* parse account overrides parameter for eth_estimateGas

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

* changelog

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

---------

Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com>
main
Sally MacFarlane 1 day ago committed by GitHub
parent fe462896d2
commit 9718fd58e7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      CHANGELOG.md
  2. 25
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGas.java
  3. 3
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCallTest.java
  4. 47
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGasTest.java
  5. 15
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java

@ -15,8 +15,8 @@
- 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)
- Add a new metric collector for counters which get their value from suppliers [#7894](https://github.com/hyperledger/besu/pull/7894) - Add a new metric collector for counters which get their value from suppliers [#7894](https://github.com/hyperledger/besu/pull/7894)
- Add account and state overrides to `eth_call` [#7801](https://github.com/hyperledger/besu/pull/7801) and `eth_estimateGas` [#7890](https://github.com/hyperledger/besu/pull/7890)
### 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)

@ -16,7 +16,9 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcRequestException;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonCallParameter; 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.JsonRpcErrorResponse; 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.response.RpcErrorType;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity;
@ -27,10 +29,12 @@ import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.transaction.CallParameter; import org.hyperledger.besu.ethereum.transaction.CallParameter;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator; import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult; import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult;
import org.hyperledger.besu.ethereum.util.AccountOverrideMap;
import org.hyperledger.besu.evm.tracing.EstimateGasOperationTracer; import org.hyperledger.besu.evm.tracing.EstimateGasOperationTracer;
import java.util.Optional; import java.util.Optional;
import com.google.common.annotations.VisibleForTesting;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -55,6 +59,8 @@ public class EthEstimateGas extends AbstractEstimateGas {
final CallParameter modifiedCallParams = final CallParameter modifiedCallParams =
overrideGasLimitAndPrice(callParams, blockHeader.getGasLimit()); overrideGasLimitAndPrice(callParams, blockHeader.getGasLimit());
Optional<AccountOverrideMap> maybeStateOverrides = getAddressAccountOverrideMap(requestContext);
// TODO implement for block overrides
final boolean isAllowExceedingBalance = !callParams.isMaybeStrict().orElse(Boolean.FALSE); final boolean isAllowExceedingBalance = !callParams.isMaybeStrict().orElse(Boolean.FALSE);
@ -68,7 +74,11 @@ public class EthEstimateGas extends AbstractEstimateGas {
LOG.debug("Processing transaction with params: {}", modifiedCallParams); LOG.debug("Processing transaction with params: {}", modifiedCallParams);
final var maybeResult = final var maybeResult =
transactionSimulator.process( transactionSimulator.process(
modifiedCallParams, transactionValidationParams, operationTracer, blockHeader); modifiedCallParams,
maybeStateOverrides,
transactionValidationParams,
operationTracer,
blockHeader);
final Optional<JsonRpcErrorResponse> maybeErrorResponse = final Optional<JsonRpcErrorResponse> maybeErrorResponse =
validateSimulationResult(requestContext, maybeResult); validateSimulationResult(requestContext, maybeResult);
@ -81,6 +91,7 @@ public class EthEstimateGas extends AbstractEstimateGas {
final var lowResult = final var lowResult =
transactionSimulator.process( transactionSimulator.process(
overrideGasLimitAndPrice(callParams, low), overrideGasLimitAndPrice(callParams, low),
maybeStateOverrides,
transactionValidationParams, transactionValidationParams,
operationTracer, operationTracer,
blockHeader); blockHeader);
@ -97,6 +108,7 @@ public class EthEstimateGas extends AbstractEstimateGas {
var binarySearchResult = var binarySearchResult =
transactionSimulator.process( transactionSimulator.process(
overrideGasLimitAndPrice(callParams, mid), overrideGasLimitAndPrice(callParams, mid),
maybeStateOverrides,
transactionValidationParams, transactionValidationParams,
operationTracer, operationTracer,
blockHeader); blockHeader);
@ -127,4 +139,15 @@ public class EthEstimateGas extends AbstractEstimateGas {
} }
return Optional.empty(); return Optional.empty();
} }
@VisibleForTesting
protected Optional<AccountOverrideMap> getAddressAccountOverrideMap(
final JsonRpcRequestContext request) {
try {
return request.getOptionalParameter(2, AccountOverrideMap.class);
} catch (JsonRpcParameter.JsonRpcParameterException e) {
throw new InvalidJsonRpcRequestException(
"Invalid account overrides parameter (index 2)", RpcErrorType.INVALID_CALL_PARAMS, e);
}
}
} }

@ -342,7 +342,8 @@ public class EthCallTest {
public void shouldUseCorrectBlockNumberWhenEarliest() { public void shouldUseCorrectBlockNumberWhenEarliest() {
final JsonRpcRequestContext request = ethCallRequest(callParameter(), "earliest"); final JsonRpcRequestContext request = ethCallRequest(callParameter(), "earliest");
when(blockchainQueries.getBlockHashByNumber(anyLong())).thenReturn(Optional.of(Hash.ZERO)); when(blockchainQueries.getBlockHashByNumber(anyLong())).thenReturn(Optional.of(Hash.ZERO));
when(transactionSimulator.process(any(), any(), any(), any(), any())) when(transactionSimulator.process(
any(), any(), any(TransactionValidationParams.class), any(), any(BlockHeader.class)))
.thenReturn(Optional.empty()); .thenReturn(Optional.empty());
when(blockchainQueries.getBlockHeaderByHash(Hash.ZERO)) when(blockchainQueries.getBlockHeaderByHash(Hash.ZERO))
.thenReturn(Optional.of(mock(BlockHeader.class))); .thenReturn(Optional.of(mock(BlockHeader.class)));

@ -44,6 +44,8 @@ import org.hyperledger.besu.ethereum.transaction.CallParameter;
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason; import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator; import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult; import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult;
import org.hyperledger.besu.ethereum.util.AccountOverride;
import org.hyperledger.besu.ethereum.util.AccountOverrideMap;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.evm.tracing.OperationTracer;
@ -99,12 +101,44 @@ public class EthEstimateGasTest {
assertThat(method.getName()).isEqualTo("eth_estimateGas"); assertThat(method.getName()).isEqualTo("eth_estimateGas");
} }
@Test
public void noAccountOverrides() {
final Wei gasPrice = Wei.of(1000);
final JsonRpcRequestContext request =
ethEstimateGasRequest(defaultLegacyTransactionCallParameter(gasPrice), "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 Wei gasPrice = Wei.of(1000);
final JsonRpcRequestContext request =
ethEstimateGasRequestWithStateOverrides(
defaultLegacyTransactionCallParameter(gasPrice), "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 shouldReturnErrorWhenTransientLegacyTransactionProcessorReturnsEmpty() { public void shouldReturnErrorWhenTransientLegacyTransactionProcessorReturnsEmpty() {
final JsonRpcRequestContext request = final JsonRpcRequestContext request =
ethEstimateGasRequest(defaultLegacyTransactionCallParameter(Wei.ZERO)); ethEstimateGasRequest(defaultLegacyTransactionCallParameter(Wei.ZERO));
when(transactionSimulator.process( when(transactionSimulator.process(
eq(modifiedLegacyTransactionCallParameter(Wei.ZERO)), eq(modifiedLegacyTransactionCallParameter(Wei.ZERO)),
eq(Optional.empty()), // no account overrides
any(TransactionValidationParams.class), any(TransactionValidationParams.class),
any(OperationTracer.class), any(OperationTracer.class),
eq(latestBlockHeader))) eq(latestBlockHeader)))
@ -341,6 +375,7 @@ public class EthEstimateGasTest {
verify(transactionSimulator) verify(transactionSimulator)
.process( .process(
eq(modifiedLegacyTransactionCallParameter(Wei.ZERO)), eq(modifiedLegacyTransactionCallParameter(Wei.ZERO)),
eq(Optional.empty()), // no account overrides
eq( eq(
ImmutableTransactionValidationParams.builder() ImmutableTransactionValidationParams.builder()
.from(TransactionValidationParams.transactionSimulator()) .from(TransactionValidationParams.transactionSimulator())
@ -361,6 +396,7 @@ public class EthEstimateGasTest {
verify(transactionSimulator) verify(transactionSimulator)
.process( .process(
eq(modifiedLegacyTransactionCallParameter(Wei.ZERO)), eq(modifiedLegacyTransactionCallParameter(Wei.ZERO)),
eq(Optional.empty()), // no account overrides
eq( eq(
ImmutableTransactionValidationParams.builder() ImmutableTransactionValidationParams.builder()
.from(TransactionValidationParams.transactionSimulator()) .from(TransactionValidationParams.transactionSimulator())
@ -461,12 +497,14 @@ public class EthEstimateGasTest {
final TransactionSimulatorResult mockTxSimResult = mock(TransactionSimulatorResult.class); final TransactionSimulatorResult mockTxSimResult = mock(TransactionSimulatorResult.class);
when(transactionSimulator.process( when(transactionSimulator.process(
eq(modifiedLegacyTransactionCallParameter(gasPrice)), eq(modifiedLegacyTransactionCallParameter(gasPrice)),
eq(Optional.empty()), // no account overrides
any(TransactionValidationParams.class), any(TransactionValidationParams.class),
any(OperationTracer.class), any(OperationTracer.class),
eq(blockHeader))) eq(blockHeader)))
.thenReturn(Optional.of(mockTxSimResult)); .thenReturn(Optional.of(mockTxSimResult));
when(transactionSimulator.process( when(transactionSimulator.process(
eq(modifiedEip1559TransactionCallParameter()), eq(modifiedEip1559TransactionCallParameter()),
eq(Optional.empty()), // no account overrides
any(TransactionValidationParams.class), any(TransactionValidationParams.class),
any(OperationTracer.class), any(OperationTracer.class),
eq(blockHeader))) eq(blockHeader)))
@ -551,4 +589,13 @@ public class EthEstimateGasTest {
return new JsonRpcRequestContext( return new JsonRpcRequestContext(
new JsonRpcRequest("2.0", "eth_estimateGas", new Object[] {callParameter, blockParam})); new JsonRpcRequest("2.0", "eth_estimateGas", new Object[] {callParameter, blockParam}));
} }
private JsonRpcRequestContext ethEstimateGasRequestWithStateOverrides(
final CallParameter callParameter,
final String blockParam,
final AccountOverrideMap overrides) {
return new JsonRpcRequestContext(
new JsonRpcRequest(
"2.0", "eth_estimateGas", new Object[] {callParameter, blockParam, overrides}));
}
} }

@ -124,6 +124,21 @@ public class TransactionSimulator {
blockHeader); blockHeader);
} }
public Optional<TransactionSimulatorResult> process(
final CallParameter callParams,
final Optional<AccountOverrideMap> maybeStateOverrides,
final TransactionValidationParams transactionValidationParams,
final OperationTracer operationTracer,
final BlockHeader blockHeader) {
return process(
callParams,
maybeStateOverrides,
transactionValidationParams,
operationTracer,
(mutableWorldState, transactionSimulatorResult) -> transactionSimulatorResult,
blockHeader);
}
public Optional<TransactionSimulatorResult> processAtHead(final CallParameter callParams) { public Optional<TransactionSimulatorResult> processAtHead(final CallParameter callParams) {
final var chainHeadHash = blockchain.getChainHeadHash(); final var chainHeadHash = blockchain.getChainHeadHash();
return process( return process(

Loading…
Cancel
Save