Skip to content

Commit

Permalink
linea_estimateGas compatibility switch (#634)
Browse files Browse the repository at this point in the history
Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com>
  • Loading branch information
fab-10 and macfarla authored Feb 29, 2024
1 parent 2a46de1 commit c98db85
Show file tree
Hide file tree
Showing 7 changed files with 259 additions and 41 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright Consensys Software Inc.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package linea.plugin.acc.test.rpc.linea;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.List;

import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.Transaction;

public class EstimateGasCompatibilityModeTest extends EstimateGasTest {

@Override
public List<String> getTestCliOptions() {
return getTestCommandLineOptionsBuilder()
.set("--plugin-linea-estimate-gas-compatibility-mode-enabled=", "true")
.build();
}

@Override
protected void assertIsProfitable(
final Transaction tx,
final Wei estimatedPriorityFee,
final Wei estimatedMaxGasPrice,
final long estimatedGasLimit) {
final var minGasPrice = minerNode.getMiningParameters().getMinTransactionGasPrice();

// since we are in compatibility mode, we want to check that returned profitable priority fee is
// the min mineable gas price
assertThat(estimatedMaxGasPrice).isEqualTo(minGasPrice);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.List;

import linea.plugin.acc.test.LineaPluginTestBase;
Expand All @@ -28,6 +29,7 @@
import net.consensys.linea.rpc.linea.LineaEstimateGas;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt64;
import org.bouncycastle.crypto.digests.KeccakDigest;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
Expand All @@ -38,25 +40,28 @@
import org.web3j.protocol.core.Request;

public class EstimateGasTest extends LineaPluginTestBase {
private static final int VERIFICATION_GAS_COST = 1_200_000;
private static final int VERIFICATION_CAPACITY = 90_000;
private static final int GAS_PRICE_RATIO = 15;
private static final double MIN_MARGIN = 1.0;
private static final double ESTIMATE_GAS_MIN_MARGIN = 1.0;
private static final Wei MIN_GAS_PRICE = Wei.of(1_000_000_000);
public static final int MAX_TRANSACTION_GAS_LIMIT = 30_000_000;
private LineaTransactionSelectorConfiguration txSelectorConf;
protected static final int VERIFICATION_GAS_COST = 1_200_000;
protected static final int VERIFICATION_CAPACITY = 90_000;
protected static final int GAS_PRICE_RATIO = 15;
protected static final double MIN_MARGIN = 1.0;
protected static final double ESTIMATE_GAS_MIN_MARGIN = 1.0;
protected static final Wei MIN_GAS_PRICE = Wei.of(1_000_000_000);
protected static final int MAX_TRANSACTION_GAS_LIMIT = 30_000_000;
protected LineaTransactionSelectorConfiguration txSelectorConf;

@Override
public List<String> getTestCliOptions() {
return getTestCommandLineOptionsBuilder().build();
}

protected TestCommandLineOptionsBuilder getTestCommandLineOptionsBuilder() {
return new TestCommandLineOptionsBuilder()
.set("--plugin-linea-verification-gas-cost=", String.valueOf(VERIFICATION_GAS_COST))
.set("--plugin-linea-verification-capacity=", String.valueOf(VERIFICATION_CAPACITY))
.set("--plugin-linea-gas-price-ratio=", String.valueOf(GAS_PRICE_RATIO))
.set("--plugin-linea-min-margin=", String.valueOf(MIN_MARGIN))
.set("--plugin-linea-estimate-gas-min-margin=", String.valueOf(ESTIMATE_GAS_MIN_MARGIN))
.set("--plugin-linea-max-tx-gas-limit=", String.valueOf(MAX_TRANSACTION_GAS_LIMIT))
.build();
.set("--plugin-linea-max-tx-gas-limit=", String.valueOf(MAX_TRANSACTION_GAS_LIMIT));
}

@BeforeEach
Expand All @@ -81,7 +86,8 @@ public void lineaEstimateGasMatchesEthEstimateGas() {

final Account sender = accounts.getSecondaryBenefactor();

final CallParams callParams = new CallParams(sender.getAddress(), null);
final CallParams callParams =
new CallParams(sender.getAddress(), sender.getAddress(), null, Bytes.EMPTY.toHexString());

final var reqEth = new RawEstimateGasRequest(callParams);
final var reqLinea = new LineaEstimateGasRequest(callParams);
Expand All @@ -95,28 +101,58 @@ public void lineaEstimateGasIsProfitable() {

final Account sender = accounts.getSecondaryBenefactor();

final CallParams callParams = new CallParams(sender.getAddress(), null);
final KeccakDigest keccakDigest = new KeccakDigest(256);
final StringBuilder txData = new StringBuilder();
txData.append("0x");
for (int i = 0; i < 5; i++) {
keccakDigest.update(new byte[] {(byte) i}, 0, 1);
final byte[] out = new byte[32];
keccakDigest.doFinal(out, 0);
txData.append(new BigInteger(out).abs());
}
final var payload = Bytes.wrap(txData.toString().getBytes(StandardCharsets.UTF_8));

final CallParams callParams =
new CallParams(sender.getAddress(), sender.getAddress(), null, payload.toHexString());

final var reqLinea = new LineaEstimateGasRequest(callParams);
final var respLinea = reqLinea.execute(minerNode.nodeRequests());

final var gasLimit = UInt64.fromHexString(respLinea.gasLimit()).toLong();
final var estimatedGasLimit = UInt64.fromHexString(respLinea.gasLimit()).toLong();
final var baseFee = Wei.fromHexString(respLinea.baseFeePerGas());
final var priorityFee = Wei.fromHexString(respLinea.priorityFeePerGas());
final var maxGasPrice = baseFee.add(priorityFee);
final var estimatedPriorityFee = Wei.fromHexString(respLinea.priorityFeePerGas());
final var estimatedMaxGasPrice = baseFee.add(estimatedPriorityFee);

final var tx =
org.hyperledger.besu.ethereum.core.Transaction.builder()
.sender(Address.fromHexString(sender.getAddress()))
.gasLimit(gasLimit)
.gasPrice(maxGasPrice)
.to(Address.fromHexString(sender.getAddress()))
.gasLimit(estimatedGasLimit)
.gasPrice(estimatedMaxGasPrice)
.chainId(BigInteger.valueOf(CHAIN_ID))
.value(Wei.ZERO)
.payload(Bytes.EMPTY)
.payload(payload)
.signature(LineaEstimateGas.FAKE_SIGNATURE_FOR_SIZE_CALCULATION)
.build();

assertIsProfitable(tx, estimatedPriorityFee, estimatedMaxGasPrice, estimatedGasLimit);
}

protected void assertIsProfitable(
final org.hyperledger.besu.ethereum.core.Transaction tx,
final Wei estimatedPriorityFee,
final Wei estimatedMaxGasPrice,
final long estimatedGasLimit) {

final var minGasPrice = minerNode.getMiningParameters().getMinTransactionGasPrice();

final var profitabilityCalculator = new TransactionProfitabilityCalculator(txSelectorConf);

final var profitablePriorityFee =
profitabilityCalculator.profitablePriorityFeePerGas(tx, minGasPrice, estimatedGasLimit);

assertThat(profitablePriorityFee.greaterThan(minGasPrice)).isTrue();

assertThat(
profitabilityCalculator.isProfitable(
"Test",
Expand All @@ -126,12 +162,30 @@ public void lineaEstimateGasIsProfitable() {
.getMinTransactionGasPrice()
.getAsBigInteger()
.doubleValue(),
maxGasPrice.getAsBigInteger().doubleValue(),
gasLimit))
estimatedMaxGasPrice.getAsBigInteger().doubleValue(),
estimatedGasLimit))
.isTrue();
}

class LineaEstimateGasRequest implements Transaction<LineaEstimateGasRequest.Response> {
@Test
public void lineaEstimateGasPriorityFeeMinGasPriceLowerBound() {

final Account sender = accounts.getSecondaryBenefactor();

final CallParams callParams = new CallParams(sender.getAddress(), null, "", "");

final var reqLinea = new LineaEstimateGasRequest(callParams);
final var respLinea = reqLinea.execute(minerNode.nodeRequests());

final var baseFee = Wei.fromHexString(respLinea.baseFeePerGas());
final var estimatedPriorityFee = Wei.fromHexString(respLinea.priorityFeePerGas());
final var estimatedMaxGasPrice = baseFee.add(estimatedPriorityFee);
final var minGasPrice = minerNode.getMiningParameters().getMinTransactionGasPrice();

assertThat(estimatedMaxGasPrice).isEqualTo(minGasPrice);
}

static class LineaEstimateGasRequest implements Transaction<LineaEstimateGasRequest.Response> {
private final CallParams callParams;

public LineaEstimateGasRequest(final CallParams callParams) {
Expand All @@ -158,7 +212,7 @@ static class LineaEstimateGasResponse extends org.web3j.protocol.core.Response<R
record Response(String gasLimit, String baseFeePerGas, String priorityFeePerGas) {}
}

class RawEstimateGasRequest implements Transaction<String> {
static class RawEstimateGasRequest implements Transaction<String> {
private final CallParams callParams;

public RawEstimateGasRequest(final CallParams callParams) {
Expand All @@ -183,5 +237,5 @@ public String execute(final NodeRequests nodeRequests) {
static class RawEstimateGasResponse extends org.web3j.protocol.core.Response<String> {}
}

record CallParams(String from, String value) {}
record CallParams(String from, String to, String value, String data) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import net.consensys.linea.compress.LibCompress;
import net.consensys.linea.config.LineaL1L2BridgeCliOptions;
import net.consensys.linea.config.LineaL1L2BridgeConfiguration;
import net.consensys.linea.config.LineaRpcCliOptions;
import net.consensys.linea.config.LineaRpcConfiguration;
import net.consensys.linea.config.LineaTransactionSelectorCliOptions;
import net.consensys.linea.config.LineaTransactionSelectorConfiguration;
import net.consensys.linea.config.LineaTransactionValidatorCliOptions;
Expand All @@ -35,9 +37,11 @@ public abstract class AbstractLineaSharedOptionsPlugin implements BesuPlugin {
private static LineaTransactionSelectorCliOptions transactionSelectorCliOptions;
private static LineaTransactionValidatorCliOptions transactionValidatorCliOptions;
private static LineaL1L2BridgeCliOptions l1L2BridgeCliOptions;
private static LineaRpcCliOptions rpcCliOptions;
protected static LineaTransactionSelectorConfiguration transactionSelectorConfiguration;
protected static LineaTransactionValidatorConfiguration transactionValidatorConfiguration;
protected static LineaL1L2BridgeConfiguration l1L2BridgeConfiguration;
protected static LineaRpcConfiguration rpcConfiguration;

static {
// force the initialization of the gnark compress native library to fail fast in case of issues
Expand All @@ -57,10 +61,12 @@ public synchronized void register(final BesuContext context) {
transactionSelectorCliOptions = LineaTransactionSelectorCliOptions.create();
transactionValidatorCliOptions = LineaTransactionValidatorCliOptions.create();
l1L2BridgeCliOptions = LineaL1L2BridgeCliOptions.create();
rpcCliOptions = LineaRpcCliOptions.create();

cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, transactionSelectorCliOptions);
cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, transactionValidatorCliOptions);
cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, l1L2BridgeCliOptions);
cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, rpcCliOptions);
cliOptionsRegistered = true;
}
}
Expand All @@ -71,6 +77,7 @@ public void beforeExternalServices() {
transactionSelectorConfiguration = transactionSelectorCliOptions.toDomainObject();
transactionValidatorConfiguration = transactionValidatorCliOptions.toDomainObject();
l1L2BridgeConfiguration = l1L2BridgeCliOptions.toDomainObject();
rpcConfiguration = rpcCliOptions.toDomainObject();
configured = true;
}

Expand All @@ -88,6 +95,8 @@ public void beforeExternalServices() {
"Configured plugin {} with L1 L2 bridge configuration: {}",
getName(),
l1L2BridgeCliOptions);

log.debug("Configured plugin {} with RPC configuration: {}", getName(), rpcConfiguration);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright Consensys Software Inc.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/

package net.consensys.linea.config;

import com.google.common.base.MoreObjects;
import picocli.CommandLine;

/** The Linea RPC CLI options. */
public class LineaRpcCliOptions {
private static final String ESTIMATE_GAS_COMPATIBILITY_MODE_ENABLED =
"--plugin-linea-estimate-gas-compatibility-mode-enabled";

@CommandLine.Option(
names = {ESTIMATE_GAS_COMPATIBILITY_MODE_ENABLED},
paramLabel = "<BOOLEAN>",
description =
"Set to true to return the min mineable gas price, instead of the profitable price (default: ${DEFAULT-VALUE})")
private boolean estimateGasCompatibilityModeEnabled = false;

private LineaRpcCliOptions() {}

/**
* Create Linea RPC CLI options.
*
* @return the Linea RPC CLI options
*/
public static LineaRpcCliOptions create() {
return new LineaRpcCliOptions();
}

/**
* Linea RPC CLI options from config.
*
* @param config the config
* @return the Linea RPC CLI options
*/
public static LineaRpcCliOptions fromConfig(final LineaRpcConfiguration config) {
final LineaRpcCliOptions options = create();
options.estimateGasCompatibilityModeEnabled = config.estimateGasCompatibilityModeEnabled();
return options;
}

/**
* To domain object Linea factory configuration.
*
* @return the Linea factory configuration
*/
public LineaRpcConfiguration toDomainObject() {
return LineaRpcConfiguration.builder()
.estimateGasCompatibilityModeEnabled(estimateGasCompatibilityModeEnabled)
.build();
}

@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add(ESTIMATE_GAS_COMPATIBILITY_MODE_ENABLED, estimateGasCompatibilityModeEnabled)
.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright Consensys Software Inc.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/

package net.consensys.linea.config;

import lombok.Builder;

/** The Linea RPC configuration. */
@Builder(toBuilder = true)
public record LineaRpcConfiguration(boolean estimateGasCompatibilityModeEnabled) {}
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,6 @@ public void doRegister(final BesuContext context) {
public void beforeExternalServices() {
super.beforeExternalServices();
lineaEstimateGasMethod.init(
transactionValidatorConfiguration, transactionSelectorConfiguration);
rpcConfiguration, transactionValidatorConfiguration, transactionSelectorConfiguration);
}
}
Loading

0 comments on commit c98db85

Please sign in to comment.