Skip to content

Commit

Permalink
eth_estimateGas state overrides (#7890)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
macfarla authored Nov 20, 2024
1 parent fe46289 commit 9718fd5
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 3 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
- 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 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 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
- Fix registering new metric categories from plugins [#7825](https://github.com/hyperledger/besu/pull/7825)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcRequestException;
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.RpcErrorType;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity;
Expand All @@ -27,10 +29,12 @@
import org.hyperledger.besu.ethereum.transaction.CallParameter;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult;
import org.hyperledger.besu.ethereum.util.AccountOverrideMap;
import org.hyperledger.besu.evm.tracing.EstimateGasOperationTracer;

import java.util.Optional;

import com.google.common.annotations.VisibleForTesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -55,6 +59,8 @@ protected Object resultByBlockHeader(

final CallParameter modifiedCallParams =
overrideGasLimitAndPrice(callParams, blockHeader.getGasLimit());
Optional<AccountOverrideMap> maybeStateOverrides = getAddressAccountOverrideMap(requestContext);
// TODO implement for block overrides

final boolean isAllowExceedingBalance = !callParams.isMaybeStrict().orElse(Boolean.FALSE);

Expand All @@ -68,7 +74,11 @@ protected Object resultByBlockHeader(
LOG.debug("Processing transaction with params: {}", modifiedCallParams);
final var maybeResult =
transactionSimulator.process(
modifiedCallParams, transactionValidationParams, operationTracer, blockHeader);
modifiedCallParams,
maybeStateOverrides,
transactionValidationParams,
operationTracer,
blockHeader);

final Optional<JsonRpcErrorResponse> maybeErrorResponse =
validateSimulationResult(requestContext, maybeResult);
Expand All @@ -81,6 +91,7 @@ protected Object resultByBlockHeader(
final var lowResult =
transactionSimulator.process(
overrideGasLimitAndPrice(callParams, low),
maybeStateOverrides,
transactionValidationParams,
operationTracer,
blockHeader);
Expand All @@ -97,6 +108,7 @@ protected Object resultByBlockHeader(
var binarySearchResult =
transactionSimulator.process(
overrideGasLimitAndPrice(callParams, mid),
maybeStateOverrides,
transactionValidationParams,
operationTracer,
blockHeader);
Expand Down Expand Up @@ -127,4 +139,15 @@ private Optional<JsonRpcErrorResponse> validateSimulationResult(
}
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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,8 @@ public void shouldUseCorrectBlockNumberWhenLatest() {
public void shouldUseCorrectBlockNumberWhenEarliest() {
final JsonRpcRequestContext request = ethCallRequest(callParameter(), "earliest");
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());
when(blockchainQueries.getBlockHeaderByHash(Hash.ZERO))
.thenReturn(Optional.of(mock(BlockHeader.class)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult;
import org.hyperledger.besu.ethereum.util.AccountOverride;
import org.hyperledger.besu.ethereum.util.AccountOverrideMap;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.tracing.OperationTracer;

Expand Down Expand Up @@ -99,12 +101,44 @@ public void shouldReturnCorrectMethodName() {
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
public void shouldReturnErrorWhenTransientLegacyTransactionProcessorReturnsEmpty() {
final JsonRpcRequestContext request =
ethEstimateGasRequest(defaultLegacyTransactionCallParameter(Wei.ZERO));
when(transactionSimulator.process(
eq(modifiedLegacyTransactionCallParameter(Wei.ZERO)),
eq(Optional.empty()), // no account overrides
any(TransactionValidationParams.class),
any(OperationTracer.class),
eq(latestBlockHeader)))
Expand Down Expand Up @@ -341,6 +375,7 @@ public void shouldIgnoreSenderBalanceAccountWhenStrictModeDisabled() {
verify(transactionSimulator)
.process(
eq(modifiedLegacyTransactionCallParameter(Wei.ZERO)),
eq(Optional.empty()), // no account overrides
eq(
ImmutableTransactionValidationParams.builder()
.from(TransactionValidationParams.transactionSimulator())
Expand All @@ -361,6 +396,7 @@ public void shouldNotIgnoreSenderBalanceAccountWhenStrictModeEnabled() {
verify(transactionSimulator)
.process(
eq(modifiedLegacyTransactionCallParameter(Wei.ZERO)),
eq(Optional.empty()), // no account overrides
eq(
ImmutableTransactionValidationParams.builder()
.from(TransactionValidationParams.transactionSimulator())
Expand Down Expand Up @@ -461,12 +497,14 @@ private TransactionSimulatorResult getMockTransactionSimulatorResult(
final TransactionSimulatorResult mockTxSimResult = mock(TransactionSimulatorResult.class);
when(transactionSimulator.process(
eq(modifiedLegacyTransactionCallParameter(gasPrice)),
eq(Optional.empty()), // no account overrides
any(TransactionValidationParams.class),
any(OperationTracer.class),
eq(blockHeader)))
.thenReturn(Optional.of(mockTxSimResult));
when(transactionSimulator.process(
eq(modifiedEip1559TransactionCallParameter()),
eq(Optional.empty()), // no account overrides
any(TransactionValidationParams.class),
any(OperationTracer.class),
eq(blockHeader)))
Expand Down Expand Up @@ -551,4 +589,13 @@ private JsonRpcRequestContext ethEstimateGasRequest(
return new JsonRpcRequestContext(
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}));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,21 @@ public Optional<TransactionSimulatorResult> process(
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) {
final var chainHeadHash = blockchain.getChainHeadHash();
return process(
Expand Down

0 comments on commit 9718fd5

Please sign in to comment.