Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

eth_estimateGas state overrides #7890

Merged
merged 5 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
- 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 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
Loading