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..99746546 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,12 @@ 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,
+ 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..f011e4ea
--- /dev/null
+++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidator.java
@@ -0,0 +1,116 @@
+/*
+ * 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.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 Map moduleLineLimitsMap;
+ private final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration;
+
+ public SimulationValidator(
+ final BlockchainService blockchainService,
+ final TransactionSimulationService transactionSimulationService,
+ final Map moduleLineLimitsMap,
+ final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration) {
+ this.blockchainService = blockchainService;
+ this.transactionSimulationService = transactionSimulationService;
+ this.moduleLineLimitsMap = moduleLineLimitsMap;
+ this.l1L2BridgeConfiguration = l1L2BridgeConfiguration;
+ }
+
+ @Override
+ public Optional validateTransaction(
+ final Transaction transaction, final boolean isLocal, final boolean hasPriority) {
+
+ 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..5fff8c7c
--- /dev/null
+++ b/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidatorTest.java
@@ -0,0 +1,159 @@
+/*
+ * 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.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) {
+ return new SimulationValidator(
+ blockchainService,
+ transactionSimulationService,
+ lineCountLimits,
+ LineaL1L2BridgeConfiguration.builder()
+ .contract(BRIDGE_CONTRACT)
+ .topic(BRIDGE_LOG_TOPIC)
+ .build());
+ }
+
+ @Test
+ public void successfulTransactionIsValid() {
+ final var simulationValidator = createSimulationValidator(lineCountLimits);
+ 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, false, true)).isEmpty();
+ }
+
+ @Test
+ public void moduleLineCountOverflowTransactionIsInvalid() {
+ lineCountLimits.put("ADD", 1);
+ final var simulationValidator = createSimulationValidator(lineCountLimits);
+ 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, false, true))
+ .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();
}