From 868073e35d87774812e681bff58e8ea8733c0e0b Mon Sep 17 00:00:00 2001 From: Gabriel-Trintinalia Date: Mon, 8 Apr 2024 15:19:25 -0300 Subject: [PATCH 01/12] feature: Add module limit verification to linea_estimateGas Signed-off-by: Gabriel-Trintinalia --- .../AbstractLineaSharedOptionsPlugin.java | 11 +- .../config/LineaTracerConfiguration.java | 22 +++ .../LineaTracerConfigurationCLiOptions.java | 72 ++++++++ .../LineaTransactionSelectorCliOptions.java | 11 -- ...LineaTransactionSelectorConfiguration.java | 1 - .../rpc/linea/LineaEndpointServicePlugin.java | 7 +- .../linea/rpc/linea/LineaEstimateGas.java | 64 ++++++- .../linea/sequencer/LimitModuleHelper.java | 54 ++++++ .../linea/sequencer/TracerAggregator.java | 174 ++++++++++++++++++ .../LineaTransactionSelectorFactory.java | 11 +- .../LineaTransactionSelectorPlugin.java | 51 +---- .../selectors/LineaTransactionSelector.java | 8 +- .../selectors/ModuleLineCountAccumulator.java | 123 +++++++++++++ .../TraceLineLimitTransactionSelector.java | 77 ++++---- ...TraceLineLimitTransactionSelectorTest.java | 5 +- 15 files changed, 577 insertions(+), 114 deletions(-) create mode 100644 arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfiguration.java create mode 100644 arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfigurationCLiOptions.java create mode 100644 arithmetization/src/main/java/net/consensys/linea/sequencer/LimitModuleHelper.java create mode 100644 arithmetization/src/main/java/net/consensys/linea/sequencer/TracerAggregator.java create mode 100644 arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ModuleLineCountAccumulator.java diff --git a/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java b/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java index afdcacdc..6625e149 100644 --- a/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java @@ -23,6 +23,8 @@ import net.consensys.linea.config.LineaProfitabilityConfiguration; import net.consensys.linea.config.LineaRpcCliOptions; import net.consensys.linea.config.LineaRpcConfiguration; +import net.consensys.linea.config.LineaTracerConfiguration; +import net.consensys.linea.config.LineaTracerConfigurationCLiOptions; import net.consensys.linea.config.LineaTransactionPoolValidatorCliOptions; import net.consensys.linea.config.LineaTransactionPoolValidatorConfiguration; import net.consensys.linea.config.LineaTransactionSelectorCliOptions; @@ -42,14 +44,16 @@ public abstract class AbstractLineaSharedOptionsPlugin implements BesuPlugin { private static LineaRpcCliOptions rpcCliOptions; private static LineaProfitabilityCliOptions profitabilityCliOptions; protected static LineaTransactionSelectorConfiguration transactionSelectorConfiguration; + protected static LineaTracerConfigurationCLiOptions tracerConfigurationCliOptions; protected static LineaTransactionPoolValidatorConfiguration transactionPoolValidatorConfiguration; protected static LineaL1L2BridgeConfiguration l1L2BridgeConfiguration; protected static LineaRpcConfiguration rpcConfiguration; protected static LineaProfitabilityConfiguration profitabilityConfiguration; + protected static LineaTracerConfiguration tracerConfiguration; static { // force the initialization of the gnark compress native library to fail fast in case of issues - LibCompress.CompressedSize(new byte[0], 0); + // LibCompress.CompressedSize(new byte[0], 0); } @Override @@ -67,12 +71,14 @@ public synchronized void register(final BesuContext context) { l1L2BridgeCliOptions = LineaL1L2BridgeCliOptions.create(); rpcCliOptions = LineaRpcCliOptions.create(); profitabilityCliOptions = LineaProfitabilityCliOptions.create(); + tracerConfigurationCliOptions = LineaTracerConfigurationCLiOptions.create(); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, transactionSelectorCliOptions); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, transactionPoolValidatorCliOptions); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, l1L2BridgeCliOptions); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, rpcCliOptions); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, profitabilityCliOptions); + cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, tracerConfigurationCliOptions); cliOptionsRegistered = true; } } @@ -85,6 +91,7 @@ public void beforeExternalServices() { l1L2BridgeConfiguration = l1L2BridgeCliOptions.toDomainObject(); rpcConfiguration = rpcCliOptions.toDomainObject(); profitabilityConfiguration = profitabilityCliOptions.toDomainObject(); + tracerConfiguration = tracerConfigurationCliOptions.toDomainObject(); configured = true; } @@ -109,6 +116,8 @@ public void beforeExternalServices() { "Configured plugin {} with profitability calculator configuration: {}", getName(), profitabilityConfiguration); + + log.debug("Configured plugin {} with tracer configuration: {}", getName(), tracerConfiguration); } @Override diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfiguration.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfiguration.java new file mode 100644 index 00000000..bf2c3ced --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfiguration.java @@ -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 transaction selectors configuration. */ +@Builder(toBuilder = true) +public record LineaTracerConfiguration(String moduleLimitsFilePath) {} diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfigurationCLiOptions.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfigurationCLiOptions.java new file mode 100644 index 00000000..74377c3b --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfigurationCLiOptions.java @@ -0,0 +1,72 @@ +/* + * 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 static net.consensys.linea.config.LineaTransactionSelectorCliOptions.DEFAULT_MODULE_LIMIT_FILE_PATH; +import static net.consensys.linea.config.LineaTransactionSelectorCliOptions.MODULE_LIMIT_FILE_PATH; + +import com.google.common.base.MoreObjects; +import picocli.CommandLine; + +public class LineaTracerConfigurationCLiOptions { + + @CommandLine.Option( + names = {MODULE_LIMIT_FILE_PATH}, + hidden = true, + paramLabel = "", + description = + "Path to the toml file containing the module limits (default: ${DEFAULT-VALUE})") + private String moduleLimitFilePath = DEFAULT_MODULE_LIMIT_FILE_PATH; + + private LineaTracerConfigurationCLiOptions() {} + + /** + * Create Linea cli options. + * + * @return the Linea cli options + */ + public static LineaTracerConfigurationCLiOptions create() { + return new LineaTracerConfigurationCLiOptions(); + } + + /** + * Linea cli options from config. + * + * @param config the config + * @return the Linea cli options + */ + public static LineaTracerConfigurationCLiOptions fromConfig( + final LineaTracerConfiguration config) { + final LineaTracerConfigurationCLiOptions options = create(); + options.moduleLimitFilePath = config.moduleLimitsFilePath(); + return options; + } + + /** + * To domain object Linea factory configuration. + * + * @return the Linea factory configuration + */ + public LineaTracerConfiguration toDomainObject() { + return LineaTracerConfiguration.builder().moduleLimitsFilePath(moduleLimitFilePath).build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add(MODULE_LIMIT_FILE_PATH, moduleLimitFilePath) + .toString(); + } +} diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorCliOptions.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorCliOptions.java index 9f8f0ba3..85856c95 100644 --- a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorCliOptions.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorCliOptions.java @@ -48,14 +48,6 @@ public class LineaTransactionSelectorCliOptions { description = "Maximum size for the calldata of a block (default: ${DEFAULT-VALUE})") private int maxBlockCallDataSize = DEFAULT_MAX_BLOCK_CALLDATA_SIZE; - @CommandLine.Option( - names = {MODULE_LIMIT_FILE_PATH}, - hidden = true, - paramLabel = "", - description = - "Path to the toml file containing the module limits (default: ${DEFAULT-VALUE})") - private String moduleLimitFilePath = DEFAULT_MODULE_LIMIT_FILE_PATH; - @Positive @CommandLine.Option( names = {OVER_LINE_COUNT_LIMIT_CACHE_SIZE}, @@ -112,7 +104,6 @@ public static LineaTransactionSelectorCliOptions fromConfig( final LineaTransactionSelectorConfiguration config) { final LineaTransactionSelectorCliOptions options = create(); options.maxBlockCallDataSize = config.maxBlockCallDataSize(); - options.moduleLimitFilePath = config.moduleLimitsFilePath(); options.overLineCountLimitCacheSize = config.overLinesLimitCacheSize(); options.maxGasPerBlock = config.maxGasPerBlock(); options.unprofitableCacheSize = config.unprofitableCacheSize(); @@ -128,7 +119,6 @@ public static LineaTransactionSelectorCliOptions fromConfig( public LineaTransactionSelectorConfiguration toDomainObject() { return LineaTransactionSelectorConfiguration.builder() .maxBlockCallDataSize(maxBlockCallDataSize) - .moduleLimitsFilePath(moduleLimitFilePath) .overLinesLimitCacheSize(overLineCountLimitCacheSize) .maxGasPerBlock(maxGasPerBlock) .unprofitableCacheSize(unprofitableCacheSize) @@ -140,7 +130,6 @@ public LineaTransactionSelectorConfiguration toDomainObject() { public String toString() { return MoreObjects.toStringHelper(this) .add(MAX_BLOCK_CALLDATA_SIZE, maxBlockCallDataSize) - .add(MODULE_LIMIT_FILE_PATH, moduleLimitFilePath) .add(OVER_LINE_COUNT_LIMIT_CACHE_SIZE, overLineCountLimitCacheSize) .add(MAX_GAS_PER_BLOCK, maxGasPerBlock) .add(UNPROFITABLE_CACHE_SIZE, unprofitableCacheSize) diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorConfiguration.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorConfiguration.java index 09afc4f2..57b56114 100644 --- a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorConfiguration.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorConfiguration.java @@ -21,7 +21,6 @@ @Builder(toBuilder = true) public record LineaTransactionSelectorConfiguration( int maxBlockCallDataSize, - String moduleLimitsFilePath, int overLinesLimitCacheSize, long maxGasPerBlock, int unprofitableCacheSize, 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 index 0dd3e8d4..e582bd06 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java @@ -15,6 +15,8 @@ package net.consensys.linea.rpc.linea; +import static net.consensys.linea.sequencer.LimitModuleHelper.createLimitModules; + import com.google.auto.service.AutoService; import lombok.extern.slf4j.Slf4j; import net.consensys.linea.AbstractLineaRequiredPlugin; @@ -87,6 +89,9 @@ public void doRegister(final BesuContext context) { public void beforeExternalServices() { super.beforeExternalServices(); lineaEstimateGasMethod.init( - rpcConfiguration, transactionPoolValidatorConfiguration, profitabilityConfiguration); + rpcConfiguration, + transactionPoolValidatorConfiguration, + profitabilityConfiguration, + createLimitModules(tracerConfiguration)); } } 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 index e5c91a99..60129659 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java @@ -15,11 +15,14 @@ package net.consensys.linea.rpc.linea; +import static net.consensys.linea.sequencer.txselection.selectors.ModuleLineCountAccumulator.ModuleLineCountResult.MODULE_NOT_DEFINED; +import static net.consensys.linea.sequencer.txselection.selectors.ModuleLineCountAccumulator.ModuleLineCountResult.TX_MODULE_LINE_COUNT_OVERFLOW; import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity.create; import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import com.fasterxml.jackson.annotation.JsonProperty; @@ -29,6 +32,9 @@ import net.consensys.linea.config.LineaProfitabilityConfiguration; import net.consensys.linea.config.LineaRpcConfiguration; import net.consensys.linea.config.LineaTransactionPoolValidatorConfiguration; +import net.consensys.linea.sequencer.TracerAggregator; +import net.consensys.linea.sequencer.txselection.selectors.ModuleLineCountAccumulator; +import net.consensys.linea.zktracer.ZkTracer; import org.apache.tuweni.bytes.Bytes; import org.bouncycastle.asn1.sec.SECNamedCurves; import org.bouncycastle.asn1.x9.X9ECParameters; @@ -77,6 +83,7 @@ public class LineaEstimateGas { private LineaTransactionPoolValidatorConfiguration txValidatorConf; private LineaProfitabilityConfiguration profitabilityConf; private TransactionProfitabilityCalculator txProfitabilityCalculator; + private Map limitsMap; public LineaEstimateGas( final BesuConfiguration besuConfiguration, @@ -90,11 +97,13 @@ public LineaEstimateGas( public void init( LineaRpcConfiguration rpcConfiguration, final LineaTransactionPoolValidatorConfiguration transactionValidatorConfiguration, - final LineaProfitabilityConfiguration profitabilityConf) { + final LineaProfitabilityConfiguration profitabilityConf, + final Map limitsMap) { this.rpcConfiguration = rpcConfiguration; this.txValidatorConf = transactionValidatorConfiguration; this.profitabilityConf = profitabilityConf; this.txProfitabilityCalculator = new TransactionProfitabilityCalculator(profitabilityConf); + this.limitsMap = limitsMap; } public String getNamespace() { @@ -187,10 +196,24 @@ private Long estimateGasUsed( final JsonCallParameter callParameters, final Transaction transaction, final Wei minGasPrice) { - final var tracer = new EstimateGasOperationTracer(); + + var estimateGasOperationTracer = new EstimateGasOperationTracer(); + var zkTracer = new ZkTracer(); + TracerAggregator tracerAggregator = + TracerAggregator.create(estimateGasOperationTracer, zkTracer); + final var chainHeadHash = blockchainService.getChainHeadHash(); final var maybeSimulationResults = - transactionSimulationService.simulate(transaction, chainHeadHash, tracer, true); + transactionSimulationService.simulate(transaction, chainHeadHash, tracerAggregator, true); + + ModuleLineCountAccumulator accumulator = new ModuleLineCountAccumulator(limitsMap); + + ModuleLineCountAccumulator.VerificationResult moduleLimit = + accumulator.verify(zkTracer.getModulesLineCount()); + + if (moduleLimit.getResult() != ModuleLineCountAccumulator.ModuleLineCountResult.VALID) { + handleModuleOverLimit(moduleLimit); + } return maybeSimulationResults .map( @@ -231,7 +254,7 @@ private Long estimateGasUsed( transactionSimulationService.simulate( createTransactionForSimulation(callParameters, lowGasEstimation, minGasPrice), chainHeadHash, - tracer, + tracerAggregator, true); return lowResult @@ -255,7 +278,7 @@ private Long estimateGasUsed( // else do a binary search to find the right estimation int iterations = 0; - var high = highGasEstimation(lr.getGasEstimate(), tracer); + var high = highGasEstimation(lr.getGasEstimate(), tracerAggregator); var mid = high; var low = lowGasEstimation; while (low + 1 < high) { @@ -267,7 +290,7 @@ private Long estimateGasUsed( createTransactionForSimulation( callParameters, mid, minGasPrice), chainHeadHash, - tracer, + tracerAggregator, true); if (binarySearchResult.isEmpty() @@ -351,13 +374,14 @@ private void validateParameters(final JsonCallParameter callParameters) { * @param operationTracer estimate gas operation tracer * @return estimate gas */ - private long highGasEstimation( - final long gasEstimation, final EstimateGasOperationTracer operationTracer) { + private long highGasEstimation(final long gasEstimation, final TracerAggregator operationTracer) { + var estimateGasTracer = operationTracer.getTracer(EstimateGasOperationTracer.class); + // 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()); + Math.pow(SUB_CALL_REMAINING_GAS_RATIO, estimateGasTracer.getMaxDepth()); // and minimum gas remaining is necessary for some operation (additionalStipend) - final long gasStipend = operationTracer.getStipendNeeded(); + final long gasStipend = estimateGasTracer.getStipendNeeded(); return ((long) ((gasEstimation + gasStipend) * subCallMultiplier)); } @@ -383,6 +407,26 @@ private Transaction createTransactionForSimulation( return txBuilder.build(); } + private void handleModuleOverLimit( + ModuleLineCountAccumulator.VerificationResult moduleLimitResult) { + // Throw specific exceptions based on the type of limit exceeded + if (moduleLimitResult.getResult() == MODULE_NOT_DEFINED) { + String moduleNotDefinedMsg = + String.format( + "Module %s does not exist in the limits file.", moduleLimitResult.getModuleName()); + log.error(moduleNotDefinedMsg); + throw new PluginRpcEndpointException(new TransactionSimulationError(moduleNotDefinedMsg)); + } + if (moduleLimitResult.getResult() == TX_MODULE_LINE_COUNT_OVERFLOW) { + String txOverflowMsg = + String.format( + "Transaction line count for module %s is above the limit", + moduleLimitResult.getModuleName()); + log.warn(txOverflowMsg); + throw new PluginRpcEndpointException(new TransactionSimulationError(txOverflowMsg)); + } + } + public record Response( @JsonProperty String gasLimit, @JsonProperty String baseFeePerGas, diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/LimitModuleHelper.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/LimitModuleHelper.java new file mode 100644 index 00000000..1dcd4556 --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/LimitModuleHelper.java @@ -0,0 +1,54 @@ +/* + * 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.sequencer; + +import java.io.File; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.stream.Collectors; + +import com.google.common.io.Resources; +import lombok.extern.slf4j.Slf4j; +import net.consensys.linea.config.LineaTracerConfiguration; +import org.apache.tuweni.toml.Toml; +import org.apache.tuweni.toml.TomlParseResult; +import org.apache.tuweni.toml.TomlTable; + +@Slf4j +public class LimitModuleHelper { + public static Map createLimitModules( + LineaTracerConfiguration lineaTracerConfiguration) { + try { + URL url = new File(lineaTracerConfiguration.moduleLimitsFilePath()).toURI().toURL(); + final String tomlString = Resources.toString(url, StandardCharsets.UTF_8); + TomlParseResult result = Toml.parse(tomlString); + final TomlTable table = result.getTable("traces-limits"); + final Map limitsMap = + table.toMap().entrySet().stream() + .collect( + Collectors.toUnmodifiableMap( + Map.Entry::getKey, e -> Math.toIntExact((Long) e.getValue()))); + + return limitsMap; + } catch (final Exception e) { + final String errorMsg = + "Problem reading the toml file containing the limits for the modules: " + + lineaTracerConfiguration.moduleLimitsFilePath(); + log.error(errorMsg); + throw new RuntimeException(errorMsg, e); + } + } +} diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/TracerAggregator.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/TracerAggregator.java new file mode 100644 index 00000000..05410fa5 --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/TracerAggregator.java @@ -0,0 +1,174 @@ +/* + * 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.sequencer; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes; +import org.hyperledger.besu.datatypes.Transaction; +import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.log.Log; +import org.hyperledger.besu.evm.operation.Operation; +import org.hyperledger.besu.evm.tracing.OperationTracer; +import org.hyperledger.besu.evm.worldstate.WorldView; + +/** + * Aggregates multiple {@link OperationTracer} instances, allowing them to be treated as a single + * tracer. This class facilitates the registration and delegation of tracing operations to multiple + * tracers. + */ +public class TracerAggregator implements OperationTracer { + private final List tracers = new ArrayList<>(); + + /** + * Registers an {@link OperationTracer} instance with the aggregator. If a tracer of the same + * class is already registered, an {@link IllegalArgumentException} is thrown. + * + * @param tracer the tracer to register + * @throws IllegalArgumentException if a tracer of the same class is already registered + */ + public void register(OperationTracer tracer) { + // Check if a tracer of the same class is already registered + for (OperationTracer existingTracer : tracers) { + if (existingTracer.getClass().equals(tracer.getClass())) { + throw new IllegalArgumentException( + "A tracer of class " + tracer.getClass().getName() + " is already registered."); + } + } + tracers.add(tracer); + } + + /** + * Retrieves a registered tracer by its class type. + * + * @param clazz the class of the tracer to retrieve + * @param the type of the tracer + * @return the registered tracer of the specified type + * @throws IllegalArgumentException if no tracer of the specified class is registered + */ + public T getTracer(Class clazz) { + for (OperationTracer tracer : tracers) { + if (clazz.isInstance(tracer)) { + return clazz.cast(tracer); + } + } + throw new IllegalArgumentException("No tracer found for class: " + clazz.getName()); + } + + /** + * Creates a {@link TracerAggregator} instance and registers the provided tracers. + * + * @param tracers the tracers to register with the aggregator + * @return a new {@link TracerAggregator} instance with the provided tracers registered + */ + public static TracerAggregator create(OperationTracer... tracers) { + TracerAggregator aggregator = new TracerAggregator(); + for (OperationTracer tracer : tracers) { + aggregator.register(tracer); + } + return aggregator; + } + + @Override + public void tracePreExecution(MessageFrame frame) { + for (OperationTracer tracer : tracers) { + tracer.tracePreExecution(frame); + } + } + + @Override + public void tracePostExecution(MessageFrame frame, Operation.OperationResult operationResult) { + for (OperationTracer tracer : tracers) { + tracer.tracePostExecution(frame, operationResult); + } + } + + @Override + public void tracePrecompileCall(MessageFrame frame, long gasRequirement, Bytes output) { + for (OperationTracer tracer : tracers) { + tracer.tracePrecompileCall(frame, gasRequirement, output); + } + } + + @Override + public void traceAccountCreationResult( + MessageFrame frame, Optional haltReason) { + for (OperationTracer tracer : tracers) { + tracer.traceAccountCreationResult(frame, haltReason); + } + } + + @Override + public void tracePrepareTransaction(WorldView worldView, Transaction transaction) { + for (OperationTracer tracer : tracers) { + tracer.tracePrepareTransaction(worldView, transaction); + } + } + + @Override + public void traceStartTransaction(WorldView worldView, Transaction transaction) { + for (OperationTracer tracer : tracers) { + tracer.traceStartTransaction(worldView, transaction); + } + } + + @Override + public void traceEndTransaction( + WorldView worldView, + Transaction tx, + boolean status, + Bytes output, + List logs, + long gasUsed, + long timeNs) { + for (OperationTracer tracer : tracers) { + tracer.traceEndTransaction(worldView, tx, status, output, logs, gasUsed, timeNs); + } + } + + @Override + public void traceContextEnter(MessageFrame frame) { + for (OperationTracer tracer : tracers) { + tracer.traceContextEnter(frame); + } + } + + @Override + public void traceContextReEnter(MessageFrame frame) { + for (OperationTracer tracer : tracers) { + tracer.traceContextReEnter(frame); + } + } + + @Override + public void traceContextExit(MessageFrame frame) { + for (OperationTracer tracer : tracers) { + tracer.traceContextExit(frame); + } + } + + @Override + public boolean isExtendedTracing() { + for (OperationTracer tracer : tracers) { + if (tracer.isExtendedTracing()) { + return true; + } + } + return false; + } +} 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 465d3e5a..cab06d47 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 @@ -19,6 +19,7 @@ import net.consensys.linea.config.LineaL1L2BridgeConfiguration; import net.consensys.linea.config.LineaProfitabilityConfiguration; +import net.consensys.linea.config.LineaTracerConfiguration; import net.consensys.linea.config.LineaTransactionSelectorConfiguration; import net.consensys.linea.sequencer.txselection.selectors.LineaTransactionSelector; import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector; @@ -29,22 +30,30 @@ public class LineaTransactionSelectorFactory implements PluginTransactionSelecto private final LineaTransactionSelectorConfiguration txSelectorConfiguration; private final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration; private final LineaProfitabilityConfiguration profitabilityConfiguration; + private final LineaTracerConfiguration tracerConfiguration; + private final Map limitsMap; public LineaTransactionSelectorFactory( final LineaTransactionSelectorConfiguration txSelectorConfiguration, final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration, final LineaProfitabilityConfiguration profitabilityConfiguration, + final LineaTracerConfiguration tracerConfiguration, final Map limitsMap) { this.txSelectorConfiguration = txSelectorConfiguration; this.l1L2BridgeConfiguration = l1L2BridgeConfiguration; this.profitabilityConfiguration = profitabilityConfiguration; + this.tracerConfiguration = tracerConfiguration; this.limitsMap = limitsMap; } @Override public PluginTransactionSelector create() { return new LineaTransactionSelector( - txSelectorConfiguration, l1L2BridgeConfiguration, profitabilityConfiguration, limitsMap); + txSelectorConfiguration, + l1L2BridgeConfiguration, + profitabilityConfiguration, + tracerConfiguration, + 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 4a450419..bdb85f82 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 @@ -15,23 +15,13 @@ package net.consensys.linea.sequencer.txselection; -import java.io.File; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.Map; +import static net.consensys.linea.sequencer.LimitModuleHelper.createLimitModules; + import java.util.Optional; -import java.util.stream.Collectors; 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.LineaL1L2BridgeConfiguration; -import net.consensys.linea.config.LineaProfitabilityConfiguration; -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.TransactionSelectionService; @@ -66,43 +56,12 @@ public void doRegister(final BesuContext context) { @Override public void beforeExternalServices() { super.beforeExternalServices(); - try { - 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"); - final Map limitsMap = - table.toMap().entrySet().stream() - .collect( - Collectors.toUnmodifiableMap( - Map.Entry::getKey, e -> Math.toIntExact((Long) e.getValue()))); - - createAndRegister( - transactionSelectionService, - transactionSelectorConfiguration, - l1L2BridgeConfiguration, - profitabilityConfiguration, - limitsMap); - } catch (final Exception e) { - final String errorMsg = - "Problem reading the toml file containing the limits for the modules: " - + transactionSelectorConfiguration.moduleLimitsFilePath(); - log.error(errorMsg); - throw new RuntimeException(errorMsg, e); - } - } - - private void createAndRegister( - final TransactionSelectionService transactionSelectionService, - final LineaTransactionSelectorConfiguration txSelectorConfiguration, - final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration, - final LineaProfitabilityConfiguration profitabilityConfiguration, - final Map limitsMap) { transactionSelectionService.registerPluginTransactionSelectorFactory( new LineaTransactionSelectorFactory( - txSelectorConfiguration, + transactionSelectorConfiguration, l1L2BridgeConfiguration, profitabilityConfiguration, - limitsMap)); + tracerConfiguration, + createLimitModules(tracerConfiguration))); } } 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 32a7c012..fadb5d78 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 @@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j; import net.consensys.linea.config.LineaL1L2BridgeConfiguration; import net.consensys.linea.config.LineaProfitabilityConfiguration; +import net.consensys.linea.config.LineaTracerConfiguration; import net.consensys.linea.config.LineaTransactionSelectorConfiguration; import org.hyperledger.besu.datatypes.PendingTransaction; import org.hyperledger.besu.plugin.data.TransactionProcessingResult; @@ -33,18 +34,20 @@ public class LineaTransactionSelector implements PluginTransactionSelector { private TraceLineLimitTransactionSelector traceLineLimitTransactionSelector; - private List selectors; + private final List selectors; public LineaTransactionSelector( final LineaTransactionSelectorConfiguration txSelectorConfiguration, final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration, final LineaProfitabilityConfiguration profitabilityConfiguration, + final LineaTracerConfiguration tracerConfiguration, final Map limitsMap) { this.selectors = createTransactionSelectors( txSelectorConfiguration, l1L2BridgeConfiguration, profitabilityConfiguration, + tracerConfiguration, limitsMap); } @@ -60,11 +63,12 @@ private List createTransactionSelectors( final LineaTransactionSelectorConfiguration txSelectorConfiguration, final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration, final LineaProfitabilityConfiguration profitabilityConfiguration, + final LineaTracerConfiguration tracerConfiguration, final Map limitsMap) { traceLineLimitTransactionSelector = new TraceLineLimitTransactionSelector( - limitsMap, txSelectorConfiguration, l1L2BridgeConfiguration); + limitsMap, txSelectorConfiguration, l1L2BridgeConfiguration, tracerConfiguration); return List.of( new MaxBlockCallDataTransactionSelector(txSelectorConfiguration.maxBlockCallDataSize()), diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ModuleLineCountAccumulator.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ModuleLineCountAccumulator.java new file mode 100644 index 00000000..405bea01 --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ModuleLineCountAccumulator.java @@ -0,0 +1,123 @@ +/* + * 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.sequencer.txselection.selectors; + +import java.util.HashMap; +import java.util.Map; + +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +/** + * Accumulates and verifies line counts for modules based on provided limits. It supports verifying + * if current transactions exceed these limits and updates the accumulated counts. + */ +@Slf4j +public class ModuleLineCountAccumulator { + private final Map moduleLineCountLimits; + + @Getter private final Map accumulatedLineCountsPerModule = new HashMap<>(); + + /** + * Constructs a new accumulator with specified module line count limits. + * + * @param moduleLineCountLimits A map of module names to their respective line count limits. + */ + public ModuleLineCountAccumulator(Map moduleLineCountLimits) { + this.moduleLineCountLimits = new HashMap<>(moduleLineCountLimits); + } + + /** + * Verifies if the current accumulated line counts for modules exceed the predefined limits. + * + * @param currentAccumulatedLineCounts A map of module names to their current accumulated line + * counts. + * @return A {@link VerificationResult} indicating the outcome of the verification. + */ + public VerificationResult verify(Map currentAccumulatedLineCounts) { + for (Map.Entry moduleEntry : currentAccumulatedLineCounts.entrySet()) { + String moduleName = moduleEntry.getKey(); + Integer currentTotalLineCountForModule = moduleEntry.getValue(); + Integer lineCountLimitForModule = moduleLineCountLimits.get(moduleName); + + if (lineCountLimitForModule == null) { + log.error("Module '{}' is not defined in the line count limits.", moduleName); + return VerificationResult.moduleNotDefined(moduleName); + } + + int previouslyAccumulatedLineCount = + accumulatedLineCountsPerModule.getOrDefault(moduleName, 0); + int lineCountAddedByCurrentTx = + currentTotalLineCountForModule - previouslyAccumulatedLineCount; + + if (lineCountAddedByCurrentTx > lineCountLimitForModule) { + return VerificationResult.txModuleLineCountOverflow(moduleName); + } + + if (currentTotalLineCountForModule > lineCountLimitForModule) { + return VerificationResult.blockModuleLineCountFull(moduleName); + } + } + return VerificationResult.ok(); + } + + /** + * Updates the internal map of accumulated line counts per module. + * + * @param newAccumulatedLineCounts A map of module names to their new accumulated line counts. + */ + public void updateAccumulatedLineCounts(Map newAccumulatedLineCounts) { + accumulatedLineCountsPerModule.clear(); + accumulatedLineCountsPerModule.putAll(newAccumulatedLineCounts); + } + + /** Represents the result of verifying module line counts against their limits. */ + @Getter + public static class VerificationResult { + // Getters for verificationOutcome and affectedModuleName + private final ModuleLineCountResult result; + private final String moduleName; + + private VerificationResult(ModuleLineCountResult result, String moduleName) { + this.result = result; + this.moduleName = moduleName; + } + + public static VerificationResult ok() { + return new VerificationResult(ModuleLineCountResult.VALID, null); + } + + public static VerificationResult moduleNotDefined(String moduleName) { + return new VerificationResult(ModuleLineCountResult.MODULE_NOT_DEFINED, moduleName); + } + + public static VerificationResult txModuleLineCountOverflow(String moduleName) { + return new VerificationResult( + ModuleLineCountResult.TX_MODULE_LINE_COUNT_OVERFLOW, moduleName); + } + + public static VerificationResult blockModuleLineCountFull(String moduleName) { + return new VerificationResult(ModuleLineCountResult.BLOCK_MODULE_LINE_COUNT_FULL, moduleName); + } + } + + /** Enumerates possible outcomes of verifying module line counts against their limits. */ + public enum ModuleLineCountResult { + VALID, + TX_MODULE_LINE_COUNT_OVERFLOW, + BLOCK_MODULE_LINE_COUNT_FULL, + MODULE_NOT_DEFINED + } +} 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 5f021dd6..154a9e85 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 @@ -17,6 +17,7 @@ import static net.consensys.linea.sequencer.txselection.LineaTransactionSelectionResult.BLOCK_MODULE_LINE_COUNT_FULL; import static net.consensys.linea.sequencer.txselection.LineaTransactionSelectionResult.TX_MODULE_LINE_COUNT_OVERFLOW; import static net.consensys.linea.sequencer.txselection.LineaTransactionSelectionResult.TX_MODULE_LINE_COUNT_OVERFLOW_CACHED; +import static net.consensys.linea.sequencer.txselection.selectors.ModuleLineCountAccumulator.ModuleLineCountResult.MODULE_NOT_DEFINED; import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.SELECTED; import java.util.LinkedHashSet; @@ -27,6 +28,7 @@ import com.google.common.annotations.VisibleForTesting; import lombok.extern.slf4j.Slf4j; import net.consensys.linea.config.LineaL1L2BridgeConfiguration; +import net.consensys.linea.config.LineaTracerConfiguration; import net.consensys.linea.config.LineaTransactionSelectorConfiguration; import net.consensys.linea.zktracer.ZkTracer; import net.consensys.linea.zktracer.module.Module; @@ -56,20 +58,21 @@ public class TraceLineLimitTransactionSelector implements PluginTransactionSelec private final String limitFilePath; private final Map moduleLimits; private final int overLimitCacheSize; - private Map consolidatedCumulatedLineCount = Map.of(); + private final ModuleLineCountAccumulator moduleLineCountAccumulator; private Map currCumulatedLineCount; public TraceLineLimitTransactionSelector( final Map moduleLimits, final LineaTransactionSelectorConfiguration txSelectorConfiguration, - final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration) { + final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration, + final LineaTracerConfiguration tracerConfiguration) { if (l1L2BridgeConfiguration.isEmpty()) { log.error("L1L2 bridge settings have not been defined."); System.exit(1); } this.moduleLimits = moduleLimits; - this.limitFilePath = txSelectorConfiguration.moduleLimitsFilePath(); + this.limitFilePath = tracerConfiguration.moduleLimitsFilePath(); this.overLimitCacheSize = txSelectorConfiguration.overLinesLimitCacheSize(); zkTracer = new ZkTracerWithLog(l1L2BridgeConfiguration); @@ -80,6 +83,7 @@ public TraceLineLimitTransactionSelector( } } zkTracer.traceStartConflation(1L); + moduleLineCountAccumulator = new ModuleLineCountAccumulator(moduleLimits); } /** @@ -114,7 +118,7 @@ public void onTransactionNotSelected( public void onTransactionSelected( final TransactionEvaluationContext evaluationContext, final TransactionProcessingResult processingResult) { - consolidatedCumulatedLineCount = currCumulatedLineCount; + moduleLineCountAccumulator.updateAccumulatedLineCounts(currCumulatedLineCount); } /** @@ -133,49 +137,41 @@ public TransactionSelectionResult evaluateTransactionPostProcessing( // check that we are not exceeding line number for any module currCumulatedLineCount = zkTracer.getModulesLineCount(); - final Transaction transaction = evaluationContext.getPendingTransaction().getTransaction(); - log.atTrace() .setMessage("Tx {} line count per module: {}") .addArgument(transaction::getHash) .addArgument(this::logTxLineCount) .log(); - for (final var module : currCumulatedLineCount.keySet()) { - final Integer moduleLineCountLimit = moduleLimits.get(module); - if (moduleLineCountLimit == null) { - final String errorMsg = - "Module " + module + " does not exist in the limits file: " + limitFilePath; - log.error(errorMsg); - throw new RuntimeException(errorMsg); - } - - final int cumulatedModuleLineCount = currCumulatedLineCount.get(module); - final int txModuleLineCount = - cumulatedModuleLineCount - consolidatedCumulatedLineCount.getOrDefault(module, 0); - - if (txModuleLineCount > moduleLineCountLimit) { - log.warn( - "Tx {} line count for module {}={} is above the limit {}, removing from the txpool", - transaction.getHash(), - module, - txModuleLineCount, - moduleLineCountLimit); + ModuleLineCountAccumulator.VerificationResult result = + moduleLineCountAccumulator.verify(currCumulatedLineCount); + + switch (result.getResult()) { + case MODULE_NOT_DEFINED: + log.error("Module {} does not exist in the limits file.", result.getModuleName()); + throw new RuntimeException( + "Module " + result.getModuleName() + " does not exist in the limits file."); + case TX_MODULE_LINE_COUNT_OVERFLOW: + /* log.warn( + "Tx {} line count for module {}={} is above the limit {}, removing from the txpool", + transaction.getHash(), + result.getModuleName(), + txModuleLineCount, + moduleLineCountLimit);*/ rememberOverLineCountLimitTransaction(transaction); return TX_MODULE_LINE_COUNT_OVERFLOW; - } - - if (cumulatedModuleLineCount > moduleLineCountLimit) { - log.atTrace() - .setMessage( - "Cumulated line count for module {}={} is above the limit {}, stopping selection") - .addArgument(module) - .addArgument(cumulatedModuleLineCount) - .addArgument(moduleLineCountLimit) - .log(); + case BLOCK_MODULE_LINE_COUNT_FULL: + /* log.atTrace() + .setMessage( + "Cumulated line count for module {}={} is above the limit {}, stopping selection") + .addArgument(result.getModuleName()) + .addArgument(cumulatedModuleLineCount) + .addArgument(moduleLineCountLimit) + .log();*/ return BLOCK_MODULE_LINE_COUNT_FULL; - } + default: + break; } return SELECTED; } @@ -207,7 +203,10 @@ private String logTxLineCount() { // tx line count / cumulated line count / line count limit e.getKey() + "=" - + (e.getValue() - consolidatedCumulatedLineCount.getOrDefault(e.getKey(), 0)) + + (e.getValue() + - moduleLineCountAccumulator + .getAccumulatedLineCountsPerModule() + .getOrDefault(e.getKey(), 0)) + "/" + e.getValue() + "/" @@ -230,7 +229,7 @@ public void traceEndBlock(final BlockHeader blockHeader, final BlockBody blockBo .addKeyValue( "traceCounts", () -> - consolidatedCumulatedLineCount.entrySet().stream() + moduleLineCountAccumulator.getAccumulatedLineCountsPerModule().entrySet().stream() .sorted(Map.Entry.comparingByKey()) .map(e -> '"' + e.getKey() + "\":" + e.getValue()) .collect(Collectors.joining(","))) 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 2cd590da..ea7f3714 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 @@ -27,6 +27,7 @@ import java.util.stream.Collectors; import net.consensys.linea.config.LineaL1L2BridgeConfiguration; +import net.consensys.linea.config.LineaTracerConfiguration; import net.consensys.linea.config.LineaTransactionSelectorConfiguration; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; @@ -227,13 +228,13 @@ private class TestableTraceLineLimitTransactionSelector super( moduleLimits, LineaTransactionSelectorConfiguration.builder() - .moduleLimitsFilePath(limitFilePath) .overLinesLimitCacheSize(overLimitCacheSize) .build(), LineaL1L2BridgeConfiguration.builder() .contract(Address.fromHexString("0xDEADBEEF")) .topic(Bytes.fromHexString("0x012345")) - .build()); + .build(), + LineaTracerConfiguration.builder().moduleLimitsFilePath(limitFilePath).build()); } void reset() { From dda9a76828223643aa1683a668135b7399257244 Mon Sep 17 00:00:00 2001 From: Gabriel-Trintinalia Date: Mon, 8 Apr 2024 17:51:22 -0300 Subject: [PATCH 02/12] feature: Add module limit verification to linea_estimateGas Signed-off-by: Gabriel-Trintinalia --- .../AbstractLineaSharedOptionsPlugin.java | 2 +- .../rpc/linea/LineaEndpointServicePlugin.java | 3 ++- .../linea/rpc/linea/LineaEstimateGas.java | 22 ++++++++++++++++--- .../zktracer/module/rlp/txn/RlpTxnChunk.java | 2 +- 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java b/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java index 6625e149..5046c8c9 100644 --- a/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java @@ -53,7 +53,7 @@ public abstract class AbstractLineaSharedOptionsPlugin implements BesuPlugin { static { // force the initialization of the gnark compress native library to fail fast in case of issues - // LibCompress.CompressedSize(new byte[0], 0); + LibCompress.CompressedSize(new byte[0], 0); } @Override 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 index e582bd06..a93193e4 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java @@ -92,6 +92,7 @@ public void beforeExternalServices() { rpcConfiguration, transactionPoolValidatorConfiguration, profitabilityConfiguration, - createLimitModules(tracerConfiguration)); + createLimitModules(tracerConfiguration), + l1L2BridgeConfiguration); } } 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 index 60129659..1f05651d 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java @@ -29,6 +29,7 @@ import com.google.common.annotations.VisibleForTesting; import lombok.extern.slf4j.Slf4j; import net.consensys.linea.bl.TransactionProfitabilityCalculator; +import net.consensys.linea.config.LineaL1L2BridgeConfiguration; import net.consensys.linea.config.LineaProfitabilityConfiguration; import net.consensys.linea.config.LineaRpcConfiguration; import net.consensys.linea.config.LineaTransactionPoolValidatorConfiguration; @@ -84,6 +85,7 @@ public class LineaEstimateGas { private LineaProfitabilityConfiguration profitabilityConf; private TransactionProfitabilityCalculator txProfitabilityCalculator; private Map limitsMap; + private LineaL1L2BridgeConfiguration l1L2BridgeConfiguration; public LineaEstimateGas( final BesuConfiguration besuConfiguration, @@ -98,12 +100,14 @@ public void init( LineaRpcConfiguration rpcConfiguration, final LineaTransactionPoolValidatorConfiguration transactionValidatorConfiguration, final LineaProfitabilityConfiguration profitabilityConf, - final Map limitsMap) { + final Map limitsMap, + final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration) { this.rpcConfiguration = rpcConfiguration; this.txValidatorConf = transactionValidatorConfiguration; this.profitabilityConf = profitabilityConf; this.txProfitabilityCalculator = new TransactionProfitabilityCalculator(profitabilityConf); this.limitsMap = limitsMap; + this.l1L2BridgeConfiguration = l1L2BridgeConfiguration; } public String getNamespace() { @@ -197,8 +201,13 @@ private Long estimateGasUsed( final Transaction transaction, final Wei minGasPrice) { - var estimateGasOperationTracer = new EstimateGasOperationTracer(); - var zkTracer = new ZkTracer(); + if (l1L2BridgeConfiguration.isEmpty()) { + log.error("L1L2 bridge settings have not been defined."); + System.exit(1); + } + + final var estimateGasOperationTracer = new EstimateGasOperationTracer(); + final var zkTracer = createZkTracer(); TracerAggregator tracerAggregator = TracerAggregator.create(estimateGasOperationTracer, zkTracer); @@ -407,6 +416,13 @@ private Transaction createTransactionForSimulation( return txBuilder.build(); } + private ZkTracer createZkTracer() { + var zkTracer = new ZkTracer(l1L2BridgeConfiguration); + zkTracer.traceStartConflation(1L); + zkTracer.traceStartBlock(blockchainService.getChainHeadHeader()); + return zkTracer; + } + private void handleModuleOverLimit( ModuleLineCountAccumulator.VerificationResult moduleLimitResult) { // Throw specific exceptions based on the type of limit exceeded diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/rlp/txn/RlpTxnChunk.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/rlp/txn/RlpTxnChunk.java index 5c22cae4..668f99db 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/rlp/txn/RlpTxnChunk.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/rlp/txn/RlpTxnChunk.java @@ -124,7 +124,7 @@ protected int computeLineCount() { // Phase 10: AccessList if (txType == 1 || txType == 2) { - if (this.tx.getAccessList().orElseThrow().isEmpty()) { + if (this.tx.getAccessList().isEmpty() || this.tx.getAccessList().get().isEmpty()) { rowSize += 1; } else { // Rlp prefix of the AccessList list From e9bcc700d1906ed02b8dd8c817decf1fad79585a Mon Sep 17 00:00:00 2001 From: Gabriel-Trintinalia Date: Mon, 8 Apr 2024 18:02:50 -0300 Subject: [PATCH 03/12] feature: Add module limit verification to linea_estimateGas Signed-off-by: Gabriel-Trintinalia --- .../rpc/linea/LineaEndpointServicePlugin.java | 2 +- .../linea/rpc/linea/LineaEstimateGas.java | 27 +++++----- .../{ => modulelimit}/LimitModuleHelper.java | 2 +- .../ModuleLimitsValidationResult.java | 51 +++++++++++++++++++ .../ModuleLineCountValidator.java} | 48 ++++------------- .../LineaTransactionSelectorPlugin.java | 2 +- .../TraceLineLimitTransactionSelector.java | 11 ++-- 7 files changed, 82 insertions(+), 61 deletions(-) rename arithmetization/src/main/java/net/consensys/linea/sequencer/{ => modulelimit}/LimitModuleHelper.java (97%) create mode 100644 arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLimitsValidationResult.java rename arithmetization/src/main/java/net/consensys/linea/sequencer/{txselection/selectors/ModuleLineCountAccumulator.java => modulelimit/ModuleLineCountValidator.java} (63%) 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 index a93193e4..f5cc8adb 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java @@ -15,7 +15,7 @@ package net.consensys.linea.rpc.linea; -import static net.consensys.linea.sequencer.LimitModuleHelper.createLimitModules; +import static net.consensys.linea.sequencer.modulelimit.LimitModuleHelper.createLimitModules; import com.google.auto.service.AutoService; import lombok.extern.slf4j.Slf4j; 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 index 1f05651d..d6cad53e 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java @@ -15,8 +15,8 @@ package net.consensys.linea.rpc.linea; -import static net.consensys.linea.sequencer.txselection.selectors.ModuleLineCountAccumulator.ModuleLineCountResult.MODULE_NOT_DEFINED; -import static net.consensys.linea.sequencer.txselection.selectors.ModuleLineCountAccumulator.ModuleLineCountResult.TX_MODULE_LINE_COUNT_OVERFLOW; +import static net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator.ModuleLineCountResult.MODULE_NOT_DEFINED; +import static net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator.ModuleLineCountResult.TX_MODULE_LINE_COUNT_OVERFLOW; import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity.create; import java.math.BigDecimal; @@ -34,7 +34,8 @@ import net.consensys.linea.config.LineaRpcConfiguration; import net.consensys.linea.config.LineaTransactionPoolValidatorConfiguration; import net.consensys.linea.sequencer.TracerAggregator; -import net.consensys.linea.sequencer.txselection.selectors.ModuleLineCountAccumulator; +import net.consensys.linea.sequencer.modulelimit.ModuleLimitsValidationResult; +import net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator; import net.consensys.linea.zktracer.ZkTracer; import org.apache.tuweni.bytes.Bytes; import org.bouncycastle.asn1.sec.SECNamedCurves; @@ -84,9 +85,10 @@ public class LineaEstimateGas { private LineaTransactionPoolValidatorConfiguration txValidatorConf; private LineaProfitabilityConfiguration profitabilityConf; private TransactionProfitabilityCalculator txProfitabilityCalculator; - private Map limitsMap; private LineaL1L2BridgeConfiguration l1L2BridgeConfiguration; + private ModuleLineCountValidator moduleLineCountValidator; + public LineaEstimateGas( final BesuConfiguration besuConfiguration, final TransactionSimulationService transactionSimulationService, @@ -106,8 +108,8 @@ public void init( this.txValidatorConf = transactionValidatorConfiguration; this.profitabilityConf = profitabilityConf; this.txProfitabilityCalculator = new TransactionProfitabilityCalculator(profitabilityConf); - this.limitsMap = limitsMap; this.l1L2BridgeConfiguration = l1L2BridgeConfiguration; + this.moduleLineCountValidator = new ModuleLineCountValidator(limitsMap); } public String getNamespace() { @@ -202,8 +204,8 @@ private Long estimateGasUsed( final Wei minGasPrice) { if (l1L2BridgeConfiguration.isEmpty()) { - log.error("L1L2 bridge settings have not been defined."); - System.exit(1); + throw new PluginRpcEndpointException( + RpcErrorType.PLUGIN_INTERNAL_ERROR, "L1L2 bridge settings have not been defined"); } final var estimateGasOperationTracer = new EstimateGasOperationTracer(); @@ -215,12 +217,10 @@ private Long estimateGasUsed( final var maybeSimulationResults = transactionSimulationService.simulate(transaction, chainHeadHash, tracerAggregator, true); - ModuleLineCountAccumulator accumulator = new ModuleLineCountAccumulator(limitsMap); - - ModuleLineCountAccumulator.VerificationResult moduleLimit = - accumulator.verify(zkTracer.getModulesLineCount()); + ModuleLimitsValidationResult moduleLimit = + moduleLineCountValidator.validate(zkTracer.getModulesLineCount()); - if (moduleLimit.getResult() != ModuleLineCountAccumulator.ModuleLineCountResult.VALID) { + if (moduleLimit.getResult() != ModuleLineCountValidator.ModuleLineCountResult.VALID) { handleModuleOverLimit(moduleLimit); } @@ -423,8 +423,7 @@ private ZkTracer createZkTracer() { return zkTracer; } - private void handleModuleOverLimit( - ModuleLineCountAccumulator.VerificationResult moduleLimitResult) { + private void handleModuleOverLimit(ModuleLimitsValidationResult moduleLimitResult) { // Throw specific exceptions based on the type of limit exceeded if (moduleLimitResult.getResult() == MODULE_NOT_DEFINED) { String moduleNotDefinedMsg = diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/LimitModuleHelper.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/LimitModuleHelper.java similarity index 97% rename from arithmetization/src/main/java/net/consensys/linea/sequencer/LimitModuleHelper.java rename to arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/LimitModuleHelper.java index 1dcd4556..385db6b1 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/LimitModuleHelper.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/LimitModuleHelper.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package net.consensys.linea.sequencer; +package net.consensys.linea.sequencer.modulelimit; import java.io.File; import java.net.URL; diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLimitsValidationResult.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLimitsValidationResult.java new file mode 100644 index 00000000..1439eb00 --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLimitsValidationResult.java @@ -0,0 +1,51 @@ +/* + * 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.sequencer.modulelimit; + +import lombok.Getter; + +/** Represents the result of verifying module line counts against their limits. */ +@Getter +public class ModuleLimitsValidationResult { + // Getters for verificationOutcome and affectedModuleName + private final ModuleLineCountValidator.ModuleLineCountResult result; + private final String moduleName; + + private ModuleLimitsValidationResult( + ModuleLineCountValidator.ModuleLineCountResult result, String moduleName) { + this.result = result; + this.moduleName = moduleName; + } + + public static ModuleLimitsValidationResult ok() { + return new ModuleLimitsValidationResult( + ModuleLineCountValidator.ModuleLineCountResult.VALID, null); + } + + public static ModuleLimitsValidationResult moduleNotDefined(String moduleName) { + return new ModuleLimitsValidationResult( + ModuleLineCountValidator.ModuleLineCountResult.MODULE_NOT_DEFINED, moduleName); + } + + public static ModuleLimitsValidationResult txModuleLineCountOverflow(String moduleName) { + return new ModuleLimitsValidationResult( + ModuleLineCountValidator.ModuleLineCountResult.TX_MODULE_LINE_COUNT_OVERFLOW, moduleName); + } + + public static ModuleLimitsValidationResult blockModuleLineCountFull(String moduleName) { + return new ModuleLimitsValidationResult( + ModuleLineCountValidator.ModuleLineCountResult.BLOCK_MODULE_LINE_COUNT_FULL, moduleName); + } +} diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ModuleLineCountAccumulator.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLineCountValidator.java similarity index 63% rename from arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ModuleLineCountAccumulator.java rename to arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLineCountValidator.java index 405bea01..e6055e83 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ModuleLineCountAccumulator.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLineCountValidator.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package net.consensys.linea.sequencer.txselection.selectors; +package net.consensys.linea.sequencer.modulelimit; import java.util.HashMap; import java.util.Map; @@ -25,7 +25,7 @@ * if current transactions exceed these limits and updates the accumulated counts. */ @Slf4j -public class ModuleLineCountAccumulator { +public class ModuleLineCountValidator { private final Map moduleLineCountLimits; @Getter private final Map accumulatedLineCountsPerModule = new HashMap<>(); @@ -35,7 +35,7 @@ public class ModuleLineCountAccumulator { * * @param moduleLineCountLimits A map of module names to their respective line count limits. */ - public ModuleLineCountAccumulator(Map moduleLineCountLimits) { + public ModuleLineCountValidator(Map moduleLineCountLimits) { this.moduleLineCountLimits = new HashMap<>(moduleLineCountLimits); } @@ -44,9 +44,9 @@ public ModuleLineCountAccumulator(Map moduleLineCountLimits) { * * @param currentAccumulatedLineCounts A map of module names to their current accumulated line * counts. - * @return A {@link VerificationResult} indicating the outcome of the verification. + * @return A {@link ModuleLimitsValidationResult} indicating the outcome of the verification. */ - public VerificationResult verify(Map currentAccumulatedLineCounts) { + public ModuleLimitsValidationResult validate(Map currentAccumulatedLineCounts) { for (Map.Entry moduleEntry : currentAccumulatedLineCounts.entrySet()) { String moduleName = moduleEntry.getKey(); Integer currentTotalLineCountForModule = moduleEntry.getValue(); @@ -54,7 +54,7 @@ public VerificationResult verify(Map currentAccumulatedLineCoun if (lineCountLimitForModule == null) { log.error("Module '{}' is not defined in the line count limits.", moduleName); - return VerificationResult.moduleNotDefined(moduleName); + return ModuleLimitsValidationResult.moduleNotDefined(moduleName); } int previouslyAccumulatedLineCount = @@ -63,14 +63,14 @@ public VerificationResult verify(Map currentAccumulatedLineCoun currentTotalLineCountForModule - previouslyAccumulatedLineCount; if (lineCountAddedByCurrentTx > lineCountLimitForModule) { - return VerificationResult.txModuleLineCountOverflow(moduleName); + return ModuleLimitsValidationResult.txModuleLineCountOverflow(moduleName); } if (currentTotalLineCountForModule > lineCountLimitForModule) { - return VerificationResult.blockModuleLineCountFull(moduleName); + return ModuleLimitsValidationResult.blockModuleLineCountFull(moduleName); } } - return VerificationResult.ok(); + return ModuleLimitsValidationResult.ok(); } /** @@ -83,36 +83,6 @@ public void updateAccumulatedLineCounts(Map newAccumulatedLineC accumulatedLineCountsPerModule.putAll(newAccumulatedLineCounts); } - /** Represents the result of verifying module line counts against their limits. */ - @Getter - public static class VerificationResult { - // Getters for verificationOutcome and affectedModuleName - private final ModuleLineCountResult result; - private final String moduleName; - - private VerificationResult(ModuleLineCountResult result, String moduleName) { - this.result = result; - this.moduleName = moduleName; - } - - public static VerificationResult ok() { - return new VerificationResult(ModuleLineCountResult.VALID, null); - } - - public static VerificationResult moduleNotDefined(String moduleName) { - return new VerificationResult(ModuleLineCountResult.MODULE_NOT_DEFINED, moduleName); - } - - public static VerificationResult txModuleLineCountOverflow(String moduleName) { - return new VerificationResult( - ModuleLineCountResult.TX_MODULE_LINE_COUNT_OVERFLOW, moduleName); - } - - public static VerificationResult blockModuleLineCountFull(String moduleName) { - return new VerificationResult(ModuleLineCountResult.BLOCK_MODULE_LINE_COUNT_FULL, moduleName); - } - } - /** Enumerates possible outcomes of verifying module line counts against their limits. */ public enum ModuleLineCountResult { VALID, 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 bdb85f82..65377ccf 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 @@ -15,7 +15,7 @@ package net.consensys.linea.sequencer.txselection; -import static net.consensys.linea.sequencer.LimitModuleHelper.createLimitModules; +import static net.consensys.linea.sequencer.modulelimit.LimitModuleHelper.createLimitModules; import java.util.Optional; 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 154a9e85..573e851b 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 @@ -17,7 +17,6 @@ import static net.consensys.linea.sequencer.txselection.LineaTransactionSelectionResult.BLOCK_MODULE_LINE_COUNT_FULL; import static net.consensys.linea.sequencer.txselection.LineaTransactionSelectionResult.TX_MODULE_LINE_COUNT_OVERFLOW; import static net.consensys.linea.sequencer.txselection.LineaTransactionSelectionResult.TX_MODULE_LINE_COUNT_OVERFLOW_CACHED; -import static net.consensys.linea.sequencer.txselection.selectors.ModuleLineCountAccumulator.ModuleLineCountResult.MODULE_NOT_DEFINED; import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.SELECTED; import java.util.LinkedHashSet; @@ -30,6 +29,8 @@ import net.consensys.linea.config.LineaL1L2BridgeConfiguration; import net.consensys.linea.config.LineaTracerConfiguration; import net.consensys.linea.config.LineaTransactionSelectorConfiguration; +import net.consensys.linea.sequencer.modulelimit.ModuleLimitsValidationResult; +import net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator; import net.consensys.linea.zktracer.ZkTracer; import net.consensys.linea.zktracer.module.Module; import org.hyperledger.besu.datatypes.Hash; @@ -58,7 +59,7 @@ public class TraceLineLimitTransactionSelector implements PluginTransactionSelec private final String limitFilePath; private final Map moduleLimits; private final int overLimitCacheSize; - private final ModuleLineCountAccumulator moduleLineCountAccumulator; + private final ModuleLineCountValidator moduleLineCountAccumulator; private Map currCumulatedLineCount; public TraceLineLimitTransactionSelector( @@ -83,7 +84,7 @@ public TraceLineLimitTransactionSelector( } } zkTracer.traceStartConflation(1L); - moduleLineCountAccumulator = new ModuleLineCountAccumulator(moduleLimits); + moduleLineCountAccumulator = new ModuleLineCountValidator(moduleLimits); } /** @@ -144,8 +145,8 @@ public TransactionSelectionResult evaluateTransactionPostProcessing( .addArgument(this::logTxLineCount) .log(); - ModuleLineCountAccumulator.VerificationResult result = - moduleLineCountAccumulator.verify(currCumulatedLineCount); + ModuleLimitsValidationResult result = + moduleLineCountAccumulator.validate(currCumulatedLineCount); switch (result.getResult()) { case MODULE_NOT_DEFINED: From 81e629e2cc2d1349af865406a0ffa57e49295a46 Mon Sep 17 00:00:00 2001 From: Gabriel-Trintinalia Date: Mon, 8 Apr 2024 18:06:09 -0300 Subject: [PATCH 04/12] feature: Add module limit verification to linea_estimateGas Signed-off-by: Gabriel-Trintinalia --- .../consensys/linea/rpc/linea/LineaEstimateGas.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) 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 index d6cad53e..79e61a74 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java @@ -287,7 +287,8 @@ private Long estimateGasUsed( // else do a binary search to find the right estimation int iterations = 0; - var high = highGasEstimation(lr.getGasEstimate(), tracerAggregator); + var high = + highGasEstimation(lr.getGasEstimate(), estimateGasOperationTracer); var mid = high; var low = lowGasEstimation; while (low + 1 < high) { @@ -383,14 +384,14 @@ private void validateParameters(final JsonCallParameter callParameters) { * @param operationTracer estimate gas operation tracer * @return estimate gas */ - private long highGasEstimation(final long gasEstimation, final TracerAggregator operationTracer) { - var estimateGasTracer = operationTracer.getTracer(EstimateGasOperationTracer.class); + 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, estimateGasTracer.getMaxDepth()); + Math.pow(SUB_CALL_REMAINING_GAS_RATIO, operationTracer.getMaxDepth()); // and minimum gas remaining is necessary for some operation (additionalStipend) - final long gasStipend = estimateGasTracer.getStipendNeeded(); + final long gasStipend = operationTracer.getStipendNeeded(); return ((long) ((gasEstimation + gasStipend) * subCallMultiplier)); } From 83396d44617a5f4ac59b77873e4f4bdd78a6ab9c Mon Sep 17 00:00:00 2001 From: Gabriel-Trintinalia Date: Mon, 8 Apr 2024 19:11:58 -0300 Subject: [PATCH 05/12] feature: Add module limit verification to linea_estimateGas Signed-off-by: Gabriel-Trintinalia --- .../rpc/linea/LineaEndpointServicePlugin.java | 2 +- .../linea/sequencer/TracerAggregator.java | 17 ------ .../modulelimit/LimitModuleHelper.java | 54 ------------------- .../ModuleLimitsValidationResult.java | 2 +- .../modulelimit/ModuleLineCountValidator.java | 34 +++++++++++- .../LineaTransactionSelectorPlugin.java | 2 +- 6 files changed, 36 insertions(+), 75 deletions(-) delete mode 100644 arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/LimitModuleHelper.java 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 index f5cc8adb..0aab37ab 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java @@ -15,7 +15,7 @@ package net.consensys.linea.rpc.linea; -import static net.consensys.linea.sequencer.modulelimit.LimitModuleHelper.createLimitModules; +import static net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator.createLimitModules; import com.google.auto.service.AutoService; import lombok.extern.slf4j.Slf4j; diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/TracerAggregator.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/TracerAggregator.java index 05410fa5..e9733312 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/TracerAggregator.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/TracerAggregator.java @@ -53,23 +53,6 @@ public void register(OperationTracer tracer) { tracers.add(tracer); } - /** - * Retrieves a registered tracer by its class type. - * - * @param clazz the class of the tracer to retrieve - * @param the type of the tracer - * @return the registered tracer of the specified type - * @throws IllegalArgumentException if no tracer of the specified class is registered - */ - public T getTracer(Class clazz) { - for (OperationTracer tracer : tracers) { - if (clazz.isInstance(tracer)) { - return clazz.cast(tracer); - } - } - throw new IllegalArgumentException("No tracer found for class: " + clazz.getName()); - } - /** * Creates a {@link TracerAggregator} instance and registers the provided tracers. * diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/LimitModuleHelper.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/LimitModuleHelper.java deleted file mode 100644 index 385db6b1..00000000 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/LimitModuleHelper.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.sequencer.modulelimit; - -import java.io.File; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.Map; -import java.util.stream.Collectors; - -import com.google.common.io.Resources; -import lombok.extern.slf4j.Slf4j; -import net.consensys.linea.config.LineaTracerConfiguration; -import org.apache.tuweni.toml.Toml; -import org.apache.tuweni.toml.TomlParseResult; -import org.apache.tuweni.toml.TomlTable; - -@Slf4j -public class LimitModuleHelper { - public static Map createLimitModules( - LineaTracerConfiguration lineaTracerConfiguration) { - try { - URL url = new File(lineaTracerConfiguration.moduleLimitsFilePath()).toURI().toURL(); - final String tomlString = Resources.toString(url, StandardCharsets.UTF_8); - TomlParseResult result = Toml.parse(tomlString); - final TomlTable table = result.getTable("traces-limits"); - final Map limitsMap = - table.toMap().entrySet().stream() - .collect( - Collectors.toUnmodifiableMap( - Map.Entry::getKey, e -> Math.toIntExact((Long) e.getValue()))); - - return limitsMap; - } catch (final Exception e) { - final String errorMsg = - "Problem reading the toml file containing the limits for the modules: " - + lineaTracerConfiguration.moduleLimitsFilePath(); - log.error(errorMsg); - throw new RuntimeException(errorMsg, e); - } - } -} diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLimitsValidationResult.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLimitsValidationResult.java index 1439eb00..e5ca09fb 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLimitsValidationResult.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLimitsValidationResult.java @@ -29,7 +29,7 @@ private ModuleLimitsValidationResult( this.moduleName = moduleName; } - public static ModuleLimitsValidationResult ok() { + public static ModuleLimitsValidationResult valid() { return new ModuleLimitsValidationResult( ModuleLineCountValidator.ModuleLineCountResult.VALID, null); } diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLineCountValidator.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLineCountValidator.java index e6055e83..0600f318 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLineCountValidator.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLineCountValidator.java @@ -14,11 +14,20 @@ */ package net.consensys.linea.sequencer.modulelimit; +import java.io.File; +import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; +import java.util.stream.Collectors; +import com.google.common.io.Resources; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import net.consensys.linea.config.LineaTracerConfiguration; +import org.apache.tuweni.toml.Toml; +import org.apache.tuweni.toml.TomlParseResult; +import org.apache.tuweni.toml.TomlTable; /** * Accumulates and verifies line counts for modules based on provided limits. It supports verifying @@ -70,7 +79,7 @@ public ModuleLimitsValidationResult validate(Map currentAccumul return ModuleLimitsValidationResult.blockModuleLineCountFull(moduleName); } } - return ModuleLimitsValidationResult.ok(); + return ModuleLimitsValidationResult.valid(); } /** @@ -90,4 +99,27 @@ public enum ModuleLineCountResult { BLOCK_MODULE_LINE_COUNT_FULL, MODULE_NOT_DEFINED } + + public static Map createLimitModules( + LineaTracerConfiguration lineaTracerConfiguration) { + try { + URL url = new File(lineaTracerConfiguration.moduleLimitsFilePath()).toURI().toURL(); + final String tomlString = Resources.toString(url, StandardCharsets.UTF_8); + TomlParseResult result = Toml.parse(tomlString); + final TomlTable table = result.getTable("traces-limits"); + final Map limitsMap = + table.toMap().entrySet().stream() + .collect( + Collectors.toUnmodifiableMap( + Map.Entry::getKey, e -> Math.toIntExact((Long) e.getValue()))); + + return limitsMap; + } catch (final Exception e) { + final String errorMsg = + "Problem reading the toml file containing the limits for the modules: " + + lineaTracerConfiguration.moduleLimitsFilePath(); + log.error(errorMsg); + throw new RuntimeException(errorMsg, e); + } + } } 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 65377ccf..edcfeb09 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 @@ -15,7 +15,7 @@ package net.consensys.linea.sequencer.txselection; -import static net.consensys.linea.sequencer.modulelimit.LimitModuleHelper.createLimitModules; +import static net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator.createLimitModules; import java.util.Optional; From 3e797297dd1aa5751fed42bc83d2de97888e1b69 Mon Sep 17 00:00:00 2001 From: Gabriel-Trintinalia Date: Tue, 9 Apr 2024 11:48:33 -0300 Subject: [PATCH 06/12] feature: PR suggestion Signed-off-by: Gabriel-Trintinalia --- .../linea/config/LineaTracerConfiguration.java | 2 +- .../config/LineaTracerConfigurationCLiOptions.java | 6 +++--- .../config/LineaTransactionSelectorCliOptions.java | 4 ---- .../consensys/linea/rpc/linea/LineaEstimateGas.java | 10 +++++----- .../modulelimit/ModuleLimitsValidationResult.java | 8 +++----- .../modulelimit/ModuleLineCountValidator.java | 2 +- 6 files changed, 13 insertions(+), 19 deletions(-) diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfiguration.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfiguration.java index bf2c3ced..a9b69033 100644 --- a/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfiguration.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfiguration.java @@ -17,6 +17,6 @@ import lombok.Builder; -/** The Linea transaction selectors configuration. */ +/** The Linea tracer configuration. */ @Builder(toBuilder = true) public record LineaTracerConfiguration(String moduleLimitsFilePath) {} diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfigurationCLiOptions.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfigurationCLiOptions.java index 74377c3b..5b6f6c04 100644 --- a/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfigurationCLiOptions.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfigurationCLiOptions.java @@ -14,14 +14,14 @@ */ package net.consensys.linea.config; -import static net.consensys.linea.config.LineaTransactionSelectorCliOptions.DEFAULT_MODULE_LIMIT_FILE_PATH; -import static net.consensys.linea.config.LineaTransactionSelectorCliOptions.MODULE_LIMIT_FILE_PATH; - import com.google.common.base.MoreObjects; import picocli.CommandLine; public class LineaTracerConfigurationCLiOptions { + public static final String MODULE_LIMIT_FILE_PATH = "--plugin-linea-module-limit-file-path"; + public static final String DEFAULT_MODULE_LIMIT_FILE_PATH = "moduleLimitFile.toml"; + @CommandLine.Option( names = {MODULE_LIMIT_FILE_PATH}, hidden = true, diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorCliOptions.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorCliOptions.java index 85856c95..acfe4ad1 100644 --- a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorCliOptions.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorCliOptions.java @@ -23,10 +23,6 @@ public class LineaTransactionSelectorCliOptions { public static final String MAX_BLOCK_CALLDATA_SIZE = "--plugin-linea-max-block-calldata-size"; public static final int DEFAULT_MAX_BLOCK_CALLDATA_SIZE = 70_000; - - public static final String MODULE_LIMIT_FILE_PATH = "--plugin-linea-module-limit-file-path"; - public static final String DEFAULT_MODULE_LIMIT_FILE_PATH = "moduleLimitFile.toml"; - public static final String OVER_LINE_COUNT_LIMIT_CACHE_SIZE = "--plugin-linea-over-line-count-limit-cache-size"; public static final int DEFAULT_OVER_LINE_COUNT_LIMIT_CACHE_SIZE = 10_000; 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 index 79e61a74..0fbb64df 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java @@ -110,6 +110,11 @@ public void init( this.txProfitabilityCalculator = new TransactionProfitabilityCalculator(profitabilityConf); this.l1L2BridgeConfiguration = l1L2BridgeConfiguration; this.moduleLineCountValidator = new ModuleLineCountValidator(limitsMap); + + if (l1L2BridgeConfiguration.isEmpty()) { + log.error("L1L2 bridge settings have not been defined."); + System.exit(1); + } } public String getNamespace() { @@ -203,11 +208,6 @@ private Long estimateGasUsed( final Transaction transaction, final Wei minGasPrice) { - if (l1L2BridgeConfiguration.isEmpty()) { - throw new PluginRpcEndpointException( - RpcErrorType.PLUGIN_INTERNAL_ERROR, "L1L2 bridge settings have not been defined"); - } - final var estimateGasOperationTracer = new EstimateGasOperationTracer(); final var zkTracer = createZkTracer(); TracerAggregator tracerAggregator = diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLimitsValidationResult.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLimitsValidationResult.java index e5ca09fb..67060827 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLimitsValidationResult.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLimitsValidationResult.java @@ -23,17 +23,15 @@ public class ModuleLimitsValidationResult { private final ModuleLineCountValidator.ModuleLineCountResult result; private final String moduleName; + public static ModuleLimitsValidationResult VALID = + new ModuleLimitsValidationResult(ModuleLineCountValidator.ModuleLineCountResult.VALID, null); + private ModuleLimitsValidationResult( ModuleLineCountValidator.ModuleLineCountResult result, String moduleName) { this.result = result; this.moduleName = moduleName; } - public static ModuleLimitsValidationResult valid() { - return new ModuleLimitsValidationResult( - ModuleLineCountValidator.ModuleLineCountResult.VALID, null); - } - public static ModuleLimitsValidationResult moduleNotDefined(String moduleName) { return new ModuleLimitsValidationResult( ModuleLineCountValidator.ModuleLineCountResult.MODULE_NOT_DEFINED, moduleName); diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLineCountValidator.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLineCountValidator.java index 0600f318..9dcb23e2 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLineCountValidator.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLineCountValidator.java @@ -79,7 +79,7 @@ public ModuleLimitsValidationResult validate(Map currentAccumul return ModuleLimitsValidationResult.blockModuleLineCountFull(moduleName); } } - return ModuleLimitsValidationResult.valid(); + return ModuleLimitsValidationResult.VALID; } /** From 855730701b8678f76595fb49f9f65ae8903d368f Mon Sep 17 00:00:00 2001 From: Gabriel-Trintinalia Date: Tue, 9 Apr 2024 13:04:24 -0300 Subject: [PATCH 07/12] feature: Add unit test Signed-off-by: Gabriel-Trintinalia --- .../EstimateGasModuleLimitOverflowTest.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasModuleLimitOverflowTest.java diff --git a/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasModuleLimitOverflowTest.java b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasModuleLimitOverflowTest.java new file mode 100644 index 00000000..8fbb4be8 --- /dev/null +++ b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasModuleLimitOverflowTest.java @@ -0,0 +1,63 @@ +/* + * 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.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.util.List; + +import linea.plugin.acc.test.LineaPluginTestBase; +import linea.plugin.acc.test.TestCommandLineOptionsBuilder; +import linea.plugin.acc.test.tests.web3j.generated.SimpleStorage; +import org.apache.tuweni.bytes.Bytes; +import org.hyperledger.besu.tests.acceptance.dsl.account.Account; +import org.junit.jupiter.api.Test; + +public class EstimateGasModuleLimitOverflowTest extends LineaPluginTestBase { + @Override + public List getTestCliOptions() { + return new TestCommandLineOptionsBuilder() + .set( + "--plugin-linea-module-limit-file-path=", + getResourcePath("/txOverflowModuleLimits.toml")) + .build(); + } + + @Test + public void estimateGasFailsForExceedingModuleLineCountTest() throws Exception { + + final Account sender = accounts.getSecondaryBenefactor(); + + final SimpleStorage simpleStorage = deploySimpleStorage(); + final String txData = simpleStorage.add(BigInteger.valueOf(100)).encodeFunctionCall(); + final var payload = Bytes.wrap(txData.getBytes(StandardCharsets.UTF_8)); + + final EstimateGasTest.CallParams callParams = + new EstimateGasTest.CallParams( + sender.getAddress(), + simpleStorage.getContractAddress(), + null, + payload.toHexString(), + "0"); + + final var reqLinea = new EstimateGasTest.BadLineaEstimateGasRequest(callParams); + final var respLinea = reqLinea.execute(minerNode.nodeRequests()); + assertThat(respLinea.getCode()).isEqualTo(-32000); + assertThat(respLinea.getMessage()) + .isEqualTo("Transaction line count for module HUB is above the limit"); + } +} From 0ec20f9d01ed158e8182f924c76c04b06348bd32 Mon Sep 17 00:00:00 2001 From: Gabriel-Trintinalia Date: Tue, 9 Apr 2024 13:34:30 -0300 Subject: [PATCH 08/12] feature: Fix logging Signed-off-by: Gabriel-Trintinalia --- .../EstimateGasModuleLimitOverflowTest.java | 2 +- .../linea/rpc/linea/LineaEstimateGas.java | 6 +- .../ModuleLimitsValidationResult.java | 57 ++++++++++++++++--- .../modulelimit/ModuleLineCountValidator.java | 14 ++++- .../TraceLineLimitTransactionSelector.java | 26 ++++----- 5 files changed, 78 insertions(+), 27 deletions(-) diff --git a/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasModuleLimitOverflowTest.java b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasModuleLimitOverflowTest.java index 8fbb4be8..c787e112 100644 --- a/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasModuleLimitOverflowTest.java +++ b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasModuleLimitOverflowTest.java @@ -58,6 +58,6 @@ public void estimateGasFailsForExceedingModuleLineCountTest() throws Exception { final var respLinea = reqLinea.execute(minerNode.nodeRequests()); assertThat(respLinea.getCode()).isEqualTo(-32000); assertThat(respLinea.getMessage()) - .isEqualTo("Transaction line count for module HUB is above the limit"); + .isEqualTo("Transaction line count for module HUB=66 is above the limit 30"); } } 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 index 0fbb64df..4e9b4707 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java @@ -436,8 +436,10 @@ private void handleModuleOverLimit(ModuleLimitsValidationResult moduleLimitResul if (moduleLimitResult.getResult() == TX_MODULE_LINE_COUNT_OVERFLOW) { String txOverflowMsg = String.format( - "Transaction line count for module %s is above the limit", - moduleLimitResult.getModuleName()); + "Transaction line count for module %s=%s is above the limit %s", + moduleLimitResult.getModuleName(), + moduleLimitResult.getModuleLineCount(), + moduleLimitResult.getModuleLineLimit()); log.warn(txOverflowMsg); throw new PluginRpcEndpointException(new TransactionSimulationError(txOverflowMsg)); } diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLimitsValidationResult.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLimitsValidationResult.java index 67060827..c2aaa694 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLimitsValidationResult.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLimitsValidationResult.java @@ -19,31 +19,70 @@ /** Represents the result of verifying module line counts against their limits. */ @Getter public class ModuleLimitsValidationResult { - // Getters for verificationOutcome and affectedModuleName private final ModuleLineCountValidator.ModuleLineCountResult result; private final String moduleName; + private final Integer moduleLineCount; + private final Integer moduleLineLimit; + private final Integer cumulativeModuleLineCount; + private final Integer cumulativeModuleLineLimit; public static ModuleLimitsValidationResult VALID = - new ModuleLimitsValidationResult(ModuleLineCountValidator.ModuleLineCountResult.VALID, null); + new ModuleLimitsValidationResult( + ModuleLineCountValidator.ModuleLineCountResult.VALID, null, null, null, null, null); private ModuleLimitsValidationResult( - ModuleLineCountValidator.ModuleLineCountResult result, String moduleName) { + final ModuleLineCountValidator.ModuleLineCountResult result, + final String moduleName, + final Integer moduleLineCount, + final Integer moduleLineLimit, + final Integer cumulativeModuleLineCount, + final Integer cumulativeModuleLineLimit) { this.result = result; this.moduleName = moduleName; + this.moduleLineCount = moduleLineCount; + this.moduleLineLimit = moduleLineLimit; + this.cumulativeModuleLineCount = cumulativeModuleLineCount; + this.cumulativeModuleLineLimit = cumulativeModuleLineLimit; } - public static ModuleLimitsValidationResult moduleNotDefined(String moduleName) { + public static ModuleLimitsValidationResult moduleNotDefined(final String moduleName) { return new ModuleLimitsValidationResult( - ModuleLineCountValidator.ModuleLineCountResult.MODULE_NOT_DEFINED, moduleName); + ModuleLineCountValidator.ModuleLineCountResult.MODULE_NOT_DEFINED, + moduleName, + null, + null, + null, + null); } - public static ModuleLimitsValidationResult txModuleLineCountOverflow(String moduleName) { + public static ModuleLimitsValidationResult txModuleLineCountOverflow( + final String moduleName, + final Integer moduleLineCount, + final Integer moduleLineLimit, + final Integer cumulativeModuleLineCount, + final Integer cumulativeModuleLineLimit) { return new ModuleLimitsValidationResult( - ModuleLineCountValidator.ModuleLineCountResult.TX_MODULE_LINE_COUNT_OVERFLOW, moduleName); + ModuleLineCountValidator.ModuleLineCountResult.TX_MODULE_LINE_COUNT_OVERFLOW, + moduleName, + moduleLineCount, + moduleLineLimit, + cumulativeModuleLineCount, + cumulativeModuleLineLimit); } - public static ModuleLimitsValidationResult blockModuleLineCountFull(String moduleName) { + public static ModuleLimitsValidationResult blockModuleLineCountFull( + final String moduleName, + final Integer moduleLineCount, + final Integer moduleLineLimit, + final Integer cumulativeModuleLineCount, + final Integer cumulativeModuleLineLimit) { + return new ModuleLimitsValidationResult( - ModuleLineCountValidator.ModuleLineCountResult.BLOCK_MODULE_LINE_COUNT_FULL, moduleName); + ModuleLineCountValidator.ModuleLineCountResult.BLOCK_MODULE_LINE_COUNT_FULL, + moduleName, + moduleLineCount, + moduleLineLimit, + cumulativeModuleLineCount, + cumulativeModuleLineLimit); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLineCountValidator.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLineCountValidator.java index 9dcb23e2..5009e3ea 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLineCountValidator.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLineCountValidator.java @@ -72,11 +72,21 @@ public ModuleLimitsValidationResult validate(Map currentAccumul currentTotalLineCountForModule - previouslyAccumulatedLineCount; if (lineCountAddedByCurrentTx > lineCountLimitForModule) { - return ModuleLimitsValidationResult.txModuleLineCountOverflow(moduleName); + return ModuleLimitsValidationResult.txModuleLineCountOverflow( + moduleName, + lineCountAddedByCurrentTx, + lineCountLimitForModule, + currentTotalLineCountForModule, + lineCountLimitForModule); } if (currentTotalLineCountForModule > lineCountLimitForModule) { - return ModuleLimitsValidationResult.blockModuleLineCountFull(moduleName); + return ModuleLimitsValidationResult.blockModuleLineCountFull( + moduleName, + lineCountAddedByCurrentTx, + lineCountLimitForModule, + currentTotalLineCountForModule, + lineCountLimitForModule); } } return ModuleLimitsValidationResult.VALID; 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 573e851b..44c1cd74 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 @@ -154,22 +154,22 @@ public TransactionSelectionResult evaluateTransactionPostProcessing( throw new RuntimeException( "Module " + result.getModuleName() + " does not exist in the limits file."); case TX_MODULE_LINE_COUNT_OVERFLOW: - /* log.warn( - "Tx {} line count for module {}={} is above the limit {}, removing from the txpool", - transaction.getHash(), - result.getModuleName(), - txModuleLineCount, - moduleLineCountLimit);*/ + log.warn( + "Tx {} line count for module {}={} is above the limit {}, removing from the txpool", + transaction.getHash(), + result.getModuleName(), + result.getModuleLineCount(), + result.getModuleLineCount()); rememberOverLineCountLimitTransaction(transaction); return TX_MODULE_LINE_COUNT_OVERFLOW; case BLOCK_MODULE_LINE_COUNT_FULL: - /* log.atTrace() - .setMessage( - "Cumulated line count for module {}={} is above the limit {}, stopping selection") - .addArgument(result.getModuleName()) - .addArgument(cumulatedModuleLineCount) - .addArgument(moduleLineCountLimit) - .log();*/ + log.atTrace() + .setMessage( + "Cumulated line count for module {}={} is above the limit {}, stopping selection") + .addArgument(result.getModuleName()) + .addArgument(result.getCumulativeModuleLineCount()) + .addArgument(result.getCumulativeModuleLineLimit()) + .log(); return BLOCK_MODULE_LINE_COUNT_FULL; default: break; From 201e5fd265e047b910c7d56a41068814db7f9996 Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Tue, 9 Apr 2024 20:03:36 +0200 Subject: [PATCH 09/12] Reject a tx, sent via eth_sendRawTransaction, if its simulation fails Signed-off-by: Fabio Di Fabio --- .../plugin/acc/test/LineaPluginTestBase.java | 14 ++ .../TransactionTraceLimitOverflowTest.java | 1 + ...SendRawTransactionSimulationCheckTest.java | 103 +++++++++++ .../AbstractLineaSharedOptionsPlugin.java | 6 +- ...ptions.java => LineaTracerCliOptions.java} | 13 +- ...neaTransactionPoolValidatorCliOptions.java | 37 +++- ...TransactionPoolValidatorConfiguration.java | 6 +- .../linea/rpc/linea/LineaEstimateGas.java | 15 +- .../LineaTransactionPoolValidatorFactory.java | 25 ++- .../LineaTransactionPoolValidatorPlugin.java | 19 +- .../validators/SimulationValidator.java | 127 ++++++++++++++ .../validators/SimulationValidatorTest.java | 166 ++++++++++++++++++ ...TraceLineLimitTransactionSelectorTest.java | 68 ++++--- 13 files changed, 555 insertions(+), 45 deletions(-) create mode 100644 acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EthSendRawTransactionSimulationCheckTest.java rename arithmetization/src/main/java/net/consensys/linea/config/{LineaTracerConfigurationCLiOptions.java => LineaTracerCliOptions.java} (83%) create mode 100644 arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidator.java create mode 100644 arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidatorTest.java 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 1c1cac21..df589f84 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 @@ -23,16 +23,20 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import linea.plugin.acc.test.tests.web3j.generated.SimpleStorage; import org.apache.commons.lang3.RandomStringUtils; +import org.apache.tuweni.bytes.Bytes; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; +import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase; import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts; import org.hyperledger.besu.tests.acceptance.dsl.condition.txpool.TxPoolConditions; @@ -198,6 +202,10 @@ protected void assertTransactionNotInThePool(String hash) { .notInTransactionPool(Hash.fromHexString(hash))); } + protected List> getTxPoolContent() { + return minerNode.execute(new TxPoolTransactions().getTxPoolContents()); + } + private TransactionReceiptProcessor createReceiptProcessor(Web3j web3j) { return new PollingTransactionReceiptProcessor( web3j, @@ -219,4 +227,10 @@ protected String sendTransactionWithGivenLengthPayload( BigInteger.ZERO) .getTransactionHash(); } + + protected Hash getTransactionHashFromRLP(final byte[] signedTxContractInteraction) { + return Transaction.readFrom( + new BytesValueRLPInput(Bytes.wrap(signedTxContractInteraction), false)) + .getHash(); + } } diff --git a/acceptance-tests/src/test/java/linea/plugin/acc/test/TransactionTraceLimitOverflowTest.java b/acceptance-tests/src/test/java/linea/plugin/acc/test/TransactionTraceLimitOverflowTest.java index 3558e4e4..21cc92fd 100644 --- a/acceptance-tests/src/test/java/linea/plugin/acc/test/TransactionTraceLimitOverflowTest.java +++ b/acceptance-tests/src/test/java/linea/plugin/acc/test/TransactionTraceLimitOverflowTest.java @@ -43,6 +43,7 @@ public List getTestCliOptions() { .set( "--plugin-linea-module-limit-file-path=", getResourcePath("/txOverflowModuleLimits.toml")) + .set("--plugin-linea-tx-pool-simulation-check-api-enabled=", "false") .build(); } diff --git a/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EthSendRawTransactionSimulationCheckTest.java b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EthSendRawTransactionSimulationCheckTest.java new file mode 100644 index 00000000..8534fe99 --- /dev/null +++ b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EthSendRawTransactionSimulationCheckTest.java @@ -0,0 +1,103 @@ +/* + * 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.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + +import linea.plugin.acc.test.LineaPluginTestBase; +import linea.plugin.acc.test.TestCommandLineOptionsBuilder; +import linea.plugin.acc.test.tests.web3j.generated.SimpleStorage; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.tests.acceptance.dsl.account.Account; +import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts; +import org.junit.jupiter.api.Test; +import org.web3j.crypto.Credentials; +import org.web3j.crypto.RawTransaction; +import org.web3j.crypto.TransactionEncoder; +import org.web3j.protocol.Web3j; +import org.web3j.protocol.core.methods.response.EthSendTransaction; +import org.web3j.tx.gas.DefaultGasProvider; +import org.web3j.utils.Numeric; + +public class EthSendRawTransactionSimulationCheckTest extends LineaPluginTestBase { + + private static final BigInteger GAS_LIMIT = DefaultGasProvider.GAS_LIMIT; + private static final BigInteger VALUE = BigInteger.ZERO; + private static final BigInteger GAS_PRICE = BigInteger.TEN.pow(9); + + @Override + public List getTestCliOptions() { + return new TestCommandLineOptionsBuilder() + .set( + "--plugin-linea-module-limit-file-path=", + getResourcePath("/txOverflowModuleLimits.toml")) + .set("--plugin-linea-tx-pool-simulation-check-api-enabled=", "true") + .build(); + } + + @Test + public void transactionOverModuleLineCountNotAccepted() throws Exception { + final SimpleStorage simpleStorage = deploySimpleStorage(); + final Web3j web3j = minerNode.nodeRequests().eth(); + final String contractAddress = simpleStorage.getContractAddress(); + final String txData = simpleStorage.add(BigInteger.valueOf(100)).encodeFunctionCall(); + + // this tx will not be accepted since it goes above the line count limit + final RawTransaction txModuleLineCountTooBig = + RawTransaction.createTransaction( + CHAIN_ID, + BigInteger.valueOf(1), + GAS_LIMIT.divide(BigInteger.TEN), + contractAddress, + VALUE, + txData, + GAS_PRICE, + GAS_PRICE.multiply(BigInteger.TEN).add(BigInteger.ONE)); + final byte[] signedTxContractInteraction = + TransactionEncoder.signMessage( + txModuleLineCountTooBig, Credentials.create(Accounts.GENESIS_ACCOUNT_TWO_PRIVATE_KEY)); + + final EthSendTransaction signedTxContractInteractionResp = + web3j.ethSendRawTransaction(Numeric.toHexString(signedTxContractInteraction)).send(); + + assertThat(signedTxContractInteractionResp.hasError()).isTrue(); + assertThat(signedTxContractInteractionResp.getError().getMessage()) + .isEqualTo("Transaction line count for module ADD=2018 is above the limit 70"); + + assertThat(getTxPoolContent()).isEmpty(); + + // these are under the line count limit and should be accepted and selected + final Account fewLinesSender = accounts.getSecondaryBenefactor(); + final Account recipient = accounts.createAccount("recipient"); + final List expectedConfirmedTxs = new ArrayList<>(4); + + expectedConfirmedTxs.addAll( + minerNode.execute( + accountTransactions.createIncrementalTransfers(fewLinesSender, recipient, 4))); + + final var txPoolContentByHash = getTxPoolContent().stream().map(e -> e.get("hash")).toList(); + assertThat(txPoolContentByHash) + .containsExactlyInAnyOrderElementsOf( + expectedConfirmedTxs.stream().map(Hash::toHexString).toList()); + + expectedConfirmedTxs.stream() + .map(Hash::toHexString) + .forEach(hash -> minerNode.verify(eth.expectSuccessfulTransactionReceipt(hash))); + } +} diff --git a/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java b/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java index 5046c8c9..94d9bdf3 100644 --- a/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java @@ -23,8 +23,8 @@ import net.consensys.linea.config.LineaProfitabilityConfiguration; import net.consensys.linea.config.LineaRpcCliOptions; import net.consensys.linea.config.LineaRpcConfiguration; +import net.consensys.linea.config.LineaTracerCliOptions; import net.consensys.linea.config.LineaTracerConfiguration; -import net.consensys.linea.config.LineaTracerConfigurationCLiOptions; import net.consensys.linea.config.LineaTransactionPoolValidatorCliOptions; import net.consensys.linea.config.LineaTransactionPoolValidatorConfiguration; import net.consensys.linea.config.LineaTransactionSelectorCliOptions; @@ -44,7 +44,7 @@ public abstract class AbstractLineaSharedOptionsPlugin implements BesuPlugin { private static LineaRpcCliOptions rpcCliOptions; private static LineaProfitabilityCliOptions profitabilityCliOptions; protected static LineaTransactionSelectorConfiguration transactionSelectorConfiguration; - protected static LineaTracerConfigurationCLiOptions tracerConfigurationCliOptions; + protected static LineaTracerCliOptions tracerConfigurationCliOptions; protected static LineaTransactionPoolValidatorConfiguration transactionPoolValidatorConfiguration; protected static LineaL1L2BridgeConfiguration l1L2BridgeConfiguration; protected static LineaRpcConfiguration rpcConfiguration; @@ -71,7 +71,7 @@ public synchronized void register(final BesuContext context) { l1L2BridgeCliOptions = LineaL1L2BridgeCliOptions.create(); rpcCliOptions = LineaRpcCliOptions.create(); profitabilityCliOptions = LineaProfitabilityCliOptions.create(); - tracerConfigurationCliOptions = LineaTracerConfigurationCLiOptions.create(); + tracerConfigurationCliOptions = LineaTracerCliOptions.create(); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, transactionSelectorCliOptions); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, transactionPoolValidatorCliOptions); diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfigurationCLiOptions.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerCliOptions.java similarity index 83% rename from arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfigurationCLiOptions.java rename to arithmetization/src/main/java/net/consensys/linea/config/LineaTracerCliOptions.java index 5b6f6c04..0d75650e 100644 --- a/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfigurationCLiOptions.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerCliOptions.java @@ -17,7 +17,7 @@ import com.google.common.base.MoreObjects; import picocli.CommandLine; -public class LineaTracerConfigurationCLiOptions { +public class LineaTracerCliOptions { public static final String MODULE_LIMIT_FILE_PATH = "--plugin-linea-module-limit-file-path"; public static final String DEFAULT_MODULE_LIMIT_FILE_PATH = "moduleLimitFile.toml"; @@ -30,15 +30,15 @@ public class LineaTracerConfigurationCLiOptions { "Path to the toml file containing the module limits (default: ${DEFAULT-VALUE})") private String moduleLimitFilePath = DEFAULT_MODULE_LIMIT_FILE_PATH; - private LineaTracerConfigurationCLiOptions() {} + private LineaTracerCliOptions() {} /** * Create Linea cli options. * * @return the Linea cli options */ - public static LineaTracerConfigurationCLiOptions create() { - return new LineaTracerConfigurationCLiOptions(); + public static LineaTracerCliOptions create() { + return new LineaTracerCliOptions(); } /** @@ -47,9 +47,8 @@ public static LineaTracerConfigurationCLiOptions create() { * @param config the config * @return the Linea cli options */ - public static LineaTracerConfigurationCLiOptions fromConfig( - final LineaTracerConfiguration config) { - final LineaTracerConfigurationCLiOptions options = create(); + public static LineaTracerCliOptions fromConfig(final LineaTracerConfiguration config) { + final LineaTracerCliOptions options = create(); options.moduleLimitFilePath = config.moduleLimitsFilePath(); return options; } diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionPoolValidatorCliOptions.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionPoolValidatorCliOptions.java index 4cb68f78..9c0ef038 100644 --- a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionPoolValidatorCliOptions.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionPoolValidatorCliOptions.java @@ -30,6 +30,14 @@ public class LineaTransactionPoolValidatorCliOptions { public static final String MAX_TX_CALLDATA_SIZE = "--plugin-linea-max-tx-calldata-size"; public static final int DEFAULT_MAX_TX_CALLDATA_SIZE = 60_000; + public static final String TX_POOL_ENABLE_SIMULATION_CHECK_API = + "--plugin-linea-tx-pool-simulation-check-api-enabled"; + public static final boolean DEFAULT_TX_POOL_ENABLE_SIMULATION_CHECK_API = true; + + public static final String TX_POOL_ENABLE_SIMULATION_CHECK_P2P = + "--plugin-linea-tx-pool-simulation-check-p2p-enabled"; + public static final boolean DEFAULT_TX_POOL_ENABLE_SIMULATION_CHECK_P2P = false; + @CommandLine.Option( names = {DENY_LIST_PATH}, hidden = true, @@ -58,6 +66,24 @@ public class LineaTransactionPoolValidatorCliOptions { + ")") private int maxTxCallDataSize = DEFAULT_MAX_TX_CALLDATA_SIZE; + @CommandLine.Option( + names = {TX_POOL_ENABLE_SIMULATION_CHECK_API}, + arity = "0..1", + hidden = true, + paramLabel = "", + description = + "Enable the simulation check for txs received via API? (default: ${DEFAULT-VALUE})") + private boolean txPoolSimulationCheckApiEnabled = DEFAULT_TX_POOL_ENABLE_SIMULATION_CHECK_API; + + @CommandLine.Option( + names = {TX_POOL_ENABLE_SIMULATION_CHECK_P2P}, + arity = "0..1", + hidden = true, + paramLabel = "", + description = + "Enable the simulation check for txs received via p2p? (default: ${DEFAULT-VALUE})") + private boolean txPoolSimulationCheckP2pEnabled = DEFAULT_TX_POOL_ENABLE_SIMULATION_CHECK_P2P; + private LineaTransactionPoolValidatorCliOptions() {} /** @@ -81,7 +107,8 @@ public static LineaTransactionPoolValidatorCliOptions fromConfig( options.denyListPath = config.denyListPath(); options.maxTxGasLimit = config.maxTxGasLimit(); options.maxTxCallDataSize = config.maxTxCalldataSize(); - + options.txPoolSimulationCheckApiEnabled = config.txPoolSimulationCheckApiEnabled(); + options.txPoolSimulationCheckP2pEnabled = config.txPoolSimulationCheckP2pEnabled(); return options; } @@ -92,7 +119,11 @@ public static LineaTransactionPoolValidatorCliOptions fromConfig( */ public LineaTransactionPoolValidatorConfiguration toDomainObject() { return new LineaTransactionPoolValidatorConfiguration( - denyListPath, maxTxGasLimit, maxTxCallDataSize); + denyListPath, + maxTxGasLimit, + maxTxCallDataSize, + txPoolSimulationCheckApiEnabled, + txPoolSimulationCheckP2pEnabled); } @Override @@ -101,6 +132,8 @@ public String toString() { .add(DENY_LIST_PATH, denyListPath) .add(MAX_TX_GAS_LIMIT_OPTION, maxTxGasLimit) .add(MAX_TX_CALLDATA_SIZE, maxTxCallDataSize) + .add(TX_POOL_ENABLE_SIMULATION_CHECK_API, txPoolSimulationCheckApiEnabled) + .add(TX_POOL_ENABLE_SIMULATION_CHECK_P2P, txPoolSimulationCheckP2pEnabled) .toString(); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionPoolValidatorConfiguration.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionPoolValidatorConfiguration.java index 90d41edf..fdad5fa0 100644 --- a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionPoolValidatorConfiguration.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionPoolValidatorConfiguration.java @@ -26,4 +26,8 @@ */ @Builder(toBuilder = true) public record LineaTransactionPoolValidatorConfiguration( - String denyListPath, int maxTxGasLimit, int maxTxCalldataSize) {} + String denyListPath, + int maxTxGasLimit, + int maxTxCalldataSize, + boolean txPoolSimulationCheckApiEnabled, + boolean txPoolSimulationCheckP2pEnabled) {} 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 index 4e9b4707..e5c38813 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java @@ -49,6 +49,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.evm.tracing.EstimateGasOperationTracer; +import org.hyperledger.besu.plugin.data.BlockHeader; import org.hyperledger.besu.plugin.services.BesuConfiguration; import org.hyperledger.besu.plugin.services.BlockchainService; import org.hyperledger.besu.plugin.services.TransactionSimulationService; @@ -209,11 +210,12 @@ private Long estimateGasUsed( final Wei minGasPrice) { final var estimateGasOperationTracer = new EstimateGasOperationTracer(); - final var zkTracer = createZkTracer(); + final var chainHeadHeader = blockchainService.getChainHeadHeader(); + final var zkTracer = createZkTracer(chainHeadHeader); TracerAggregator tracerAggregator = TracerAggregator.create(estimateGasOperationTracer, zkTracer); - final var chainHeadHash = blockchainService.getChainHeadHash(); + final var chainHeadHash = chainHeadHeader.getBlockHash(); final var maybeSimulationResults = transactionSimulationService.simulate(transaction, chainHeadHash, tracerAggregator, true); @@ -417,10 +419,10 @@ private Transaction createTransactionForSimulation( return txBuilder.build(); } - private ZkTracer createZkTracer() { + private ZkTracer createZkTracer(final BlockHeader chainHeadHeader) { var zkTracer = new ZkTracer(l1L2BridgeConfiguration); zkTracer.traceStartConflation(1L); - zkTracer.traceStartBlock(blockchainService.getChainHeadHeader()); + zkTracer.traceStartBlock(chainHeadHeader); return zkTracer; } @@ -443,6 +445,11 @@ private void handleModuleOverLimit(ModuleLimitsValidationResult moduleLimitResul log.warn(txOverflowMsg); throw new PluginRpcEndpointException(new TransactionSimulationError(txOverflowMsg)); } + + final String internalErrorMsg = + String.format("Do not know what to do with result %s", moduleLimitResult.getResult()); + log.error(internalErrorMsg); + throw new PluginRpcEndpointException(RpcErrorType.PLUGIN_INTERNAL_ERROR, internalErrorMsg); } public record Response( diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/LineaTransactionPoolValidatorFactory.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/LineaTransactionPoolValidatorFactory.java index 6fabee4e..0873678a 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/LineaTransactionPoolValidatorFactory.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/LineaTransactionPoolValidatorFactory.java @@ -16,18 +16,22 @@ package net.consensys.linea.sequencer.txpoolvalidation; import java.util.Arrays; +import java.util.Map; import java.util.Optional; import java.util.Set; +import net.consensys.linea.config.LineaL1L2BridgeConfiguration; import net.consensys.linea.config.LineaProfitabilityConfiguration; import net.consensys.linea.config.LineaTransactionPoolValidatorConfiguration; import net.consensys.linea.sequencer.txpoolvalidation.validators.AllowedAddressValidator; import net.consensys.linea.sequencer.txpoolvalidation.validators.CalldataValidator; import net.consensys.linea.sequencer.txpoolvalidation.validators.GasLimitValidator; import net.consensys.linea.sequencer.txpoolvalidation.validators.ProfitabilityValidator; +import net.consensys.linea.sequencer.txpoolvalidation.validators.SimulationValidator; import org.hyperledger.besu.datatypes.Address; 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.txvalidator.PluginTransactionPoolValidator; import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidatorFactory; @@ -36,21 +40,30 @@ public class LineaTransactionPoolValidatorFactory implements PluginTransactionPo private final BesuConfiguration besuConfiguration; private final BlockchainService blockchainService; + private final TransactionSimulationService transactionSimulationService; private final LineaTransactionPoolValidatorConfiguration txPoolValidatorConf; private final LineaProfitabilityConfiguration profitabilityConf; private final Set
denied; + private final Map moduleLineLimitsMap; + private final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration; public LineaTransactionPoolValidatorFactory( final BesuConfiguration besuConfiguration, final BlockchainService blockchainService, + final TransactionSimulationService transactionSimulationService, final LineaTransactionPoolValidatorConfiguration txPoolValidatorConf, final LineaProfitabilityConfiguration profitabilityConf, - final Set
denied) { + final Set
deniedAddresses, + final Map moduleLineLimitsMap, + final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration) { this.besuConfiguration = besuConfiguration; this.blockchainService = blockchainService; + this.transactionSimulationService = transactionSimulationService; this.txPoolValidatorConf = txPoolValidatorConf; this.profitabilityConf = profitabilityConf; - this.denied = denied; + this.denied = deniedAddresses; + this.moduleLineLimitsMap = moduleLineLimitsMap; + this.l1L2BridgeConfiguration = l1L2BridgeConfiguration; } @Override @@ -60,7 +73,13 @@ public PluginTransactionPoolValidator createTransactionValidator() { new AllowedAddressValidator(denied), new GasLimitValidator(txPoolValidatorConf), new CalldataValidator(txPoolValidatorConf), - new ProfitabilityValidator(besuConfiguration, blockchainService, profitabilityConf) + new ProfitabilityValidator(besuConfiguration, blockchainService, profitabilityConf), + new SimulationValidator( + blockchainService, + transactionSimulationService, + txPoolValidatorConf, + moduleLineLimitsMap, + l1L2BridgeConfiguration) }; return (transaction, isLocal, hasPriority) -> diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/LineaTransactionPoolValidatorPlugin.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/LineaTransactionPoolValidatorPlugin.java index 52eb2de9..4d255cb5 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/LineaTransactionPoolValidatorPlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/LineaTransactionPoolValidatorPlugin.java @@ -15,6 +15,8 @@ package net.consensys.linea.sequencer.txpoolvalidation; +import static net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator.createLimitModules; + import java.io.File; import java.nio.file.Files; import java.nio.file.Path; @@ -32,6 +34,7 @@ import org.hyperledger.besu.plugin.services.BesuConfiguration; import org.hyperledger.besu.plugin.services.BlockchainService; import org.hyperledger.besu.plugin.services.TransactionPoolValidatorService; +import org.hyperledger.besu.plugin.services.TransactionSimulationService; /** * This class extends the default transaction validation rules for adding transactions to the @@ -46,6 +49,7 @@ public class LineaTransactionPoolValidatorPlugin extends AbstractLineaRequiredPl private BesuConfiguration besuConfiguration; private BlockchainService blockchainService; private TransactionPoolValidatorService transactionPoolValidatorService; + private TransactionSimulationService transactionSimulationService; @Override public Optional getName() { @@ -77,6 +81,14 @@ public void doRegister(final BesuContext context) { () -> new RuntimeException( "Failed to obtain TransactionPoolValidationService from the BesuContext.")); + + transactionSimulationService = + context + .getService(TransactionSimulationService.class) + .orElseThrow( + () -> + new RuntimeException( + "Failed to obtain TransactionSimulatorService from the BesuContext.")); } @Override @@ -85,16 +97,19 @@ public void beforeExternalServices() { try (Stream lines = Files.lines( Path.of(new File(transactionPoolValidatorConfiguration.denyListPath()).toURI()))) { - final Set
denied = + final Set
deniedAddresses = lines.map(l -> Address.fromHexString(l.trim())).collect(Collectors.toUnmodifiableSet()); transactionPoolValidatorService.registerPluginTransactionValidatorFactory( new LineaTransactionPoolValidatorFactory( besuConfiguration, blockchainService, + transactionSimulationService, transactionPoolValidatorConfiguration, profitabilityConfiguration, - denied)); + deniedAddresses, + createLimitModules(tracerConfiguration), + l1L2BridgeConfiguration)); } catch (Exception e) { throw new RuntimeException(e); diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidator.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidator.java new file mode 100644 index 00000000..070e1eab --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidator.java @@ -0,0 +1,127 @@ +/* + * 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.sequencer.txpoolvalidation.validators; + +import static net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator.ModuleLineCountResult.MODULE_NOT_DEFINED; +import static net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator.ModuleLineCountResult.TX_MODULE_LINE_COUNT_OVERFLOW; + +import java.util.Map; +import java.util.Optional; + +import lombok.extern.slf4j.Slf4j; +import net.consensys.linea.config.LineaL1L2BridgeConfiguration; +import net.consensys.linea.config.LineaTransactionPoolValidatorConfiguration; +import net.consensys.linea.sequencer.modulelimit.ModuleLimitsValidationResult; +import net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator; +import net.consensys.linea.zktracer.ZkTracer; +import org.hyperledger.besu.datatypes.Transaction; +import org.hyperledger.besu.plugin.data.BlockHeader; +import org.hyperledger.besu.plugin.services.BlockchainService; +import org.hyperledger.besu.plugin.services.TransactionSimulationService; +import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidator; + +@Slf4j +public class SimulationValidator implements PluginTransactionPoolValidator { + private final BlockchainService blockchainService; + private final TransactionSimulationService transactionSimulationService; + private final LineaTransactionPoolValidatorConfiguration txPoolValidatorConf; + private final Map moduleLineLimitsMap; + private final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration; + + public SimulationValidator( + final BlockchainService blockchainService, + final TransactionSimulationService transactionSimulationService, + final LineaTransactionPoolValidatorConfiguration txPoolValidatorConf, + final Map moduleLineLimitsMap, + final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration) { + this.blockchainService = blockchainService; + this.transactionSimulationService = transactionSimulationService; + this.txPoolValidatorConf = txPoolValidatorConf; + this.moduleLineLimitsMap = moduleLineLimitsMap; + this.l1L2BridgeConfiguration = l1L2BridgeConfiguration; + } + + @Override + public Optional validateTransaction( + final Transaction transaction, final boolean isLocal, final boolean hasPriority) { + + if ((isLocal && txPoolValidatorConf.txPoolSimulationCheckApiEnabled()) + || (!isLocal && txPoolValidatorConf.txPoolSimulationCheckP2pEnabled())) { + + final ModuleLineCountValidator moduleLineCountValidator = + new ModuleLineCountValidator(moduleLineLimitsMap); + final var chainHeadHeader = blockchainService.getChainHeadHeader(); + + final var zkTracer = createZkTracer(chainHeadHeader); + final var maybeSimulationResults = + transactionSimulationService.simulate( + transaction, chainHeadHeader.getBlockHash(), zkTracer, true); + + ModuleLimitsValidationResult moduleLimit = + moduleLineCountValidator.validate(zkTracer.getModulesLineCount()); + + if (moduleLimit.getResult() != ModuleLineCountValidator.ModuleLineCountResult.VALID) { + return Optional.of(handleModuleOverLimit(moduleLimit)); + } + + if (maybeSimulationResults.isPresent()) { + final var simulationResult = maybeSimulationResults.get(); + if (simulationResult.isInvalid()) { + return Optional.of( + "Invalid transaction" + + simulationResult.getInvalidReason().map(ir -> ": " + ir).orElse("")); + } + if (!simulationResult.isSuccessful()) { + return Optional.of( + "Reverted transaction" + + simulationResult + .getRevertReason() + .map(rr -> ": " + rr.toHexString()) + .orElse("")); + } + } + } + + return Optional.empty(); + } + + private ZkTracer createZkTracer(final BlockHeader chainHeadHeader) { + var zkTracer = new ZkTracer(l1L2BridgeConfiguration); + zkTracer.traceStartConflation(1L); + zkTracer.traceStartBlock(chainHeadHeader); + return zkTracer; + } + + private String handleModuleOverLimit(ModuleLimitsValidationResult moduleLimitResult) { + if (moduleLimitResult.getResult() == MODULE_NOT_DEFINED) { + String moduleNotDefinedMsg = + String.format( + "Module %s does not exist in the limits file.", moduleLimitResult.getModuleName()); + log.error(moduleNotDefinedMsg); + return moduleNotDefinedMsg; + } + if (moduleLimitResult.getResult() == TX_MODULE_LINE_COUNT_OVERFLOW) { + String txOverflowMsg = + String.format( + "Transaction line count for module %s=%s is above the limit %s", + moduleLimitResult.getModuleName(), + moduleLimitResult.getModuleLineCount(), + moduleLimitResult.getModuleLineLimit()); + log.warn(txOverflowMsg); + return txOverflowMsg; + } + return "Internal Error: do not know what to do with result: " + moduleLimitResult.getResult(); + } +} diff --git a/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidatorTest.java b/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidatorTest.java new file mode 100644 index 00000000..477ca7c4 --- /dev/null +++ b/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidatorTest.java @@ -0,0 +1,166 @@ +/* + * 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.sequencer.txpoolvalidation.validators; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.math.BigInteger; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import net.consensys.linea.config.LineaL1L2BridgeConfiguration; +import net.consensys.linea.config.LineaTracerConfiguration; +import net.consensys.linea.config.LineaTransactionPoolValidatorConfiguration; +import net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator; +import net.consensys.linea.sequencer.txselection.selectors.TraceLineLimitTransactionSelectorTest; +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.Address; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.plugin.services.BlockchainService; +import org.hyperledger.besu.plugin.services.TransactionSimulationService; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@Slf4j +@RequiredArgsConstructor +@ExtendWith(MockitoExtension.class) +public class SimulationValidatorTest { + private static final String MODULE_LINE_LIMITS_RESOURCE_NAME = "/sequencer/line-limits.toml"; + public static final Address SENDER = + Address.fromHexString("0x0000000000000000000000000000000000001000"); + public static final Address RECIPIENT = + Address.fromHexString("0x0000000000000000000000000000000000001001"); + private static Wei BASE_FEE = Wei.of(7); + private static Wei PROFITABLE_GAS_PRICE = Wei.of(11000000); + private static final SECPSignature FAKE_SIGNATURE; + private static final Address BRIDGE_CONTRACT = + Address.fromHexString("0x508Ca82Df566dCD1B0DE8296e70a96332cD644ec"); + private static final Bytes BRIDGE_LOG_TOPIC = + Bytes.fromHexString("e856c2b8bd4eb0027ce32eeaf595c21b0b6b4644b326e5b7bd80a1cf8db72e6c"); + + static { + final X9ECParameters params = SECNamedCurves.getByName("secp256k1"); + final ECDomainParameters curve = + new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH()); + FAKE_SIGNATURE = + SECPSignature.create( + new BigInteger( + "66397251408932042429874251838229702988618145381408295790259650671563847073199"), + new BigInteger( + "24729624138373455972486746091821238755870276413282629437244319694880507882088"), + (byte) 0, + curve.getN()); + } + + private Map lineCountLimits; + + @Mock BlockchainService blockchainService; + @Mock TransactionSimulationService transactionSimulationService; + + @TempDir static Path tempDir; + static Path lineLimitsConfPath; + + @BeforeAll + public static void beforeAll() throws IOException { + lineLimitsConfPath = tempDir.resolve("line-limits.toml"); + Files.copy( + TraceLineLimitTransactionSelectorTest.class.getResourceAsStream( + MODULE_LINE_LIMITS_RESOURCE_NAME), + lineLimitsConfPath); + } + + @BeforeEach + public void initialize() { + final var tracerConf = + LineaTracerConfiguration.builder() + .moduleLimitsFilePath(lineLimitsConfPath.toString()) + .build(); + lineCountLimits = new HashMap<>(ModuleLineCountValidator.createLimitModules(tracerConf)); + final var blockHeader = mock(BlockHeader.class); + when(blockHeader.getBaseFee()).thenReturn(Optional.of(BASE_FEE)); + when(blockchainService.getChainHeadHeader()).thenReturn(blockHeader); + } + + private SimulationValidator createSimulationValidator( + final Map lineCountLimits, + final boolean enableForApi, + final boolean enableForP2p) { + return new SimulationValidator( + blockchainService, + transactionSimulationService, + LineaTransactionPoolValidatorConfiguration.builder() + .txPoolSimulationCheckApiEnabled(enableForApi) + .txPoolSimulationCheckP2pEnabled(enableForP2p) + .build(), + lineCountLimits, + LineaL1L2BridgeConfiguration.builder() + .contract(BRIDGE_CONTRACT) + .topic(BRIDGE_LOG_TOPIC) + .build()); + } + + @Test + public void successfulTransactionIsValid() { + final var simulationValidator = createSimulationValidator(lineCountLimits, true, false); + final org.hyperledger.besu.ethereum.core.Transaction transaction = + org.hyperledger.besu.ethereum.core.Transaction.builder() + .sender(SENDER) + .to(RECIPIENT) + .gasLimit(21000) + .gasPrice(PROFITABLE_GAS_PRICE) + .payload(Bytes.EMPTY) + .value(Wei.ONE) + .signature(FAKE_SIGNATURE) + .build(); + assertThat(simulationValidator.validateTransaction(transaction, true, false)).isEmpty(); + } + + @Test + public void moduleLineCountOverflowTransactionIsInvalid() { + lineCountLimits.put("ADD", 1); + final var simulationValidator = createSimulationValidator(lineCountLimits, true, false); + final org.hyperledger.besu.ethereum.core.Transaction transaction = + org.hyperledger.besu.ethereum.core.Transaction.builder() + .sender(SENDER) + .to(RECIPIENT) + .gasLimit(21000) + .gasPrice(PROFITABLE_GAS_PRICE) + .payload(Bytes.repeat((byte) 1, 1000)) + .value(Wei.ONE) + .signature(FAKE_SIGNATURE) + .build(); + assertThat(simulationValidator.validateTransaction(transaction, true, false)) + .contains("Transaction line count for module ADD=2 is above the limit 1"); + } +} 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 ea7f3714..eb9fb983 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 @@ -22,16 +22,17 @@ import static org.mockito.Mockito.when; import java.io.IOException; -import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; import java.util.Map; -import java.util.stream.Collectors; import net.consensys.linea.config.LineaL1L2BridgeConfiguration; import net.consensys.linea.config.LineaTracerConfiguration; import net.consensys.linea.config.LineaTransactionSelectorConfiguration; +import net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; -import org.apache.tuweni.toml.Toml; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.PendingTransaction; @@ -40,38 +41,50 @@ import org.hyperledger.besu.plugin.data.TransactionProcessingResult; import org.hyperledger.besu.plugin.data.TransactionSelectionResult; import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; public class TraceLineLimitTransactionSelectorTest { private static final int OVER_LINE_COUNT_LIMIT_CACHE_SIZE = 2; - private TestableTraceLineLimitTransactionSelector transactionSelector; + private static final String MODULE_LINE_LIMITS_RESOURCE_NAME = "/sequencer/line-limits.toml"; private Map lineCountLimits; + private LineaTracerConfiguration lineaTracerConfiguration; + + @TempDir static Path tempDir; + static Path lineLimitsConfPath; + + @BeforeAll + public static void beforeAll() throws IOException { + lineLimitsConfPath = tempDir.resolve("line-limits.toml"); + Files.copy( + TraceLineLimitTransactionSelectorTest.class.getResourceAsStream( + MODULE_LINE_LIMITS_RESOURCE_NAME), + lineLimitsConfPath); + } @BeforeEach public void initialize() { - lineCountLimits = loadLineCountLimitConf(); - transactionSelector = newSelectorForNewBlock(); - transactionSelector.reset(); + lineaTracerConfiguration = + LineaTracerConfiguration.builder() + .moduleLimitsFilePath(lineLimitsConfPath.toString()) + .build(); + lineCountLimits = + new HashMap<>(ModuleLineCountValidator.createLimitModules(lineaTracerConfiguration)); } - private TestableTraceLineLimitTransactionSelector newSelectorForNewBlock() { + private TestableTraceLineLimitTransactionSelector newSelectorForNewBlock( + final Map lineCountLimits) { return new TestableTraceLineLimitTransactionSelector( - lineCountLimits, "line-limits.toml", OVER_LINE_COUNT_LIMIT_CACHE_SIZE); - } - - private Map loadLineCountLimitConf() { - try (final InputStream is = - this.getClass().getResourceAsStream("/sequencer/line-limits.toml")) { - return Toml.parse(is).getTable("traces-limits").toMap().entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, e -> Math.toIntExact((long) e.getValue()))); - } catch (IOException e) { - throw new RuntimeException(e); - } + lineaTracerConfiguration, lineCountLimits, OVER_LINE_COUNT_LIMIT_CACHE_SIZE); } @Test public void shouldSelectWhenBelowLimits() { + final var transactionSelector = newSelectorForNewBlock(lineCountLimits); + transactionSelector.resetCache(); + final var evaluationContext = mockEvaluationContext(false, 100, Wei.of(1_100_000_000), Wei.of(1_000_000_000), 21000); verifyTransactionSelection( @@ -89,6 +102,9 @@ public void shouldSelectWhenBelowLimits() { @Test public void shouldNotSelectWhenOverLimits() { lineCountLimits.put("ADD", 1); + final var transactionSelector = newSelectorForNewBlock(lineCountLimits); + transactionSelector.resetCache(); + final var evaluationContext = mockEvaluationContext(false, 100, Wei.of(1_100_000_000), Wei.of(1_000_000_000), 21000); verifyTransactionSelection( @@ -106,6 +122,9 @@ public void shouldNotSelectWhenOverLimits() { @Test public void shouldNotReprocessedWhenOverLimits() { lineCountLimits.put("ADD", 1); + var transactionSelector = newSelectorForNewBlock(lineCountLimits); + transactionSelector.resetCache(); + var evaluationContext = mockEvaluationContext(false, 100, Wei.of(1_100_000_000), Wei.of(1_000_000_000), 21000); verifyTransactionSelection( @@ -119,7 +138,7 @@ public void shouldNotReprocessedWhenOverLimits() { transactionSelector.isOverLineCountLimitTxCached( evaluationContext.getPendingTransaction().getTransaction().getHash())) .isTrue(); - transactionSelector = newSelectorForNewBlock(); + transactionSelector = newSelectorForNewBlock(lineCountLimits); assertThat( transactionSelector.isOverLineCountLimitTxCached( evaluationContext.getPendingTransaction().getTransaction().getHash())) @@ -140,6 +159,9 @@ public void shouldNotReprocessedWhenOverLimits() { @Test public void shouldEvictWhenCacheIsFull() { lineCountLimits.put("ADD", 1); + final var transactionSelector = newSelectorForNewBlock(lineCountLimits); + transactionSelector.resetCache(); + final TestTransactionEvaluationContext[] evaluationContexts = new TestTransactionEvaluationContext[OVER_LINE_COUNT_LIMIT_CACHE_SIZE + 1]; for (int i = 0; i <= OVER_LINE_COUNT_LIMIT_CACHE_SIZE; i++) { @@ -222,8 +244,8 @@ private TestTransactionEvaluationContext mockEvaluationContext( private class TestableTraceLineLimitTransactionSelector extends TraceLineLimitTransactionSelector { TestableTraceLineLimitTransactionSelector( + final LineaTracerConfiguration lineaTracerConfiguration, final Map moduleLimits, - final String limitFilePath, final int overLimitCacheSize) { super( moduleLimits, @@ -234,10 +256,10 @@ private class TestableTraceLineLimitTransactionSelector .contract(Address.fromHexString("0xDEADBEEF")) .topic(Bytes.fromHexString("0x012345")) .build(), - LineaTracerConfiguration.builder().moduleLimitsFilePath(limitFilePath).build()); + lineaTracerConfiguration); } - void reset() { + void resetCache() { overLineCountLimitCache.clear(); } From 1af55058365581ba42619ea8e3f450d30949f173 Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Wed, 10 Apr 2024 18:59:30 +0200 Subject: [PATCH 10/12] remove unused method Signed-off-by: Fabio Di Fabio --- .../java/linea/plugin/acc/test/LineaPluginTestBase.java | 9 --------- 1 file changed, 9 deletions(-) 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 df589f84..4275b8b6 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 @@ -30,13 +30,10 @@ import linea.plugin.acc.test.tests.web3j.generated.SimpleStorage; import org.apache.commons.lang3.RandomStringUtils; -import org.apache.tuweni.bytes.Bytes; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; -import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; -import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase; import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts; import org.hyperledger.besu.tests.acceptance.dsl.condition.txpool.TxPoolConditions; @@ -227,10 +224,4 @@ protected String sendTransactionWithGivenLengthPayload( BigInteger.ZERO) .getTransactionHash(); } - - protected Hash getTransactionHashFromRLP(final byte[] signedTxContractInteraction) { - return Transaction.readFrom( - new BytesValueRLPInput(Bytes.wrap(signedTxContractInteraction), false)) - .getHash(); - } } From e398ff39d4c950a9edc6d9c69105b679fddae39c Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Wed, 10 Apr 2024 20:03:01 +0200 Subject: [PATCH 11/12] Apply suggestions from code review Signed-off-by: Fabio Di Fabio --- PLUGINS.md | 14 ++++++++------ .../validators/SimulationValidator.java | 7 +++++-- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/PLUGINS.md b/PLUGINS.md index c62f1f79..645f3aa3 100644 --- a/PLUGINS.md +++ b/PLUGINS.md @@ -44,12 +44,14 @@ of a transaction. #### CLI Options -| Option Name | Default Value | Command Line Argument | -|----------------------------------|----------------------|---------------------------------------------------| -| MAX_BLOCK_CALLDATA_SIZE | 70000 | `--plugin-linea-max-block-calldata-size` | -| MODULE_LIMIT_FILE_PATH | moduleLimitFile.toml | `--plugin-linea-module-limit-file-path` | -| OVER_LINE_COUNT_LIMIT_CACHE_SIZE | 10_000 | `--plugin-linea-over-line-count-limit-cache-size` | -| MAX_GAS_PER_BLOCK | 30_000_000L | `--plugin-linea-max-block-gas` | +| Option Name | Default Value | Command Line Argument | +|-------------------------------------|----------------------|-------------------------------------------------------| +| MAX_BLOCK_CALLDATA_SIZE | 70000 | `--plugin-linea-max-block-calldata-size` | +| MODULE_LIMIT_FILE_PATH | moduleLimitFile.toml | `--plugin-linea-module-limit-file-path` | +| OVER_LINE_COUNT_LIMIT_CACHE_SIZE | 10_000 | `--plugin-linea-over-line-count-limit-cache-size` | +| MAX_GAS_PER_BLOCK | 30_000_000L | `--plugin-linea-max-block-gas` | +| TX_POOL_ENABLE_SIMULATION_CHECK_API | true | `--plugin-linea-tx-pool-simulation-check-api-enabled` | +| TX_POOL_ENABLE_SIMULATION_CHECK_P2P | false | `--plugin-linea-tx-pool-simulation-check-p2p-enabled` | ### Transaction validation - LineaTransactionValidatorPlugin diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidator.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidator.java index 070e1eab..0709f4d9 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidator.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidator.java @@ -57,8 +57,11 @@ public SimulationValidator( public Optional validateTransaction( final Transaction transaction, final boolean isLocal, final boolean hasPriority) { - if ((isLocal && txPoolValidatorConf.txPoolSimulationCheckApiEnabled()) - || (!isLocal && txPoolValidatorConf.txPoolSimulationCheckP2pEnabled())) { + final boolean isLocalAndApiEnabled = + isLocal && txPoolValidatorConf.txPoolSimulationCheckApiEnabled(); + final boolean isRemoteAndP2pEnabled = + !isLocal && txPoolValidatorConf.txPoolSimulationCheckP2pEnabled(); + if (isRemoteAndP2pEnabled || isLocalAndApiEnabled) { final ModuleLineCountValidator moduleLineCountValidator = new ModuleLineCountValidator(moduleLineLimitsMap); From 88a814a41a02f2da15b219a8457e6f300770bfe5 Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Thu, 11 Apr 2024 14:33:06 +0200 Subject: [PATCH 12/12] Fix test flakyness Signed-off-by: Fabio Di Fabio --- .../plugin/acc/test/LineaPluginTestBase.java | 7 +- .../ProfitabilityValidatorTest.java | 80 +++++-------------- 2 files changed, 26 insertions(+), 61 deletions(-) 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 4275b8b6..38a898f1 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 @@ -150,7 +150,8 @@ private void assertTransactionsInCorrectBlocks(Web3j web3j, List hashes, protected SimpleStorage deploySimpleStorage() throws Exception { final Web3j web3j = minerNode.nodeRequests().eth(); final Credentials credentials = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY); - TransactionManager txManager = new RawTransactionManager(web3j, credentials, CHAIN_ID); + TransactionManager txManager = + new RawTransactionManager(web3j, credentials, CHAIN_ID, createReceiptProcessor(web3j)); final RemoteCall deploy = SimpleStorage.deploy(web3j, txManager, new DefaultGasProvider()); @@ -206,8 +207,8 @@ protected List> getTxPoolContent() { private TransactionReceiptProcessor createReceiptProcessor(Web3j web3j) { return new PollingTransactionReceiptProcessor( web3j, - TransactionManager.DEFAULT_POLLING_FREQUENCY, - TransactionManager.DEFAULT_POLLING_ATTEMPTS_PER_TX_HASH); + Math.max(1000, LINEA_CLIQUE_OPTIONS.blockPeriodSeconds() * 1000 / 5), + LINEA_CLIQUE_OPTIONS.blockPeriodSeconds() * 3); } protected String sendTransactionWithGivenLengthPayload( diff --git a/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/ProfitabilityValidatorTest.java b/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/ProfitabilityValidatorTest.java index 34a18b6e..5a8a1143 100644 --- a/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/ProfitabilityValidatorTest.java +++ b/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/ProfitabilityValidatorTest.java @@ -16,9 +16,9 @@ package net.consensys.linea.sequencer.txpoolvalidation.validators; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; import java.math.BigInteger; -import java.nio.file.Path; import java.util.Optional; import lombok.RequiredArgsConstructor; @@ -30,18 +30,18 @@ import org.bouncycastle.crypto.params.ECDomainParameters; import org.hyperledger.besu.crypto.SECPSignature; import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Wei; -import org.hyperledger.besu.plugin.data.BlockContext; -import org.hyperledger.besu.plugin.data.BlockHeader; import org.hyperledger.besu.plugin.services.BesuConfiguration; import org.hyperledger.besu.plugin.services.BlockchainService; -import org.hyperledger.besu.plugin.services.storage.DataStorageFormat; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; @Slf4j @RequiredArgsConstructor +@ExtendWith(MockitoExtension.class) public class ProfitabilityValidatorTest { public static final Address SENDER = Address.fromHexString("0x0000000000000000000000000000000000001000"); @@ -71,6 +71,9 @@ public class ProfitabilityValidatorTest { private ProfitabilityValidator profitabilityValidatorOnlyP2p; private ProfitabilityValidator profitabilityValidatorNever; + @Mock BesuConfiguration besuConfiguration; + @Mock BlockchainService blockchainService; + @BeforeEach public void initialize() { final var profitabilityConfBuilder = @@ -79,8 +82,8 @@ public void initialize() { profitabilityValidatorAlways = new ProfitabilityValidator( - new TestBesuConfiguration(), - new TestBlockchainService(), + besuConfiguration, + blockchainService, profitabilityConfBuilder .txPoolCheckP2pEnabled(true) .txPoolCheckApiEnabled(true) @@ -88,8 +91,8 @@ public void initialize() { profitabilityValidatorNever = new ProfitabilityValidator( - new TestBesuConfiguration(), - new TestBlockchainService(), + besuConfiguration, + blockchainService, profitabilityConfBuilder .txPoolCheckP2pEnabled(false) .txPoolCheckApiEnabled(false) @@ -97,8 +100,8 @@ public void initialize() { profitabilityValidatorOnlyApi = new ProfitabilityValidator( - new TestBesuConfiguration(), - new TestBlockchainService(), + besuConfiguration, + blockchainService, profitabilityConfBuilder .txPoolCheckP2pEnabled(false) .txPoolCheckApiEnabled(true) @@ -106,8 +109,8 @@ public void initialize() { profitabilityValidatorOnlyP2p = new ProfitabilityValidator( - new TestBesuConfiguration(), - new TestBlockchainService(), + besuConfiguration, + blockchainService, profitabilityConfBuilder .txPoolCheckP2pEnabled(true) .txPoolCheckApiEnabled(false) @@ -132,6 +135,8 @@ public void acceptPriorityRemoteWhenBelowMinProfitability() { @Test public void rejectRemoteWhenBelowMinProfitability() { + when(besuConfiguration.getMinGasPrice()).thenReturn(Wei.of(100_000_000)); + when(blockchainService.getNextBlockBaseFee()).thenReturn(Optional.of(Wei.of(7))); final org.hyperledger.besu.ethereum.core.Transaction transaction = org.hyperledger.besu.ethereum.core.Transaction.builder() .sender(SENDER) @@ -196,6 +201,8 @@ public void acceptRemoteWhenBelowMinProfitabilityIfCheckDisabledForP2p() { @Test public void rejectRemoteWhenBelowMinProfitabilityIfCheckEnableForP2p() { + when(besuConfiguration.getMinGasPrice()).thenReturn(Wei.of(100_000_000)); + when(blockchainService.getNextBlockBaseFee()).thenReturn(Optional.of(Wei.of(7))); final org.hyperledger.besu.ethereum.core.Transaction transaction = org.hyperledger.besu.ethereum.core.Transaction.builder() .sender(SENDER) @@ -229,6 +236,8 @@ public void acceptLocalWhenBelowMinProfitabilityIfCheckDisabledForApi() { @Test public void rejectLocalWhenBelowMinProfitabilityIfCheckEnableForApi() { + when(besuConfiguration.getMinGasPrice()).thenReturn(Wei.of(100_000_000)); + when(blockchainService.getNextBlockBaseFee()).thenReturn(Optional.of(Wei.of(7))); final org.hyperledger.besu.ethereum.core.Transaction transaction = org.hyperledger.besu.ethereum.core.Transaction.builder() .sender(SENDER) @@ -243,49 +252,4 @@ public void rejectLocalWhenBelowMinProfitabilityIfCheckEnableForApi() { .isPresent() .contains("Gas price too low"); } - - private static class TestBesuConfiguration implements BesuConfiguration { - @Override - public Path getStoragePath() { - throw new UnsupportedOperationException(); - } - - @Override - public Path getDataPath() { - throw new UnsupportedOperationException(); - } - - @Override - public DataStorageFormat getDatabaseFormat() { - throw new UnsupportedOperationException(); - } - - @Override - public Wei getMinGasPrice() { - return Wei.of(100_000_000); - } - } - - private static class TestBlockchainService implements BlockchainService { - - @Override - public Optional getBlockByNumber(final long l) { - throw new UnsupportedOperationException(); - } - - @Override - public Hash getChainHeadHash() { - throw new UnsupportedOperationException(); - } - - @Override - public BlockHeader getChainHeadHeader() { - throw new UnsupportedOperationException(); - } - - @Override - public Optional getNextBlockBaseFee() { - return Optional.of(Wei.of(7)); - } - } }