diff --git a/CHANGELOG.md b/CHANGELOG.md index cc1a258888..80a459c7ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,44 @@ # Changelog +## 0.1.4-test18-RC3 +Test pre-release 18-RC3 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only) +* Use compressed tx size also when selecting txs from block creation [#590](https://github.com/Consensys/besu-sequencer-plugins/pull/590) + +## 0.1.4-test18-RC2 +Test pre-release 18-RC2 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only) +* Fix linea_estimateGas reports Internal error when value or gas price is missing [#587](https://github.com/Consensys/besu-sequencer-plugins/pull/587) + +## 0.1.4-test18-RC1 +Test pre-release 18-RC1 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only) +* Linea estimate gas endpoint [#585](https://github.com/Consensys/besu-sequencer-plugins/pull/585) + +## 0.1.4-test17 +Test pre-release 17 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only) +* tests: drop huge random tests [#563](https://github.com/Consensys/besu-sequencer-plugins/pull/563) +* feat(modexp-data): implement MODEXP_DATA module [#547](https://github.com/Consensys/besu-sequencer-plugins/pull/547) +* feat: mechanics to capture conflations & replay them as test cases [#561](https://github.com/Consensys/besu-sequencer-plugins/pull/561) +* perf(EUC): one less column [#570](https://github.com/Consensys/besu-sequencer-plugins/pull/570) +* docs: Add basic plugins doc [#509](https://github.com/Consensys/besu-sequencer-plugins/pull/509) +* Check upfront profitability + Unprofitable txs cache and retry limit [#565](https://github.com/Consensys/besu-sequencer-plugins/pull/565) +* Avoid reprocessing txs that go over line count limit [#571](https://github.com/Consensys/besu-sequencer-plugins/pull/571) + +## 0.1.4-test16 +Test pre-release 16 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only) +* fix: bug-compatibility with Geth +* fix: PubHash 16 factor + +Full changeset https://github.com/Consensys/besu-sequencer-plugins/compare/v0.1.4-test15...v0.1.4-test16 + +## 0.1.4-test15 +release rebase off of main +* add option to adjust the tx size used to calculate the profitability of a tx during block creation(#562)[https://github.com/Consensys/besu-sequencer-plugins/pull/562] + +## 0.1.4-test14 +release rebase off of main +Test pre-release 14 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only) +* Fix log of line counts in case of block limit reached + minor changes [#555](https://github.com/ConsenSys/besu-sequencer-plugins/pull/555) +* build: update Corset to 9.3.0 [#554](https://github.com/ConsenSys/besu-sequencer-plugins/pull/554) + ## 0.1.4-test13 Test pre-release 13 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only) * fix stackedSet [c3f226775f24508b93a758e4226a51ae386d76a5](https://github.com/Consensys/besu-sequencer-plugins/commit/c3f226775f24508b93a758e4226a51ae386d76a5) diff --git a/acceptance-tests/build.gradle b/acceptance-tests/build.gradle index f5de3b8f94..c8ed0b5d94 100644 --- a/acceptance-tests/build.gradle +++ b/acceptance-tests/build.gradle @@ -54,6 +54,7 @@ tasks.register('acceptanceTests', Test) { } dependencies { + testImplementation project(":arithmetization") testImplementation "${besuArtifactGroup}.internal:dsl:$besuVersion" testImplementation "${besuArtifactGroup}:besu-datatypes:$besuVersion" testImplementation "${besuArtifactGroup}.internal:eth:$besuVersion" diff --git a/acceptance-tests/src/test/java/linea/plugin/acc/test/LineaPluginTestBase.java b/acceptance-tests/src/test/java/linea/plugin/acc/test/LineaPluginTestBase.java index a74603ce3a..1c1cac2152 100644 --- a/acceptance-tests/src/test/java/linea/plugin/acc/test/LineaPluginTestBase.java +++ b/acceptance-tests/src/test/java/linea/plugin/acc/test/LineaPluginTestBase.java @@ -65,8 +65,8 @@ public class LineaPluginTestBase extends AcceptanceTestBase { @BeforeEach public void setup() throws Exception { minerNode = - besu.createCliqueNodeWithExtraCliOptions( - "miner1", LINEA_CLIQUE_OPTIONS, getTestCliOptions()); + besu.createCliqueNodeWithExtraCliOptionsAndRpcApis( + "miner1", LINEA_CLIQUE_OPTIONS, getTestCliOptions(), Set.of("LINEA")); minerNode.setTransactionPoolConfiguration( ImmutableTransactionPoolConfiguration.builder() .from(TransactionPoolConfiguration.DEFAULT) diff --git a/acceptance-tests/src/test/java/linea/plugin/acc/test/ProfitableTransactionTest.java b/acceptance-tests/src/test/java/linea/plugin/acc/test/ProfitableTransactionTest.java index 9f5c76c0a3..2f640509d5 100644 --- a/acceptance-tests/src/test/java/linea/plugin/acc/test/ProfitableTransactionTest.java +++ b/acceptance-tests/src/test/java/linea/plugin/acc/test/ProfitableTransactionTest.java @@ -57,7 +57,7 @@ public void transactionIsNotMinedWhenUnprofitable() throws Exception { final Credentials credentials = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY); TransactionManager txManager = new RawTransactionManager(web3j, credentials, CHAIN_ID); - final String txData = "not profitable transaction".repeat(10); + final String txData = "not profitable transaction".repeat(60); final var txUnprofitable = txManager.sendTransaction( diff --git a/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasTest.java b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasTest.java new file mode 100644 index 0000000000..e204ccc33a --- /dev/null +++ b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasTest.java @@ -0,0 +1,187 @@ +/* + * 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.io.IOException; +import java.math.BigInteger; +import java.util.List; + +import linea.plugin.acc.test.LineaPluginTestBase; +import linea.plugin.acc.test.TestCommandLineOptionsBuilder; +import net.consensys.linea.bl.TransactionProfitabilityCalculator; +import net.consensys.linea.config.LineaTransactionSelectorCliOptions; +import net.consensys.linea.config.LineaTransactionSelectorConfiguration; +import net.consensys.linea.rpc.linea.LineaEstimateGas; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.units.bigints.UInt64; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.tests.acceptance.dsl.account.Account; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +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; + + @Override + public List getTestCliOptions() { + 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(); + } + + @BeforeEach + public void setMinGasPrice() { + minerNode.getMiningParameters().setMinTransactionGasPrice(MIN_GAS_PRICE); + } + + @BeforeEach + public void createDefaultConfigurations() { + txSelectorConf = + LineaTransactionSelectorCliOptions.create().toDomainObject().toBuilder() + .verificationCapacity(VERIFICATION_CAPACITY) + .verificationGasCost(VERIFICATION_GAS_COST) + .gasPriceRatio(GAS_PRICE_RATIO) + .minMargin(MIN_MARGIN) + .estimateGasMinMargin(ESTIMATE_GAS_MIN_MARGIN) + .build(); + } + + @Test + public void lineaEstimateGasMatchesEthEstimateGas() { + + final Account sender = accounts.getSecondaryBenefactor(); + + final CallParams callParams = new CallParams(sender.getAddress(), null); + + final var reqEth = new RawEstimateGasRequest(callParams); + final var reqLinea = new LineaEstimateGasRequest(callParams); + final var respEth = reqEth.execute(minerNode.nodeRequests()); + final var respLinea = reqLinea.execute(minerNode.nodeRequests()); + assertThat(respEth).isEqualTo(respLinea.gasLimit()); + } + + @Test + public void lineaEstimateGasIsProfitable() { + + 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 gasLimit = 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 tx = + org.hyperledger.besu.ethereum.core.Transaction.builder() + .sender(Address.fromHexString(sender.getAddress())) + .gasLimit(gasLimit) + .gasPrice(maxGasPrice) + .chainId(BigInteger.valueOf(CHAIN_ID)) + .value(Wei.ZERO) + .payload(Bytes.EMPTY) + .signature(LineaEstimateGas.FAKE_SIGNATURE_FOR_SIZE_CALCULATION) + .build(); + + final var profitabilityCalculator = new TransactionProfitabilityCalculator(txSelectorConf); + assertThat( + profitabilityCalculator.isProfitable( + "Test", + tx, + minerNode + .getMiningParameters() + .getMinTransactionGasPrice() + .getAsBigInteger() + .doubleValue(), + maxGasPrice.getAsBigInteger().doubleValue(), + gasLimit)) + .isTrue(); + } + + class LineaEstimateGasRequest implements Transaction { + private final CallParams callParams; + + public LineaEstimateGasRequest(final CallParams callParams) { + this.callParams = callParams; + } + + @Override + public LineaEstimateGasRequest.Response execute(final NodeRequests nodeRequests) { + try { + return new Request<>( + "linea_estimateGas", + List.of(callParams), + nodeRequests.getWeb3jService(), + LineaEstimateGasResponse.class) + .send() + .getResult(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + static class LineaEstimateGasResponse extends org.web3j.protocol.core.Response {} + + record Response(String gasLimit, String baseFeePerGas, String priorityFeePerGas) {} + } + + class RawEstimateGasRequest implements Transaction { + private final CallParams callParams; + + public RawEstimateGasRequest(final CallParams callParams) { + this.callParams = callParams; + } + + @Override + public String execute(final NodeRequests nodeRequests) { + try { + return new Request<>( + "eth_estimateGas", + List.of(callParams), + nodeRequests.getWeb3jService(), + RawEstimateGasResponse.class) + .send() + .getResult(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + static class RawEstimateGasResponse extends org.web3j.protocol.core.Response {} + } + + record CallParams(String from, String value) {} +} diff --git a/acceptance-tests/src/test/resources/clique/clique.json.tpl b/acceptance-tests/src/test/resources/clique/clique.json.tpl index 9ecfc4e4de..a3c61b88c5 100644 --- a/acceptance-tests/src/test/resources/clique/clique.json.tpl +++ b/acceptance-tests/src/test/resources/clique/clique.json.tpl @@ -12,7 +12,7 @@ } }, "zeroBaseFee": false, - "baseFeePerGas": "1000000000", + "baseFeePerGas": "7", "nonce": "0x0", "timestamp": "0x6391BFF3", "extraData": "%extraData%", diff --git a/arithmetization/build.gradle b/arithmetization/build.gradle index c865294562..36f028348e 100644 --- a/arithmetization/build.gradle +++ b/arithmetization/build.gradle @@ -43,6 +43,7 @@ dependencies { implementation "${besuArtifactGroup}:evm" implementation "${besuArtifactGroup}:plugin-api" implementation "${besuArtifactGroup}:besu-datatypes" + implementation "${besuArtifactGroup}.internal:api:${besuVersion}" implementation "${besuArtifactGroup}.internal:core:${besuVersion}" implementation "${besuArtifactGroup}.internal:rlp:${besuVersion}" implementation "${besuArtifactGroup}.internal:algorithms:${besuVersion}" diff --git a/arithmetization/src/main/java/net/consensys/linea/AbstractLineaRequiredPlugin.java b/arithmetization/src/main/java/net/consensys/linea/AbstractLineaRequiredPlugin.java index 4cf1131570..cfc79cffd3 100644 --- a/arithmetization/src/main/java/net/consensys/linea/AbstractLineaRequiredPlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/AbstractLineaRequiredPlugin.java @@ -20,7 +20,7 @@ import org.hyperledger.besu.plugin.BesuPlugin; @Slf4j -public abstract class AbstractLineaRequiredPlugin implements BesuPlugin { +public abstract class AbstractLineaRequiredPlugin extends AbstractLineaSharedOptionsPlugin { /** * Linea plugins extending this class will halt startup of Besu in case of exception during @@ -32,6 +32,7 @@ public abstract class AbstractLineaRequiredPlugin implements BesuPlugin { */ @Override public void register(final BesuContext context) { + super.register(context); try { log.info("Registering Linea plugin " + this.getClass().getName()); @@ -44,17 +45,4 @@ public void register(final BesuContext context) { System.exit(1); } } - - /** - * Linea plugins need to implement this method. Called by {@link BesuPlugin} register method - * - * @param context - */ - public abstract void doRegister(final BesuContext context); - - @Override - public void start() {} - - @Override - public void stop() {} } diff --git a/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java b/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java new file mode 100644 index 0000000000..8c9a08717a --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java @@ -0,0 +1,84 @@ +/* + * 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; + +import lombok.extern.slf4j.Slf4j; +import net.consensys.linea.config.LineaTransactionSelectorCliOptions; +import net.consensys.linea.config.LineaTransactionSelectorConfiguration; +import net.consensys.linea.config.LineaTransactionValidatorCliOptions; +import net.consensys.linea.config.LineaTransactionValidatorConfiguration; +import org.hyperledger.besu.plugin.BesuContext; +import org.hyperledger.besu.plugin.BesuPlugin; +import org.hyperledger.besu.plugin.services.PicoCLIOptions; + +@Slf4j +public abstract class AbstractLineaSharedOptionsPlugin implements BesuPlugin { + private static String CLI_OPTIONS_PREFIX = "linea"; + private static boolean cliOptionsRegistered = false; + private static LineaTransactionSelectorCliOptions transactionSelectorCliOptions; + private static LineaTransactionValidatorCliOptions transactionValidatorCliOptions; + protected static LineaTransactionSelectorConfiguration transactionSelectorConfiguration; + protected static LineaTransactionValidatorConfiguration transactionValidatorConfiguration; + + @Override + public synchronized void register(final BesuContext context) { + if (!cliOptionsRegistered) { + final PicoCLIOptions cmdlineOptions = + context + .getService(PicoCLIOptions.class) + .orElseThrow( + () -> + new IllegalStateException( + "Failed to obtain PicoCLI options from the BesuContext")); + transactionSelectorCliOptions = LineaTransactionSelectorCliOptions.create(); + transactionValidatorCliOptions = LineaTransactionValidatorCliOptions.create(); + + cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, transactionSelectorCliOptions); + cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, transactionValidatorCliOptions); + cliOptionsRegistered = true; + } + } + + @Override + public void beforeExternalServices() { + log.debug( + "Configuring plugin {} with transaction selector configuration: {}", + getName(), + transactionSelectorCliOptions); + transactionSelectorConfiguration = transactionSelectorCliOptions.toDomainObject(); + + log.debug( + "Configuring plugin {} with transaction validator configuration: {}", + getName(), + transactionValidatorCliOptions); + transactionValidatorConfiguration = transactionValidatorCliOptions.toDomainObject(); + } + + /** + * Linea plugins need to implement this method. Called by {@link BesuPlugin} register method + * + * @param context + */ + public abstract void doRegister(final BesuContext context); + + @Override + public void start() {} + + @Override + public void stop() { + cliOptionsRegistered = false; + } +} diff --git a/arithmetization/src/main/java/net/consensys/linea/bl/TransactionProfitabilityCalculator.java b/arithmetization/src/main/java/net/consensys/linea/bl/TransactionProfitabilityCalculator.java new file mode 100644 index 0000000000..c40be3093b --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/bl/TransactionProfitabilityCalculator.java @@ -0,0 +1,153 @@ +/* + * 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.bl; + +import java.math.BigDecimal; + +import lombok.extern.slf4j.Slf4j; +import net.consensys.linea.config.LineaTransactionSelectorConfiguration; +import org.hyperledger.besu.datatypes.Transaction; +import org.hyperledger.besu.datatypes.Wei; +import org.slf4j.spi.LoggingEventBuilder; + +@Slf4j +public class TransactionProfitabilityCalculator { + + private final LineaTransactionSelectorConfiguration conf; + private final double preComputedValue; + + public TransactionProfitabilityCalculator(final LineaTransactionSelectorConfiguration conf) { + this.conf = conf; + this.preComputedValue = + conf.estimateGasMinMargin() * conf.gasPriceRatio() * conf.verificationGasCost(); + } + + public Wei profitablePriorityFeePerGas( + final Transaction transaction, final Wei minGasPrice, final long gas) { + final double compressedTxSize = getCompressedTxSize(transaction); + + final var profitAt = + preComputedValue + * compressedTxSize + * minGasPrice.getAsBigInteger().doubleValue() + / (gas * conf.verificationCapacity()); + + final var profitAtWei = Wei.ofNumber(BigDecimal.valueOf(profitAt).toBigInteger()); + + log.atDebug() + .setMessage( + "Estimated profitable priorityFeePerGas: {}; estimateGasMinMargin={}, verificationCapacity={}, " + + "verificationGasCost={}, gasPriceRatio={}, gas={}, minGasPrice={}, " + + "l1GasPrice={}, txSize={}, compressedTxSize={}, adjustTxSize={}") + .addArgument(profitAtWei::toHumanReadableString) + .addArgument(conf.estimateGasMinMargin()) + .addArgument(conf.verificationCapacity()) + .addArgument(conf.verificationGasCost()) + .addArgument(conf.gasPriceRatio()) + .addArgument(gas) + .addArgument(minGasPrice::toHumanReadableString) + .addArgument(() -> minGasPrice.multiply(conf.gasPriceRatio()).toHumanReadableString()) + .addArgument(transaction::getSize) + .addArgument(compressedTxSize) + .addArgument(conf.adjustTxSize()) + .log(); + + return profitAtWei; + } + + public boolean isProfitable( + final String step, + final Transaction transaction, + final double minGasPrice, + final double effectiveGasPrice, + final long gas) { + final double revenue = effectiveGasPrice * gas; + + final double l1GasPrice = minGasPrice * conf.gasPriceRatio(); + final double compressedTxSize = getCompressedTxSize(transaction); + final double verificationGasCostSlice = + (compressedTxSize / conf.verificationCapacity()) * conf.verificationGasCost(); + final double cost = l1GasPrice * verificationGasCostSlice; + + final double margin = revenue / cost; + + if (margin < conf.minMargin()) { + log( + log.atDebug(), + step, + transaction, + margin, + effectiveGasPrice, + gas, + minGasPrice, + l1GasPrice, + compressedTxSize, + conf.adjustTxSize()); + return false; + } else { + log( + log.atTrace(), + step, + transaction, + margin, + effectiveGasPrice, + gas, + minGasPrice, + l1GasPrice, + compressedTxSize, + conf.adjustTxSize()); + return true; + } + } + + private double getCompressedTxSize(final Transaction transaction) { + // this is just a temporary estimation, that will be replaced by gnarkCompression when available + // at that point conf.txCompressionRatio and conf.adjustTxSize options can be removed + final double adjustedTxSize = Math.max(0, transaction.getSize() + conf.adjustTxSize()); + return adjustedTxSize / conf.txCompressionRatio(); + } + + private void log( + final LoggingEventBuilder leb, + final String context, + final Transaction transaction, + final double margin, + final double effectiveGasPrice, + final long gasUsed, + final double minGasPrice, + final double l1GasPrice, + final double compressedTxSize, + final int adjustTxSize) { + leb.setMessage( + "Context {}. Transaction {} has a margin of {}, minMargin={}, verificationCapacity={}, " + + "verificationGasCost={}, gasPriceRatio={}, effectiveGasPrice={}, gasUsed={}, minGasPrice={}, " + + "l1GasPrice={}, txSize={}, compressedTxSize={}, adjustTxSize={}") + .addArgument(context) + .addArgument(transaction::getHash) + .addArgument(margin) + .addArgument(conf.minMargin()) + .addArgument(conf.verificationCapacity()) + .addArgument(conf.verificationGasCost()) + .addArgument(conf.gasPriceRatio()) + .addArgument(effectiveGasPrice) + .addArgument(gasUsed) + .addArgument(minGasPrice) + .addArgument(l1GasPrice) + .addArgument(transaction::getSize) + .addArgument(compressedTxSize) + .addArgument(adjustTxSize) + .log(); + } +} diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorCliOptions.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorCliOptions.java similarity index 86% rename from arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorCliOptions.java rename to arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorCliOptions.java index 3200ea4e6e..8da3a51032 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorCliOptions.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorCliOptions.java @@ -13,7 +13,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package net.consensys.linea.sequencer.txselection; +package net.consensys.linea.config; import java.math.BigDecimal; @@ -31,7 +31,9 @@ public class LineaTransactionSelectorCliOptions { public static final int DEFAULT_VERIFICATION_CAPACITY = 90_000; public static final int DEFAULT_GAS_PRICE_RATIO = 15; public static final BigDecimal DEFAULT_MIN_MARGIN = BigDecimal.ONE; + public static final BigDecimal DEFAULT_ESTIMATE_GAS_MIN_MARGIN = BigDecimal.ONE; public static final int DEFAULT_ADJUST_TX_SIZE = -45; + public static final int DEFAULT_TX_COMPRESSION_RATIO = 5; public static final int DEFAULT_UNPROFITABLE_CACHE_SIZE = 100_000; public static final int DEFAULT_UNPROFITABLE_RETRY_LIMIT = 10; private static final String MAX_BLOCK_CALLDATA_SIZE = "--plugin-linea-max-block-calldata-size"; @@ -43,7 +45,9 @@ public class LineaTransactionSelectorCliOptions { private static final String VERIFICATION_CAPACITY = "--plugin-linea-verification-capacity"; private static final String GAS_PRICE_RATIO = "--plugin-linea-gas-price-ratio"; private static final String MIN_MARGIN = "--plugin-linea-min-margin"; + private static final String ESTIMATE_GAS_MIN_MARGIN = "--plugin-linea-estimate-gas-min-margin"; private static final String ADJUST_TX_SIZE = "--plugin-linea-adjust-tx-size"; + private static final String TX_COMPRESSION_RATIO = "--plugin-linea-tx-compression-ratio"; private static final String UNPROFITABLE_CACHE_SIZE = "--plugin-linea-unprofitable-cache-size"; private static final String UNPROFITABLE_RETRY_LIMIT = "--plugin-linea-unprofitable-retry-limit"; @@ -112,6 +116,15 @@ public class LineaTransactionSelectorCliOptions { description = "Minimum margin of a transaction to be selected (default: ${DEFAULT-VALUE})") private BigDecimal minMargin = DEFAULT_MIN_MARGIN; + @Positive + @CommandLine.Option( + names = {ESTIMATE_GAS_MIN_MARGIN}, + hidden = true, + paramLabel = "", + description = + "Recommend a specific gas price when using linea_estimateGas (default: ${DEFAULT-VALUE})") + private BigDecimal estimageGasMinMargin = DEFAULT_ESTIMATE_GAS_MIN_MARGIN; + @Positive @CommandLine.Option( names = {ADJUST_TX_SIZE}, @@ -121,6 +134,15 @@ public class LineaTransactionSelectorCliOptions { "Adjust transaction size for profitability calculation (default: ${DEFAULT-VALUE})") private int adjustTxSize = DEFAULT_ADJUST_TX_SIZE; + @Positive + @CommandLine.Option( + names = {TX_COMPRESSION_RATIO}, + hidden = true, + paramLabel = "", + description = + "The ratio between tx serialized size and its compressed size (default: ${DEFAULT-VALUE})") + private int txCompressionRatio = DEFAULT_TX_COMPRESSION_RATIO; + @Positive @CommandLine.Option( names = {UNPROFITABLE_CACHE_SIZE}, @@ -168,6 +190,7 @@ public static LineaTransactionSelectorCliOptions fromConfig( options.gasPriceRatio = config.gasPriceRatio(); options.minMargin = BigDecimal.valueOf(config.minMargin()); options.adjustTxSize = config.adjustTxSize(); + options.txCompressionRatio = config.txCompressionRatio(); options.unprofitableCacheSize = config.unprofitableCacheSize(); options.unprofitableRetryLimit = config.unprofitableRetryLimit(); return options; @@ -188,7 +211,9 @@ public LineaTransactionSelectorConfiguration toDomainObject() { .verificationCapacity(verificationCapacity) .gasPriceRatio(gasPriceRatio) .minMargin(minMargin.doubleValue()) + .estimateGasMinMargin((estimageGasMinMargin.doubleValue())) .adjustTxSize(adjustTxSize) + .txCompressionRatio(txCompressionRatio) .unprofitableCacheSize(unprofitableCacheSize) .unprofitableRetryLimit(unprofitableRetryLimit) .build(); @@ -205,7 +230,9 @@ public String toString() { .add(VERIFICATION_CAPACITY, verificationCapacity) .add(GAS_PRICE_RATIO, gasPriceRatio) .add(MIN_MARGIN, minMargin) + .add(ESTIMATE_GAS_MIN_MARGIN, estimageGasMinMargin) .add(ADJUST_TX_SIZE, adjustTxSize) + .add(TX_COMPRESSION_RATIO, txCompressionRatio) .add(UNPROFITABLE_CACHE_SIZE, unprofitableCacheSize) .add(UNPROFITABLE_RETRY_LIMIT, unprofitableRetryLimit) .toString(); diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorConfiguration.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorConfiguration.java similarity index 89% rename from arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorConfiguration.java rename to arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorConfiguration.java index dd47424c59..a7fa215dd5 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorConfiguration.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorConfiguration.java @@ -13,12 +13,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -package net.consensys.linea.sequencer.txselection; +package net.consensys.linea.config; import lombok.Builder; /** The Linea transaction selectors configuration. */ -@Builder +@Builder(toBuilder = true) public record LineaTransactionSelectorConfiguration( int maxBlockCallDataSize, String moduleLimitsFilePath, @@ -28,7 +28,9 @@ public record LineaTransactionSelectorConfiguration( int verificationCapacity, int gasPriceRatio, double minMargin, + double estimateGasMinMargin, int adjustTxSize, + int txCompressionRatio, int unprofitableCacheSize, int unprofitableRetryLimit) {} ; diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorCliOptions.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionValidatorCliOptions.java similarity index 98% rename from arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorCliOptions.java rename to arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionValidatorCliOptions.java index e574d57df6..eeb0cd4a0c 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorCliOptions.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionValidatorCliOptions.java @@ -13,7 +13,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package net.consensys.linea.sequencer.txvalidation; +package net.consensys.linea.config; import com.google.common.base.MoreObjects; import picocli.CommandLine; diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorConfiguration.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionValidatorConfiguration.java similarity index 92% rename from arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorConfiguration.java rename to arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionValidatorConfiguration.java index 9609a72ec9..488651f49a 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorConfiguration.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionValidatorConfiguration.java @@ -13,7 +13,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -package net.consensys.linea.sequencer.txvalidation; +package net.consensys.linea.config; + +import lombok.Builder; /** * The Linea configuration. @@ -22,5 +24,6 @@ * @param maxTxGasLimit the maximum gas limit allowed for transactions * @param maxTxCalldataSize the maximum size of calldata allowed for transactions */ +@Builder(toBuilder = true) public record LineaTransactionValidatorConfiguration( String denyListPath, int maxTxGasLimit, int maxTxCalldataSize) {} diff --git a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java new file mode 100644 index 0000000000..3cd7d7f03e --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java @@ -0,0 +1,92 @@ +/* + * 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.rpc.linea; + +import com.google.auto.service.AutoService; +import lombok.extern.slf4j.Slf4j; +import net.consensys.linea.AbstractLineaRequiredPlugin; +import org.hyperledger.besu.plugin.BesuContext; +import org.hyperledger.besu.plugin.BesuPlugin; +import org.hyperledger.besu.plugin.services.BesuConfiguration; +import org.hyperledger.besu.plugin.services.BlockchainService; +import org.hyperledger.besu.plugin.services.RpcEndpointService; +import org.hyperledger.besu.plugin.services.TransactionSimulationService; + +/** Registers RPC endpoints. This class provides RPC endpoints under the 'linea' namespace. */ +@AutoService(BesuPlugin.class) +@Slf4j +public class LineaEndpointServicePlugin extends AbstractLineaRequiredPlugin { + private BesuConfiguration besuConfiguration; + private RpcEndpointService rpcEndpointService; + private TransactionSimulationService transactionSimulationService; + private BlockchainService blockchainService; + private LineaEstimateGas lineaEstimateGasMethod; + + /** + * Register the RPC service. + * + * @param context the BesuContext to be used. + */ + @Override + public void doRegister(final BesuContext context) { + besuConfiguration = + context + .getService(BesuConfiguration.class) + .orElseThrow( + () -> + new RuntimeException( + "Failed to obtain BesuConfiguration from the BesuContext.")); + + rpcEndpointService = + context + .getService(RpcEndpointService.class) + .orElseThrow( + () -> + new RuntimeException( + "Failed to obtain RpcEndpointService from the BesuContext.")); + + transactionSimulationService = + context + .getService(TransactionSimulationService.class) + .orElseThrow( + () -> + new RuntimeException( + "Failed to obtain TransactionSimulatorService from the BesuContext.")); + + blockchainService = + context + .getService(BlockchainService.class) + .orElseThrow( + () -> + new RuntimeException( + "Failed to obtain BlockchainService from the BesuContext.")); + + lineaEstimateGasMethod = + new LineaEstimateGas(besuConfiguration, transactionSimulationService, blockchainService); + + rpcEndpointService.registerRPCEndpoint( + lineaEstimateGasMethod.getNamespace(), + lineaEstimateGasMethod.getName(), + lineaEstimateGasMethod::execute); + } + + @Override + public void beforeExternalServices() { + super.beforeExternalServices(); + lineaEstimateGasMethod.init( + transactionValidatorConfiguration, transactionSelectorConfiguration); + } +} diff --git a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java new file mode 100644 index 0000000000..bc933c4a17 --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java @@ -0,0 +1,290 @@ +/* + * 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.rpc.linea; + +import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity.create; + +import java.math.BigInteger; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.annotations.VisibleForTesting; +import lombok.extern.slf4j.Slf4j; +import net.consensys.linea.bl.TransactionProfitabilityCalculator; +import net.consensys.linea.config.LineaTransactionSelectorConfiguration; +import net.consensys.linea.config.LineaTransactionValidatorConfiguration; +import org.apache.tuweni.bytes.Bytes; +import org.bouncycastle.asn1.sec.SECNamedCurves; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.hyperledger.besu.crypto.SECPSignature; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcParameters; +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.core.Transaction; +import org.hyperledger.besu.evm.tracing.EstimateGasOperationTracer; +import org.hyperledger.besu.plugin.services.BesuConfiguration; +import org.hyperledger.besu.plugin.services.BlockchainService; +import org.hyperledger.besu.plugin.services.TransactionSimulationService; +import org.hyperledger.besu.plugin.services.rpc.PluginRpcRequest; + +@Slf4j +public class LineaEstimateGas { + @VisibleForTesting public static final SECPSignature FAKE_SIGNATURE_FOR_SIZE_CALCULATION; + + private static final double SUB_CALL_REMAINING_GAS_RATIO = 65D / 64D; + + static { + final X9ECParameters params = SECNamedCurves.getByName("secp256k1"); + final ECDomainParameters curve = + new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH()); + FAKE_SIGNATURE_FOR_SIZE_CALCULATION = + SECPSignature.create( + new BigInteger( + "66397251408932042429874251838229702988618145381408295790259650671563847073199"), + new BigInteger( + "24729624138373455972486746091821238755870276413282629437244319694880507882088"), + (byte) 0, + curve.getN()); + } + + private final JsonRpcParameter parameterParser = new JsonRpcParameter(); + private final BesuConfiguration besuConfiguration; + private final TransactionSimulationService transactionSimulationService; + private final BlockchainService blockchainService; + private LineaTransactionValidatorConfiguration txValidatorConf; + private LineaTransactionSelectorConfiguration txSelectorConf; + private TransactionProfitabilityCalculator txProfitabilityCalculator; + + public LineaEstimateGas( + final BesuConfiguration besuConfiguration, + final TransactionSimulationService transactionSimulationService, + final BlockchainService blockchainService) { + this.besuConfiguration = besuConfiguration; + this.transactionSimulationService = transactionSimulationService; + this.blockchainService = blockchainService; + } + + public void init( + final LineaTransactionValidatorConfiguration transactionValidatorConfiguration, + final LineaTransactionSelectorConfiguration transactionSelectorConfiguration) { + this.txValidatorConf = transactionValidatorConfiguration; + this.txSelectorConf = transactionSelectorConfiguration; + this.txProfitabilityCalculator = new TransactionProfitabilityCalculator(txSelectorConf); + } + + public String getNamespace() { + return "linea"; + } + + public String getName() { + return "estimateGas"; + } + + public LineaEstimateGas.Response execute(final PluginRpcRequest request) { + final var callParameters = parseRequest(request.getParams()); + final var minGasPrice = besuConfiguration.getMinGasPrice(); + + final var transaction = + createTransactionForSimulation( + callParameters, txValidatorConf.maxTxGasLimit(), minGasPrice); + log.atTrace() + .setMessage("Parsed call parameters: {}; Transaction: {}") + .addArgument(callParameters) + .addArgument(transaction::toTraceLog) + .log(); + final var estimatedGasUsed = estimateGasUsed(callParameters, transaction, minGasPrice); + + final Wei estimatedPriorityFee = + txProfitabilityCalculator.profitablePriorityFeePerGas( + transaction, minGasPrice, estimatedGasUsed); + + final Wei baseFee = + blockchainService + .getNextBlockBaseFee() + .orElseThrow(() -> new IllegalStateException("Not on a baseFee market")); + + final var response = + new Response(create(estimatedGasUsed), create(baseFee), create(estimatedPriorityFee)); + log.debug("Response for call params {} is {}", callParameters, response); + + return response; + } + + private Long estimateGasUsed( + final JsonCallParameter callParameters, + final Transaction transaction, + final Wei minGasPrice) { + final var tracer = new EstimateGasOperationTracer(); + final var chainHeadHash = blockchainService.getChainHeadHash(); + final var maybeSimulationResults = + transactionSimulationService.simulate(transaction, chainHeadHash, tracer, true); + + return maybeSimulationResults + .map( + r -> { + + // if the transaction is invalid or doesn't have enough gas with the max it never + // will! + if (r.isInvalid() || !r.isSuccessful()) { + log.atDebug() + .setMessage("Invalid or unsuccessful transaction {}, reason {}") + .addArgument(transaction::toTraceLog) + .addArgument(r.result()) + .log(); + final var invalidReason = r.result().getInvalidReason(); + throw new RuntimeException( + "Invalid or unsuccessful transaction" + + invalidReason.map(ir -> ", reason: " + ir).orElse("")); + } + + final var lowGasEstimation = r.result().getEstimateGasUsedByTransaction(); + final var lowResult = + transactionSimulationService.simulate( + createTransactionForSimulation(callParameters, lowGasEstimation, minGasPrice), + chainHeadHash, + tracer, + true); + + return lowResult + .map( + lr -> { + // if with the low estimation gas is successful the return this + // estimation + if (lr.isSuccessful()) { + log.trace( + "Low gas estimation {} successful, call params {}", + lowGasEstimation, + callParameters); + return lowGasEstimation; + } else { + log.trace( + "Low gas estimation {} unsuccessful, result{}, call params {}", + lowGasEstimation, + lr.result(), + callParameters); + + // else do a binary search to find the right estimation + var high = highGasEstimation(lr.getGasEstimate(), tracer); + var mid = high; + var low = lowGasEstimation; + while (low + 1 < high) { + mid = (high + low) / 2; + + final var binarySearchResult = + transactionSimulationService.simulate( + createTransactionForSimulation( + callParameters, mid, minGasPrice), + chainHeadHash, + tracer, + true); + + if (binarySearchResult.isEmpty() + || !binarySearchResult.get().isSuccessful()) { + low = mid; + log.atTrace() + .setMessage( + "Binary gas estimation search low={},med={},high={}, unsuccessful result {}, call params {}") + .addArgument(lowGasEstimation) + .addArgument( + () -> + binarySearchResult + .map(result -> result.result().toString()) + .orElse("empty")) + .addArgument(callParameters) + .log(); + + } else { + high = mid; + log.trace( + "Binary gas estimation search low={},med={},high={}, successful, call params {}", + lowGasEstimation, + callParameters); + } + } + return high; + } + }) + .orElseThrow(); + }) + .orElseThrow(); + } + + private JsonCallParameter parseRequest(final Object[] params) { + final var callParameters = parameterParser.required(params, 0, JsonCallParameter.class); + validateParameters(callParameters); + return callParameters; + } + + private void validateParameters(final JsonCallParameter callParameters) { + if (callParameters.getGasPrice() != null + && (callParameters.getMaxFeePerGas().isPresent() + || callParameters.getMaxPriorityFeePerGas().isPresent())) { + throw new InvalidJsonRpcParameters( + "gasPrice cannot be used with maxFeePerGas or maxPriorityFeePerGas"); + } + + if (callParameters.getGasLimit() > 0 + && callParameters.getGasLimit() > txValidatorConf.maxTxGasLimit()) { + throw new InvalidJsonRpcParameters("gasLimit above maximum"); + } + } + + /** + * Estimate gas by adding minimum gas remaining for some operation and the necessary gas for sub + * calls + * + * @param gasEstimation transaction gas estimation + * @param operationTracer estimate gas operation tracer + * @return estimate gas + */ + private long highGasEstimation( + final long gasEstimation, final EstimateGasOperationTracer operationTracer) { + // no more than 63/64s of the remaining gas can be passed to the sub calls + final double subCallMultiplier = + Math.pow(SUB_CALL_REMAINING_GAS_RATIO, operationTracer.getMaxDepth()); + // and minimum gas remaining is necessary for some operation (additionalStipend) + final long gasStipend = operationTracer.getStipendNeeded(); + return ((long) ((gasEstimation + gasStipend) * subCallMultiplier)); + } + + private Transaction createTransactionForSimulation( + final JsonCallParameter callParameters, final long maxTxGasLimit, final Wei minGasPrice) { + + final var txBuilder = + Transaction.builder() + .sender(callParameters.getFrom()) + .to(callParameters.getTo()) + .gasLimit(maxTxGasLimit) + .payload( + callParameters.getPayload() == null ? Bytes.EMPTY : callParameters.getPayload()) + .gasPrice( + callParameters.getGasPrice() == null ? minGasPrice : callParameters.getGasPrice()) + .value(callParameters.getValue() == null ? Wei.ZERO : callParameters.getValue()) + .signature(FAKE_SIGNATURE_FOR_SIZE_CALCULATION); + + callParameters.getMaxFeePerGas().ifPresent(txBuilder::maxFeePerGas); + callParameters.getMaxPriorityFeePerGas().ifPresent(txBuilder::maxPriorityFeePerGas); + callParameters.getAccessList().ifPresent(txBuilder::accessList); + + return txBuilder.build(); + } + + public record Response( + @JsonProperty String gasLimit, + @JsonProperty String baseFeePerGas, + @JsonProperty String priorityFeePerGas) {} +} diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorFactory.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorFactory.java index c53d07fdce..d9100a1b4b 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorFactory.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorFactory.java @@ -16,26 +16,26 @@ package net.consensys.linea.sequencer.txselection; import java.util.Map; -import java.util.function.Supplier; +import net.consensys.linea.config.LineaTransactionSelectorConfiguration; import net.consensys.linea.sequencer.txselection.selectors.LineaTransactionSelector; import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector; import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelectorFactory; /** Represents a factory for creating transaction selectors. */ public class LineaTransactionSelectorFactory implements PluginTransactionSelectorFactory { - private final LineaTransactionSelectorCliOptions options; - private final Supplier> limitsMapSupplier; + final LineaTransactionSelectorConfiguration txSelectorConfiguration; + private final Map limitsMap; public LineaTransactionSelectorFactory( - final LineaTransactionSelectorCliOptions options, - final Supplier> limitsMapSupplier) { - this.options = options; - this.limitsMapSupplier = limitsMapSupplier; + final LineaTransactionSelectorConfiguration txSelectorConfiguration, + final Map limitsMap) { + this.txSelectorConfiguration = txSelectorConfiguration; + this.limitsMap = limitsMap; } @Override public PluginTransactionSelector create() { - return new LineaTransactionSelector(options.toDomainObject(), this.limitsMapSupplier); + return new LineaTransactionSelector(txSelectorConfiguration, limitsMap); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorPlugin.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorPlugin.java index a48cc6394f..2de47045b5 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorPlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorPlugin.java @@ -20,19 +20,18 @@ import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; -import com.fasterxml.jackson.databind.ObjectMapper; import com.google.auto.service.AutoService; import com.google.common.io.Resources; import lombok.extern.slf4j.Slf4j; import net.consensys.linea.AbstractLineaRequiredPlugin; +import net.consensys.linea.config.LineaTransactionSelectorConfiguration; import org.apache.tuweni.toml.Toml; import org.apache.tuweni.toml.TomlParseResult; import org.apache.tuweni.toml.TomlTable; import org.hyperledger.besu.plugin.BesuContext; import org.hyperledger.besu.plugin.BesuPlugin; -import org.hyperledger.besu.plugin.services.PicoCLIOptions; import org.hyperledger.besu.plugin.services.TransactionSelectionService; /** @@ -44,13 +43,7 @@ @AutoService(BesuPlugin.class) public class LineaTransactionSelectorPlugin extends AbstractLineaRequiredPlugin { public static final String NAME = "linea"; - private final LineaTransactionSelectorCliOptions options; - private Optional service; - private final Map limitsMap = new ConcurrentHashMap<>(); - - public LineaTransactionSelectorPlugin() { - options = LineaTransactionSelectorCliOptions.create(); - } + private TransactionSelectionService transactionSelectionService; @Override public Optional getName() { @@ -59,48 +52,44 @@ public Optional getName() { @Override public void doRegister(final BesuContext context) { - final Optional cmdlineOptions = context.getService(PicoCLIOptions.class); - - if (cmdlineOptions.isEmpty()) { - throw new IllegalStateException("Failed to obtain PicoCLI options from the BesuContext"); - } - - cmdlineOptions.get().addPicoCLIOptions(getName().get(), options); - - service = context.getService(TransactionSelectionService.class); - createAndRegister( - service.orElseThrow( - () -> - new RuntimeException( - "Failed to obtain TransactionSelectionService from the BesuContext."))); + transactionSelectionService = + context + .getService(TransactionSelectionService.class) + .orElseThrow( + () -> + new RuntimeException( + "Failed to obtain TransactionSelectionService from the BesuContext.")); } @Override - public void start() { - log.debug("Starting {} with configuration: {}", NAME, options); - final LineaTransactionSelectorConfiguration lineaConfiguration = options.toDomainObject(); - ObjectMapper objectMapper = new ObjectMapper(); - + public void beforeExternalServices() { + super.beforeExternalServices(); try { - URL url = new File(lineaConfiguration.moduleLimitsFilePath()).toURI().toURL(); + URL url = new File(transactionSelectorConfiguration.moduleLimitsFilePath()).toURI().toURL(); final String tomlString = Resources.toString(url, StandardCharsets.UTF_8); TomlParseResult result = Toml.parse(tomlString); final TomlTable table = result.getTable("traces-limits"); - table - .toMap() - .keySet() - .forEach(key -> limitsMap.put(key, Math.toIntExact(table.getLong(key)))); + final Map limitsMap = + table.toMap().entrySet().stream() + .collect( + Collectors.toUnmodifiableMap( + Map.Entry::getKey, e -> Math.toIntExact((Long) e.getValue()))); + + createAndRegister(transactionSelectionService, transactionSelectorConfiguration, limitsMap); } catch (final Exception e) { final String errorMsg = "Problem reading the toml file containing the limits for the modules: " - + lineaConfiguration.moduleLimitsFilePath(); + + transactionSelectorConfiguration.moduleLimitsFilePath(); log.error(errorMsg); throw new RuntimeException(errorMsg, e); } } - private void createAndRegister(final TransactionSelectionService transactionSelectionService) { + private void createAndRegister( + final TransactionSelectionService transactionSelectionService, + final LineaTransactionSelectorConfiguration txSelectorConfiguration, + final Map limitsMap) { transactionSelectionService.registerTransactionSelectorFactory( - new LineaTransactionSelectorFactory(options, () -> this.limitsMap)); + new LineaTransactionSelectorFactory(txSelectorConfiguration, limitsMap)); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/LineaTransactionSelector.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/LineaTransactionSelector.java index 7230c3a090..f4b959b5bd 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/LineaTransactionSelector.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/LineaTransactionSelector.java @@ -16,10 +16,9 @@ import java.util.List; import java.util.Map; -import java.util.function.Supplier; import lombok.extern.slf4j.Slf4j; -import net.consensys.linea.sequencer.txselection.LineaTransactionSelectorConfiguration; +import net.consensys.linea.config.LineaTransactionSelectorConfiguration; import org.hyperledger.besu.datatypes.PendingTransaction; import org.hyperledger.besu.plugin.data.TransactionProcessingResult; import org.hyperledger.besu.plugin.data.TransactionSelectionResult; @@ -36,38 +35,31 @@ public class LineaTransactionSelector implements PluginTransactionSelector { public LineaTransactionSelector( LineaTransactionSelectorConfiguration lineaConfiguration, - final Supplier> limitsMapSupplier) { - this.selectors = createTransactionSelectors(lineaConfiguration, limitsMapSupplier); + final Map limitsMap) { + this.selectors = createTransactionSelectors(lineaConfiguration, limitsMap); } /** * Creates a list of selectors based on Linea configuration. * * @param lineaConfiguration The configuration to use. - * @param limitsMapSupplier The supplier for the limits map. + * @param limitsMap The limits map. * @return A list of selectors. */ private List createTransactionSelectors( final LineaTransactionSelectorConfiguration lineaConfiguration, - final Supplier> limitsMapSupplier) { + final Map limitsMap) { traceLineLimitTransactionSelector = new TraceLineLimitTransactionSelector( - limitsMapSupplier, + limitsMap, lineaConfiguration.moduleLimitsFilePath(), lineaConfiguration.overLinesLimitCacheSize()); return List.of( new MaxBlockCallDataTransactionSelector(lineaConfiguration.maxBlockCallDataSize()), new MaxBlockGasTransactionSelector(lineaConfiguration.maxGasPerBlock()), - new ProfitableTransactionSelector( - lineaConfiguration.verificationGasCost(), - lineaConfiguration.verificationCapacity(), - lineaConfiguration.gasPriceRatio(), - lineaConfiguration.minMargin(), - lineaConfiguration.adjustTxSize(), - lineaConfiguration.unprofitableCacheSize(), - lineaConfiguration.unprofitableRetryLimit()), + new ProfitableTransactionSelector(lineaConfiguration), traceLineLimitTransactionSelector); } diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelector.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelector.java index fc907d9772..415fdebfb7 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelector.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelector.java @@ -24,8 +24,9 @@ import java.util.Set; import com.google.common.annotations.VisibleForTesting; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import net.consensys.linea.bl.TransactionProfitabilityCalculator; +import net.consensys.linea.config.LineaTransactionSelectorConfiguration; import org.apache.commons.lang3.mutable.MutableBoolean; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.PendingTransaction; @@ -35,24 +36,23 @@ import org.hyperledger.besu.plugin.data.TransactionSelectionResult; import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector; import org.hyperledger.besu.plugin.services.txselection.TransactionEvaluationContext; -import org.slf4j.spi.LoggingEventBuilder; @Slf4j -@RequiredArgsConstructor public class ProfitableTransactionSelector implements PluginTransactionSelector { @VisibleForTesting protected static Set unprofitableCache = new LinkedHashSet<>(); @VisibleForTesting protected static Wei prevMinGasPrice = Wei.MAX_WEI; - private final int verificationGasCost; - private final int verificationCapacity; - private final int gasPriceRatio; - private final double minMargin; - private final int adjustTxSize; - private final int unprofitableCacheSize; - private final int unprofitableRetryLimit; + private final LineaTransactionSelectorConfiguration conf; + private final TransactionProfitabilityCalculator transactionProfitabilityCalculator; + private int unprofitableRetries; private MutableBoolean minGasPriceDecreased; + public ProfitableTransactionSelector(final LineaTransactionSelectorConfiguration conf) { + this.conf = conf; + this.transactionProfitabilityCalculator = new TransactionProfitabilityCalculator(conf); + } + @Override public TransactionSelectionResult evaluateTransactionPreProcessing( final TransactionEvaluationContext evaluationContext) { @@ -72,7 +72,7 @@ public TransactionSelectionResult evaluateTransactionPreProcessing( final long gasLimit = transaction.getGasLimit(); // check the upfront profitability using the gas limit of the tx - if (!isProfitable( + if (!transactionProfitabilityCalculator.isProfitable( "PreProcessing", transaction, minGasPrice.getAsBigInteger().doubleValue(), @@ -85,18 +85,18 @@ public TransactionSelectionResult evaluateTransactionPreProcessing( // only retry unprofitable txs if the min gas price went down if (minGasPriceDecreased.isTrue()) { - if (unprofitableRetries >= unprofitableRetryLimit) { + if (unprofitableRetries >= conf.unprofitableRetryLimit()) { log.atTrace() .setMessage("Limit of unprofitable tx retries reached: {}/{}") .addArgument(unprofitableRetries) - .addArgument(unprofitableRetryLimit); + .addArgument(conf.unprofitableRetryLimit()); return TX_UNPROFITABLE_RETRY_LIMIT; } log.atTrace() .setMessage("Retrying unprofitable tx. Retry: {}/{}") .addArgument(unprofitableRetries) - .addArgument(unprofitableRetryLimit); + .addArgument(conf.unprofitableRetryLimit()); unprofitableCache.remove(transaction.getHash()); unprofitableRetries++; @@ -127,7 +127,8 @@ public TransactionSelectionResult evaluateTransactionPostProcessing( evaluationContext.getTransactionGasPrice().getAsBigInteger().doubleValue(); final long gasUsed = processingResult.getEstimateGasUsedByTransaction(); - if (!isProfitable("PostProcessing", transaction, minGasPrice, effectiveGasPrice, gasUsed)) { + if (!transactionProfitabilityCalculator.isProfitable( + "PostProcessing", transaction, minGasPrice, effectiveGasPrice, gasUsed)) { rememberUnprofitable(transaction); return TX_UNPROFITABLE; } @@ -152,53 +153,8 @@ public void onTransactionNotSelected( } } - private boolean isProfitable( - final String step, - final Transaction transaction, - final double minGasPrice, - final double effectiveGasPrice, - final long gas) { - final double revenue = effectiveGasPrice * gas; - - final double l1GasPrice = minGasPrice * gasPriceRatio; - final int serializedSize = Math.max(0, transaction.getSize() + adjustTxSize); - final double verificationGasCostSlice = - (((double) serializedSize) / verificationCapacity) * verificationGasCost; - final double cost = l1GasPrice * verificationGasCostSlice; - - final double margin = revenue / cost; - - if (margin < minMargin) { - log( - log.atDebug(), - step, - transaction, - margin, - effectiveGasPrice, - gas, - minGasPrice, - l1GasPrice, - serializedSize, - adjustTxSize); - return false; - } else { - log( - log.atTrace(), - step, - transaction, - margin, - effectiveGasPrice, - gas, - minGasPrice, - l1GasPrice, - serializedSize, - adjustTxSize); - return true; - } - } - private void rememberUnprofitable(final Transaction transaction) { - while (unprofitableCache.size() >= unprofitableCacheSize) { + while (unprofitableCache.size() >= conf.unprofitableCacheSize()) { final var it = unprofitableCache.iterator(); if (it.hasNext()) { it.next(); @@ -208,35 +164,4 @@ private void rememberUnprofitable(final Transaction transaction) { unprofitableCache.add(transaction.getHash()); log.atTrace().setMessage("unprofitableCache={}").addArgument(unprofitableCache::size).log(); } - - private void log( - final LoggingEventBuilder leb, - final String step, - final Transaction transaction, - final double margin, - final double effectiveGasPrice, - final long gasUsed, - final double minGasPrice, - final double l1GasPrice, - final int serializedSize, - final int adjustTxSize) { - leb.setMessage( - "Step {}. Transaction {} has a margin of {}, minMargin={}, verificationCapacity={}, " - + "verificationGasCost={}, gasPriceRatio={}, effectiveGasPrice={}, gasUsed={}, minGasPrice={}, " - + "l1GasPrice={}, serializedSize={}, adjustTxSize={}") - .addArgument(step) - .addArgument(transaction::getHash) - .addArgument(margin) - .addArgument(minMargin) - .addArgument(verificationCapacity) - .addArgument(verificationGasCost) - .addArgument(gasPriceRatio) - .addArgument(effectiveGasPrice) - .addArgument(gasUsed) - .addArgument(minGasPrice) - .addArgument(l1GasPrice) - .addArgument(serializedSize) - .addArgument(adjustTxSize) - .log(); - } } diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/TraceLineLimitTransactionSelector.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/TraceLineLimitTransactionSelector.java index 479f981a1e..a9af311c36 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/TraceLineLimitTransactionSelector.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/TraceLineLimitTransactionSelector.java @@ -22,12 +22,12 @@ import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; -import java.util.function.Supplier; import java.util.stream.Collectors; import com.google.common.annotations.VisibleForTesting; import lombok.extern.slf4j.Slf4j; import net.consensys.linea.zktracer.ZkTracer; +import net.consensys.linea.zktracer.module.Module; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.PendingTransaction; import org.hyperledger.besu.datatypes.Transaction; @@ -58,11 +58,17 @@ public class TraceLineLimitTransactionSelector implements PluginTransactionSelec private Map currCumulatedLineCount; public TraceLineLimitTransactionSelector( - final Supplier> moduleLimitsProvider, + final Map moduleLimits, final String limitFilePath, final int overLimitCacheSize) { - moduleLimits = moduleLimitsProvider.get(); + this.moduleLimits = moduleLimits; zkTracer = new ZkTracerWithLog(); + for (Module m : zkTracer.getHub().getModulesToCount()) { + if (!moduleLimits.containsKey(m.moduleKey())) { + throw new IllegalStateException( + "Limit for module %s not defined in %s".formatted(m.moduleKey(), limitFilePath)); + } + } zkTracer.traceStartConflation(1L); this.limitFilePath = limitFilePath; this.overLimitCacheSize = overLimitCacheSize; diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidator.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidator.java index 494b0a8c05..fc7fee8626 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidator.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidator.java @@ -20,6 +20,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import net.consensys.linea.config.LineaTransactionValidatorConfiguration; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Transaction; import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionValidator; diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorFactory.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorFactory.java index 319ea79ba2..bbbd8424ed 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorFactory.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorFactory.java @@ -17,6 +17,7 @@ import java.util.Set; +import net.consensys.linea.config.LineaTransactionValidatorConfiguration; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionValidator; import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionValidatorFactory; @@ -24,17 +25,18 @@ /** Represents a factory for creating transaction validators. */ public class LineaTransactionValidatorFactory implements PluginTransactionValidatorFactory { - private final LineaTransactionValidatorCliOptions options; + private final LineaTransactionValidatorConfiguration transactionValidatorConfiguration; private final Set
denied; public LineaTransactionValidatorFactory( - final LineaTransactionValidatorCliOptions options, final Set
denied) { - this.options = options; + final LineaTransactionValidatorConfiguration transactionValidatorConfiguration, + final Set
denied) { + this.transactionValidatorConfiguration = transactionValidatorConfiguration; this.denied = denied; } @Override public PluginTransactionValidator create() { - return new LineaTransactionValidator(options.toDomainObject(), denied); + return new LineaTransactionValidator(transactionValidatorConfiguration, denied); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorPlugin.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorPlugin.java index c3422b1c21..54df1ea4b4 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorPlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorPlugin.java @@ -15,20 +15,21 @@ package net.consensys.linea.sequencer.txvalidation; +import java.io.File; import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.HashSet; +import java.nio.file.Path; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import java.util.stream.Stream; import com.google.auto.service.AutoService; import lombok.extern.slf4j.Slf4j; import net.consensys.linea.AbstractLineaRequiredPlugin; +import net.consensys.linea.config.LineaTransactionValidatorConfiguration; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.plugin.BesuContext; import org.hyperledger.besu.plugin.BesuPlugin; -import org.hyperledger.besu.plugin.services.PicoCLIOptions; import org.hyperledger.besu.plugin.services.PluginTransactionValidatorService; /** @@ -41,12 +42,7 @@ @AutoService(BesuPlugin.class) public class LineaTransactionValidatorPlugin extends AbstractLineaRequiredPlugin { public static final String NAME = "linea"; - private final LineaTransactionValidatorCliOptions options; - private final Set
denied = new HashSet<>(); - - public LineaTransactionValidatorPlugin() { - options = LineaTransactionValidatorCliOptions.create(); - } + private PluginTransactionValidatorService transactionValidatorService; @Override public Optional getName() { @@ -55,43 +51,34 @@ public Optional getName() { @Override public void doRegister(final BesuContext context) { - final Optional cmdlineOptions = context.getService(PicoCLIOptions.class); - - if (cmdlineOptions.isEmpty()) { - throw new IllegalStateException("Failed to obtain PicoCLI options from the BesuContext"); - } - cmdlineOptions.get().addPicoCLIOptions(NAME, options); - - Optional service = - context.getService(PluginTransactionValidatorService.class); - createAndRegister( - service.orElseThrow( - () -> - new RuntimeException( - "Failed to obtain TransactionValidationService from the BesuContext."))); + transactionValidatorService = + context + .getService(PluginTransactionValidatorService.class) + .orElseThrow( + () -> + new RuntimeException( + "Failed to obtain TransactionValidationService from the BesuContext.")); } @Override - public void start() { - final LineaTransactionValidatorConfiguration config = options.toDomainObject(); - - try (Stream lines = Files.lines(Paths.get(config.denyListPath()))) { - lines.forEach( - l -> { - final Address address = Address.fromHexString(l.trim()); - denied.add(address); - }); + public void beforeExternalServices() { + super.beforeExternalServices(); + try (Stream lines = + Files.lines(Path.of(new File(transactionValidatorConfiguration.denyListPath()).toURI()))) { + final Set
denied = + lines.map(l -> Address.fromHexString(l.trim())).collect(Collectors.toUnmodifiableSet()); + createAndRegister(transactionValidatorService, transactionValidatorConfiguration, denied); } catch (Exception e) { throw new RuntimeException(e); } - - log.debug("Starting {} with configuration: {}", NAME, options); } private void createAndRegister( - final PluginTransactionValidatorService transactionValidationService) { + final PluginTransactionValidatorService transactionValidationService, + final LineaTransactionValidatorConfiguration transactionValidatorConfiguration, + final Set
denied) { transactionValidationService.registerTransactionValidatorFactory( - new LineaTransactionValidatorFactory(options, denied)); + new LineaTransactionValidatorFactory(transactionValidatorConfiguration, denied)); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/ZkTracer.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/ZkTracer.java index eb45f90766..c30e20fd5e 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/ZkTracer.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/ZkTracer.java @@ -67,6 +67,13 @@ public ZkTracer() { Toml.parse(getClass().getClassLoader().getResourceAsStream("spillings.toml")) .getTable("spillings"); table.toMap().keySet().forEach(k -> spillings.put(k, Math.toIntExact(table.getLong(k)))); + + for (Module m : this.hub.getModulesToCount()) { + if (!this.spillings.containsKey(m.moduleKey())) { + throw new IllegalStateException( + "Spilling for module " + m.moduleKey() + " not defined in spillings.toml"); + } + } } catch (final Exception e) { throw new RuntimeException(e); } diff --git a/arithmetization/src/test/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelectorTest.java b/arithmetization/src/test/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelectorTest.java index 15873f2615..6b342161f5 100644 --- a/arithmetization/src/test/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelectorTest.java +++ b/arithmetization/src/test/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelectorTest.java @@ -23,6 +23,8 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import net.consensys.linea.config.LineaTransactionSelectorCliOptions; +import net.consensys.linea.config.LineaTransactionSelectorConfiguration; import org.apache.tuweni.bytes.Bytes32; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.PendingTransaction; @@ -42,7 +44,16 @@ public class ProfitableTransactionSelectorTest { private static final int ADJUST_TX_SIZE = -45; private static final int UNPROFITABLE_CACHE_SIZE = 2; private static final int UNPROFITABLE_RETRY_LIMIT = 1; - + private final LineaTransactionSelectorConfiguration conf = + LineaTransactionSelectorCliOptions.create().toDomainObject().toBuilder() + .gasPriceRatio(GAS_PRICE_RATIO) + .adjustTxSize(ADJUST_TX_SIZE) + .minMargin(MIN_MARGIN) + .unprofitableCacheSize(UNPROFITABLE_CACHE_SIZE) + .unprofitableRetryLimit(UNPROFITABLE_RETRY_LIMIT) + .verificationCapacity(VERIFICATION_CAPACITY) + .verificationGasCost(VERIFICATION_GAS_COST) + .build(); private TestableProfitableTransactionSelector transactionSelector; @BeforeEach @@ -52,14 +63,7 @@ public void initialize() { } private TestableProfitableTransactionSelector newSelectorForNewBlock() { - return new TestableProfitableTransactionSelector( - VERIFICATION_GAS_COST, - VERIFICATION_CAPACITY, - GAS_PRICE_RATIO, - MIN_MARGIN, - ADJUST_TX_SIZE, - UNPROFITABLE_CACHE_SIZE, - UNPROFITABLE_RETRY_LIMIT); + return new TestableProfitableTransactionSelector(conf); } @Test @@ -423,22 +427,8 @@ private void notifySelector( private static class TestableProfitableTransactionSelector extends ProfitableTransactionSelector { - TestableProfitableTransactionSelector( - final int verificationGasCost, - final int verificationCapacity, - final int gasPriceRatio, - final double minMargin, - final int adjustTxSize, - final int unprofitableCacheSize, - final int unprofitableRetryLimit) { - super( - verificationGasCost, - verificationCapacity, - gasPriceRatio, - minMargin, - adjustTxSize, - unprofitableCacheSize, - unprofitableRetryLimit); + TestableProfitableTransactionSelector(final LineaTransactionSelectorConfiguration conf) { + super(conf); } boolean isUnprofitableTxCached(final Hash txHash) { diff --git a/arithmetization/src/test/java/net/consensys/linea/sequencer/txselection/selectors/TraceLineLimitTransactionSelectorTest.java b/arithmetization/src/test/java/net/consensys/linea/sequencer/txselection/selectors/TraceLineLimitTransactionSelectorTest.java index 452d6785e0..ca84a9ff89 100644 --- a/arithmetization/src/test/java/net/consensys/linea/sequencer/txselection/selectors/TraceLineLimitTransactionSelectorTest.java +++ b/arithmetization/src/test/java/net/consensys/linea/sequencer/txselection/selectors/TraceLineLimitTransactionSelectorTest.java @@ -24,7 +24,6 @@ import java.io.IOException; import java.io.InputStream; import java.util.Map; -import java.util.function.Supplier; import java.util.stream.Collectors; import org.apache.tuweni.bytes.Bytes32; @@ -53,7 +52,7 @@ public void initialize() { private TestableTraceLineLimitTransactionSelector newSelectorForNewBlock() { return new TestableTraceLineLimitTransactionSelector( - () -> lineCountLimits, "line-limits.toml", OVER_LINE_COUNT_LIMIT_CACHE_SIZE); + lineCountLimits, "line-limits.toml", OVER_LINE_COUNT_LIMIT_CACHE_SIZE); } private Map loadLineCountLimitConf() { @@ -218,10 +217,10 @@ private TestTransactionEvaluationContext mockEvaluationContext( private class TestableTraceLineLimitTransactionSelector extends TraceLineLimitTransactionSelector { TestableTraceLineLimitTransactionSelector( - final Supplier> moduleLimitsProvider, + final Map moduleLimits, final String limitFilePath, final int overLimitCacheSize) { - super(moduleLimitsProvider, limitFilePath, overLimitCacheSize); + super(moduleLimits, limitFilePath, overLimitCacheSize); } void reset() { diff --git a/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorTest.java b/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorTest.java index 8d18daf4db..97bac3003c 100644 --- a/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorTest.java +++ b/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorTest.java @@ -21,6 +21,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import net.consensys.linea.config.LineaTransactionValidatorConfiguration; import org.apache.tuweni.bytes.Bytes; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; diff --git a/gradle/dist.gradle b/gradle/dist.gradle index 0dc571ae0a..28631a9cc2 100644 --- a/gradle/dist.gradle +++ b/gradle/dist.gradle @@ -49,7 +49,10 @@ jar { ) } - from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } + from { + configurations.runtimeClasspath.filter( {! (it.name =~ /log4j.*\.jar/ )} ) + .collect {it.isDirectory() ? it : zipTree(it) } + } exclude 'META-INF/*.RSA', 'META-INF/*.SF', 'META-INF/*.DSA' duplicatesStrategy(DuplicatesStrategy.INCLUDE) }