Skip to content

Commit

Permalink
Support state overrides in linea_estimateGas (#113)
Browse files Browse the repository at this point in the history
Signed-off-by: Fabio Di Fabio <[email protected]>
  • Loading branch information
fab-10 authored Dec 4, 2024
1 parent ab849c5 commit b20d27c
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import linea.plugin.acc.test.LineaPluginTestBase;
import linea.plugin.acc.test.TestCommandLineOptionsBuilder;
Expand Down Expand Up @@ -122,6 +124,36 @@ public void passingGasPriceFieldWorks() {
assertThat(respLinea.getResult()).isNotNull();
}

@Test
public void passingStateOverridesWorks() {

final Account sender = accounts.getSecondaryBenefactor();

final var actualBalance = minerNode.execute(ethTransactions.getBalance(sender));

assertThat(actualBalance).isGreaterThan(BigInteger.ONE);

final CallParams callParams =
new CallParams(
sender.getAddress(),
sender.getAddress(),
"1",
Bytes.EMPTY.toHexString(),
"0",
"0x1234");

final var zeroBalance = Map.of("balance", Wei.ZERO.toHexString());

final var stateOverrides = Map.of(accounts.getSecondaryBenefactor().getAddress(), zeroBalance);

final var reqLinea = new LineaEstimateGasRequest(callParams, stateOverrides);
final var respLinea = reqLinea.execute(minerNode.nodeRequests());
assertThat(respLinea.hasError()).isTrue();
assertThat(respLinea.getError().getMessage())
.isEqualTo(
"transaction up-front cost 0x208cbab601 exceeds transaction sender account balance 0x0");
}

@Test
public void lineaEstimateGasIsProfitable() {

Expand Down Expand Up @@ -258,17 +290,24 @@ protected void assertMinGasPriceLowerBound(final Wei baseFee, final Wei estimate
static class LineaEstimateGasRequest
implements Transaction<LineaEstimateGasRequest.LineaEstimateGasResponse> {
private final CallParams callParams;
private final Map<String, Map<String, String>> stateOverrides;

public LineaEstimateGasRequest(final CallParams callParams) {
this(callParams, null);
}

public LineaEstimateGasRequest(
final CallParams callParams, final Map<String, Map<String, String>> stateOverrides) {
this.callParams = callParams;
this.stateOverrides = stateOverrides;
}

@Override
public LineaEstimateGasResponse execute(final NodeRequests nodeRequests) {
try {
return new Request<>(
"linea_estimateGas",
List.of(callParams),
Arrays.asList(callParams, stateOverrides),
nodeRequests.getWeb3jService(),
LineaEstimateGasResponse.class)
.send();
Expand Down Expand Up @@ -337,4 +376,6 @@ static class RawEstimateGasResponse extends org.web3j.protocol.core.Response<Str

record CallParams(
String from, String to, String value, String data, String gas, String gasPrice) {}

record StateOverride(String account, String balance) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;

import com.fasterxml.jackson.annotation.JsonProperty;
Expand All @@ -43,6 +44,7 @@
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.datatypes.AccountOverrideMap;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcParameters;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcRequestException;
Expand Down Expand Up @@ -146,7 +148,8 @@ public LineaEstimateGas.Response execute(final PluginRpcRequest request) {
logId = 0;
}

final var callParameters = parseRequest(request.getParams());
final var callParameters = parseCallParameters(request.getParams());
final var maybeStateOverrides = getAddressAccountOverrideMap(request.getParams());
final var minGasPrice = besuConfiguration.getMinGasPrice();
final var gasLimitUpperBound = calculateGasLimitUpperBound(callParameters, logId);
final var transaction = createTransactionForSimulation(callParameters, gasLimitUpperBound);
Expand All @@ -157,7 +160,8 @@ public LineaEstimateGas.Response execute(final PluginRpcRequest request) {
.addArgument(transaction::toTraceLog)
.addArgument(gasLimitUpperBound)
.log();
final var estimatedGasUsed = estimateGasUsed(callParameters, transaction, logId);
final var estimatedGasUsed =
estimateGasUsed(callParameters, maybeStateOverrides, transaction, logId);

final Wei baseFee =
blockchainService
Expand Down Expand Up @@ -263,7 +267,10 @@ private Wei getEstimatedPriorityFee(
}

private Long estimateGasUsed(
final JsonCallParameter callParameters, final Transaction transaction, final long logId) {
final JsonCallParameter callParameters,
final Optional<AccountOverrideMap> maybeStateOverrides,
final Transaction transaction,
final long logId) {

final var estimateGasTracer = new EstimateGasOperationTracer();
final var chainHeadHeader = blockchainService.getChainHeadHeader();
Expand All @@ -272,7 +279,8 @@ private Long estimateGasUsed(

final var chainHeadHash = chainHeadHeader.getBlockHash();
final var maybeSimulationResults =
transactionSimulationService.simulate(transaction, chainHeadHash, zkAndGasTracer, false);
transactionSimulationService.simulate(
transaction, maybeStateOverrides, chainHeadHash, zkAndGasTracer, false);

ModuleLimitsValidationResult moduleLimit =
moduleLineCountValidator.validate(zkTracer.getModulesLineCount());
Expand Down Expand Up @@ -319,6 +327,7 @@ private Long estimateGasUsed(
final var lowResult =
transactionSimulationService.simulate(
createTransactionForSimulation(callParameters, lowGasEstimation),
maybeStateOverrides,
chainHeadHash,
estimateGasTracer,
true);
Expand Down Expand Up @@ -354,6 +363,7 @@ private Long estimateGasUsed(
final var binarySearchResult =
transactionSimulationService.simulate(
createTransactionForSimulation(callParameters, mid),
maybeStateOverrides,
chainHeadHash,
estimateGasTracer,
true);
Expand Down Expand Up @@ -409,19 +419,19 @@ private Long estimateGasUsed(
RpcErrorType.PLUGIN_INTERNAL_ERROR, "Empty result from simulation"));
}

private JsonCallParameter parseRequest(final Object[] params) {
private JsonCallParameter parseCallParameters(final Object[] params) {
final JsonCallParameter callParameters;
try {
callParameters = parameterParser.required(params, 0, JsonCallParameter.class);
} catch (JsonRpcParameter.JsonRpcParameterException e) {
throw new InvalidJsonRpcParameters(
"Invalid call parameters (index 0)", RpcErrorType.INVALID_CALL_PARAMS);
}
validateParameters(callParameters);
validateCallParameters(callParameters);
return callParameters;
}

private void validateParameters(final JsonCallParameter callParameters) {
private void validateCallParameters(final JsonCallParameter callParameters) {
if (callParameters.getGasPrice() != null && isBaseFeeTransaction(callParameters)) {
throw new InvalidJsonRpcParameters(
"gasPrice cannot be used with maxFeePerGas or maxPriorityFeePerGas or maxFeePerBlobGas");
Expand All @@ -434,6 +444,15 @@ private void validateParameters(final JsonCallParameter callParameters) {
}
}

protected Optional<AccountOverrideMap> getAddressAccountOverrideMap(final Object[] params) {
try {
return parameterParser.optional(params, 1, AccountOverrideMap.class);
} catch (JsonRpcParameter.JsonRpcParameterException e) {
throw new InvalidJsonRpcRequestException(
"Invalid account overrides parameter (index 1)", RpcErrorType.INVALID_CALL_PARAMS, e);
}
}

private boolean isBaseFeeTransaction(final JsonCallParameter callParameters) {
return (callParameters.getMaxFeePerGas().isPresent()
|| callParameters.getMaxPriorityFeePerGas().isPresent()
Expand Down

0 comments on commit b20d27c

Please sign in to comment.