diff --git a/gradle.properties b/gradle.properties index 7073103864..b56a7fa1bc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ version=0.0.1-SNAPSHOT -besuVersion=23.7.4-SNAPSHOT +besuVersion=23.10.1-SNAPSHOT besuArtifactGroup=io.consensys.linea-besu distributionIdentifier=linea-besu-plugin distributionBaseUrl=https://artifacts.consensys.net/public/linea-besu/raw/names/linea-besu.tar.gz/versions/ \ No newline at end of file diff --git a/src/main/java/net/consensys/linea/sequencer/LineaCliOptions.java b/src/main/java/net/consensys/linea/sequencer/LineaCliOptions.java index 736e8b89c0..9759548355 100644 --- a/src/main/java/net/consensys/linea/sequencer/LineaCliOptions.java +++ b/src/main/java/net/consensys/linea/sequencer/LineaCliOptions.java @@ -34,7 +34,7 @@ public class LineaCliOptions { "Maximum size for the calldata of a Transaction (default: " + DEFAULT_MAX_TX_CALLDATA_SIZE + ")") - private int maxTxCalldataSize = DEFAULT_MAX_TX_CALLDATA_SIZE; + private int maxTxCallDataSize = DEFAULT_MAX_TX_CALLDATA_SIZE; @CommandLine.Option( names = {MAX_BLOCK_CALLDATA_SIZE}, @@ -44,7 +44,7 @@ public class LineaCliOptions { "Maximum size for the calldata of a Block (default: " + DEFAULT_MAX_BLOCK_CALLDATA_SIZE + ")") - private int maxBlockCalldataSize = DEFAULT_MAX_BLOCK_CALLDATA_SIZE; + private int maxBlockCallDataSize = DEFAULT_MAX_BLOCK_CALLDATA_SIZE; private LineaCliOptions() {} @@ -65,9 +65,8 @@ public static LineaCliOptions create() { */ public static LineaCliOptions fromConfig(final LineaConfiguration config) { final LineaCliOptions options = create(); - options.maxTxCalldataSize = config.maxTxCalldataSize(); - options.maxBlockCalldataSize = config.maxBlockCalldataSize(); - + options.maxTxCallDataSize = config.maxTxCallDataSize(); + options.maxBlockCallDataSize = config.maxBlockCallDataSize(); return options; } @@ -77,14 +76,17 @@ public static LineaCliOptions fromConfig(final LineaConfiguration config) { * @return the Linea factory configuration */ public LineaConfiguration toDomainObject() { - return new LineaConfiguration(maxTxCalldataSize, maxBlockCalldataSize); + return new LineaConfiguration.Builder() + .maxTxCallDataSize(maxTxCallDataSize) + .maxBlockCallDataSize(maxBlockCallDataSize) + .build(); } @Override public String toString() { return MoreObjects.toStringHelper(this) - .add(MAX_TX_CALLDATA_SIZE, maxTxCalldataSize) - .add(MAX_BLOCK_CALLDATA_SIZE, maxBlockCalldataSize) + .add(MAX_TX_CALLDATA_SIZE, maxTxCallDataSize) + .add(MAX_BLOCK_CALLDATA_SIZE, maxBlockCallDataSize) .toString(); } } diff --git a/src/main/java/net/consensys/linea/sequencer/LineaConfiguration.java b/src/main/java/net/consensys/linea/sequencer/LineaConfiguration.java index 223b29bccc..84dbfcefe7 100644 --- a/src/main/java/net/consensys/linea/sequencer/LineaConfiguration.java +++ b/src/main/java/net/consensys/linea/sequencer/LineaConfiguration.java @@ -15,10 +15,40 @@ package net.consensys.linea.sequencer; -/** - * The Linea configuration. - * - * @param maxTxCalldataSize the maximum calldata size for a transaction. - * @param maxBlockCalldataSize the maximum calldata size for a block. - */ -public record LineaConfiguration(int maxTxCalldataSize, int maxBlockCalldataSize) {} +/** The Linea configuration. */ +public final class LineaConfiguration { + private final int maxTxCallDataSize; + private final int maxBlockCallDataSize; + + private LineaConfiguration(int maxTxCallDataSize, int maxBlockCallDataSize) { + this.maxTxCallDataSize = maxTxCallDataSize; + this.maxBlockCallDataSize = maxBlockCallDataSize; + } + + public int maxTxCallDataSize() { + return maxTxCallDataSize; + } + + public int maxBlockCallDataSize() { + return maxBlockCallDataSize; + } + + public static class Builder { + private int maxTxCallDataSize; + private int maxBlockCallDataSize; + + public Builder maxTxCallDataSize(int maxTxCallDataSize) { + this.maxTxCallDataSize = maxTxCallDataSize; + return this; + } + + public Builder maxBlockCallDataSize(int maxBlockCallDataSize) { + this.maxBlockCallDataSize = maxBlockCallDataSize; + return this; + } + + public LineaConfiguration build() { + return new LineaConfiguration(maxTxCallDataSize, maxBlockCallDataSize); + } + } +} diff --git a/src/main/java/net/consensys/linea/sequencer/LineaTransactionSelector.java b/src/main/java/net/consensys/linea/sequencer/LineaTransactionSelector.java deleted file mode 100644 index d49037dac9..0000000000 --- a/src/main/java/net/consensys/linea/sequencer/LineaTransactionSelector.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright ConsenSys AG. - * - * 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 lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.hyperledger.besu.datatypes.PendingTransaction; -import org.hyperledger.besu.datatypes.Transaction; -import org.hyperledger.besu.plugin.data.TransactionSelectionResult; -import org.hyperledger.besu.plugin.services.txselection.TransactionSelector; - -/** - * Represents an implementation of a transaction selector which decides if a transaction is to be - * added to a block. - */ -@Slf4j -@RequiredArgsConstructor -public class LineaTransactionSelector implements TransactionSelector { - private final int maxTxCalldataSize; - private final int maxBlockCalldataSize; - private int blockCalldataSum; - - @Override - public TransactionSelectionResult evaluateTransactionPreProcessing( - final PendingTransaction pendingTransaction) { - final Transaction transaction = pendingTransaction.getTransaction(); - - final int txCalldataSize = transaction.getPayload().size(); - - if (txCalldataSize > maxTxCalldataSize) { - log.warn( - "Not adding transaction {} because calldata size {} is too big", - transaction, - txCalldataSize); - return TransactionSelectionResult.invalid("Calldata too big"); - } - try { - blockCalldataSum = Math.addExact(blockCalldataSum, txCalldataSize); - } catch (final ArithmeticException e) { - // this should never happen - log.warn( - "Not adding transaction {} otherwise block calldata size {} overflows", - transaction, - blockCalldataSum); - return TransactionSelectionResult.BLOCK_FULL; - } - if (blockCalldataSum > maxBlockCalldataSize) { - return TransactionSelectionResult.BLOCK_FULL; - } - - return TransactionSelectionResult.SELECTED; - } -} diff --git a/src/main/java/net/consensys/linea/sequencer/LineaTransactionSelectorFactory.java b/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorFactory.java similarity index 79% rename from src/main/java/net/consensys/linea/sequencer/LineaTransactionSelectorFactory.java rename to src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorFactory.java index 80fec452b6..829b8ead59 100644 --- a/src/main/java/net/consensys/linea/sequencer/LineaTransactionSelectorFactory.java +++ b/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorFactory.java @@ -13,8 +13,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -package net.consensys.linea.sequencer; +package net.consensys.linea.sequencer.txselection; +import net.consensys.linea.sequencer.LineaCliOptions; +import net.consensys.linea.sequencer.LineaConfiguration; +import net.consensys.linea.sequencer.txselection.selectors.LineaTransactionSelector; import org.hyperledger.besu.plugin.services.txselection.TransactionSelector; import org.hyperledger.besu.plugin.services.txselection.TransactionSelectorFactory; @@ -29,7 +32,6 @@ public LineaTransactionSelectorFactory(final LineaCliOptions options) { @Override public TransactionSelector create() { final LineaConfiguration lineaConfiguration = options.toDomainObject(); - return new LineaTransactionSelector( - lineaConfiguration.maxTxCalldataSize(), lineaConfiguration.maxBlockCalldataSize()); + return new LineaTransactionSelector(lineaConfiguration); } } diff --git a/src/main/java/net/consensys/linea/sequencer/LineaTransactionSelectorPlugin.java b/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorPlugin.java similarity index 95% rename from src/main/java/net/consensys/linea/sequencer/LineaTransactionSelectorPlugin.java rename to src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorPlugin.java index 7b7d9eaedd..78a52a07c0 100644 --- a/src/main/java/net/consensys/linea/sequencer/LineaTransactionSelectorPlugin.java +++ b/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorPlugin.java @@ -13,12 +13,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -package net.consensys.linea.sequencer; +package net.consensys.linea.sequencer.txselection; import java.util.Optional; import com.google.auto.service.AutoService; import lombok.extern.slf4j.Slf4j; +import net.consensys.linea.sequencer.LineaCliOptions; import org.hyperledger.besu.plugin.BesuContext; import org.hyperledger.besu.plugin.BesuPlugin; import org.hyperledger.besu.plugin.services.PicoCLIOptions; diff --git a/src/main/java/net/consensys/linea/sequencer/txselection/selectors/LineaTransactionSelector.java b/src/main/java/net/consensys/linea/sequencer/txselection/selectors/LineaTransactionSelector.java new file mode 100644 index 0000000000..b462d22542 --- /dev/null +++ b/src/main/java/net/consensys/linea/sequencer/txselection/selectors/LineaTransactionSelector.java @@ -0,0 +1,114 @@ +/* + * Copyright ConsenSys AG. + * + * 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.List; + +import net.consensys.linea.sequencer.LineaConfiguration; +import org.hyperledger.besu.datatypes.PendingTransaction; +import org.hyperledger.besu.plugin.data.TransactionProcessingResult; +import org.hyperledger.besu.plugin.data.TransactionSelectionResult; +import org.hyperledger.besu.plugin.services.txselection.TransactionSelector; + +/** Class for transaction selection using a list of selectors. */ +public class LineaTransactionSelector implements TransactionSelector { + + final LineaConfiguration lineaConfiguration; + List selectors; + + public LineaTransactionSelector(LineaConfiguration lineaConfiguration) { + this.lineaConfiguration = lineaConfiguration; + this.selectors = createTransactionSelectors(lineaConfiguration); + } + + /** + * Creates a list of selectors based on Linea configuration. + * + * @param lineaConfiguration The configuration to use. + * @return A list of selectors. + */ + private static List createTransactionSelectors( + final LineaConfiguration lineaConfiguration) { + return List.of( + new MaxTransactionCallDataTransactionSelector(lineaConfiguration.maxTxCallDataSize()), + new MaxBlockCallDataTransactionSelector(lineaConfiguration.maxBlockCallDataSize())); + } + + /** + * Evaluates a transaction before processing using all selectors. Stops if any selector doesn't + * select the transaction. + * + * @param pendingTransaction The transaction to evaluate. + * @return The first non-SELECTED result or SELECTED if all selectors select the transaction. + */ + @Override + public TransactionSelectionResult evaluateTransactionPreProcessing( + PendingTransaction pendingTransaction) { + for (var selector : selectors) { + TransactionSelectionResult result = + selector.evaluateTransactionPreProcessing(pendingTransaction); + if (!result.equals(TransactionSelectionResult.SELECTED)) { + return result; + } + } + return TransactionSelectionResult.SELECTED; + } + + /** + * Evaluates a transaction considering its processing result. Stops if any selector doesn't select + * the transaction. + * + * @param pendingTransaction The processed transaction. + * @param processingResult The result of the transaction processing. + * @return The first non-SELECTED result or SELECTED if all selectors select the transaction. + */ + @Override + public TransactionSelectionResult evaluateTransactionPostProcessing( + PendingTransaction pendingTransaction, TransactionProcessingResult processingResult) { + for (var selector : selectors) { + TransactionSelectionResult result = + selector.evaluateTransactionPostProcessing(pendingTransaction, processingResult); + if (!result.equals(TransactionSelectionResult.SELECTED)) { + return result; + } + } + return TransactionSelectionResult.SELECTED; + } + + /** + * Notifies all selectors when a transaction is selected. + * + * @param pendingTransaction The selected transaction. + */ + @Override + public void onTransactionSelected(final PendingTransaction pendingTransaction) { + selectors.forEach(selector -> selector.onTransactionSelected(pendingTransaction)); + } + + /** + * Notifies all selectors when a transaction is not selected. + * + * @param pendingTransaction The non-selected transaction. + * @param transactionSelectionResult The reason for not selecting the transaction. + */ + @Override + public void onTransactionNotSelected( + final PendingTransaction pendingTransaction, + final TransactionSelectionResult transactionSelectionResult) { + selectors.forEach( + selector -> + selector.onTransactionNotSelected(pendingTransaction, transactionSelectionResult)); + } +} diff --git a/src/main/java/net/consensys/linea/sequencer/txselection/selectors/MaxBlockCallDataTransactionSelector.java b/src/main/java/net/consensys/linea/sequencer/txselection/selectors/MaxBlockCallDataTransactionSelector.java new file mode 100644 index 0000000000..2ed172ae26 --- /dev/null +++ b/src/main/java/net/consensys/linea/sequencer/txselection/selectors/MaxBlockCallDataTransactionSelector.java @@ -0,0 +1,103 @@ +/* + * Copyright ConsenSys AG. + * + * 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 lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.hyperledger.besu.datatypes.PendingTransaction; +import org.hyperledger.besu.datatypes.Transaction; +import org.hyperledger.besu.plugin.data.TransactionProcessingResult; +import org.hyperledger.besu.plugin.data.TransactionSelectionResult; +import org.hyperledger.besu.plugin.services.txselection.TransactionSelector; + +/** + * This class implements TransactionSelector and provides a specific implementation for evaluating + * transactions based on the size of the call data. It checks if adding a transaction to the block + * pushes the call data size of the block over the limit. + */ +@Slf4j +@RequiredArgsConstructor +public class MaxBlockCallDataTransactionSelector implements TransactionSelector { + + private final int maxBlockCallDataSize; + private int blockCallDataSize; + + /** + * Evaluates a transaction before processing. Checks if adding the transaction to the block pushes + * the call data size of the block over the limit. + * + * @param pendingTransaction The transaction to evaluate. + * @return BLOCK_FULL if the call data size of a transactions pushes the size for the block over + * the limit, otherwise SELECTED. + */ + @Override + public TransactionSelectionResult evaluateTransactionPreProcessing( + final PendingTransaction pendingTransaction) { + + final Transaction transaction = pendingTransaction.getTransaction(); + final int transactionCallDataSize = transaction.getPayload().size(); + + if (isTransactionExceedingBlockCallDataSizeLimit(transactionCallDataSize)) { + log.trace( + "BlockCallData {} greater than {}, completing operation", + transactionCallDataSize, + maxBlockCallDataSize); + return TransactionSelectionResult.BLOCK_FULL; + } + return TransactionSelectionResult.SELECTED; + } + + /** + * Checks if the total call data size of all transactions in a block would exceed the maximum + * allowed size if the given transaction were added. + * + * @param transactionCallDataSize The call data size of the transaction. + * @return true if the total call data size would be too big, false otherwise. + */ + private boolean isTransactionExceedingBlockCallDataSizeLimit(int transactionCallDataSize) { + try { + return Math.addExact(blockCallDataSize, transactionCallDataSize) > maxBlockCallDataSize; + } catch (final ArithmeticException e) { + // Overflow won't occur as blockCallDataSize won't exceed Integer.MAX_VALUE + return true; + } + } + + /** + * Updates the total call data size of all transactions in a block when a transaction is selected. + * + * @param pendingTransaction The selected transaction. + */ + @Override + public void onTransactionSelected(PendingTransaction pendingTransaction) { + final int transactionCallDataSize = pendingTransaction.getTransaction().getPayload().size(); + blockCallDataSize = Math.addExact(blockCallDataSize, transactionCallDataSize); + } + + /** + * No evaluation is performed post-processing. + * + * @param pendingTransaction The processed transaction. + * @param processingResult The result of the transaction processing. + * @return Always returns SELECTED. + */ + @Override + public TransactionSelectionResult evaluateTransactionPostProcessing( + final PendingTransaction pendingTransaction, + final TransactionProcessingResult processingResult) { + // Evaluation done in pre-processing, no action needed here. + return TransactionSelectionResult.SELECTED; + } +} diff --git a/src/main/java/net/consensys/linea/sequencer/txselection/selectors/MaxTransactionCallDataTransactionSelector.java b/src/main/java/net/consensys/linea/sequencer/txselection/selectors/MaxTransactionCallDataTransactionSelector.java new file mode 100644 index 0000000000..a41fca43c6 --- /dev/null +++ b/src/main/java/net/consensys/linea/sequencer/txselection/selectors/MaxTransactionCallDataTransactionSelector.java @@ -0,0 +1,74 @@ +/* + * Copyright ConsenSys AG. + * + * 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 lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.hyperledger.besu.datatypes.PendingTransaction; +import org.hyperledger.besu.datatypes.Transaction; +import org.hyperledger.besu.plugin.data.TransactionProcessingResult; +import org.hyperledger.besu.plugin.data.TransactionSelectionResult; +import org.hyperledger.besu.plugin.services.txselection.TransactionSelector; + +/** + * This class implements TransactionSelector and is used to select transactions based on their call + * data size. If the call data size of a transaction exceeds the maximum limit, the transaction is + * not selected. The maximum limit for the call data size is defined in the LineaConfiguration. + */ +@Slf4j +@RequiredArgsConstructor +public class MaxTransactionCallDataTransactionSelector implements TransactionSelector { + + private final int maxTxCallDataSize; + /** The reason for invalidation if the call data size is too big. */ + public static String CALL_DATA_TOO_BIG_INVALID_REASON = "Transaction Call Data is too big"; + + /** + * Evaluates a pending transaction based on its call data size. + * + * @param pendingTransaction The transaction to be evaluated. + * @return The result of the transaction selection. + */ + @Override + public TransactionSelectionResult evaluateTransactionPreProcessing( + final PendingTransaction pendingTransaction) { + final Transaction transaction = pendingTransaction.getTransaction(); + + var transactionCallDataSize = transaction.getPayload().size(); + if (transactionCallDataSize > maxTxCallDataSize) { + log.warn( + "Not adding transaction {} because callData size {} is too big", + transaction, + transactionCallDataSize); + return TransactionSelectionResult.invalid(CALL_DATA_TOO_BIG_INVALID_REASON); + } + return TransactionSelectionResult.SELECTED; + } + + /** + * No evaluation is performed post-processing. + * + * @param pendingTransaction The processed transaction. + * @param processingResult The result of the transaction processing. + * @return Always returns SELECTED. + */ + @Override + public TransactionSelectionResult evaluateTransactionPostProcessing( + final PendingTransaction pendingTransaction, + final TransactionProcessingResult processingResult) { + // Evaluation done in pre-processing, no action needed here. + return TransactionSelectionResult.SELECTED; + } +} diff --git a/src/test/java/net/consensys/linea/sequencer/txselection/MaxBlockCallDataSizeTransactionSelectorTest.java b/src/test/java/net/consensys/linea/sequencer/txselection/MaxBlockCallDataSizeTransactionSelectorTest.java new file mode 100644 index 0000000000..73cd577c0f --- /dev/null +++ b/src/test/java/net/consensys/linea/sequencer/txselection/MaxBlockCallDataSizeTransactionSelectorTest.java @@ -0,0 +1,113 @@ +/* + * Copyright ConsenSys AG. + * + * 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; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import net.consensys.linea.sequencer.txselection.selectors.MaxBlockCallDataTransactionSelector; +import org.apache.tuweni.bytes.Bytes; +import org.hyperledger.besu.datatypes.PendingTransaction; +import org.hyperledger.besu.datatypes.Transaction; +import org.hyperledger.besu.plugin.data.TransactionSelectionResult; +import org.hyperledger.besu.plugin.services.txselection.TransactionSelector; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class MaxBlockCallDataSizeTransactionSelectorTest { + private static final int BLOCK_CALL_DATA_MAX_SIZE = 100; + private static final int BLOCK_CALL_DATA_HALF_SIZE = 50; + private static final int TX_CALL_DATA_SIZE = BLOCK_CALL_DATA_HALF_SIZE + 1; + private TransactionSelector transactionSelector; + + @BeforeEach + public void initialize() { + transactionSelector = new MaxBlockCallDataTransactionSelector(BLOCK_CALL_DATA_MAX_SIZE); + } + + @Test + public void shouldSelectTransactionWhen_BlockCallDataSize_IsLessThan_MaxBlockCallDataSize() { + var mockTransaction = mockTransactionOfCallDataSize(BLOCK_CALL_DATA_HALF_SIZE); + var mockTransaction2 = mockTransactionOfCallDataSize(BLOCK_CALL_DATA_HALF_SIZE - 1); + verifyTransactionSelection( + transactionSelector, mockTransaction, TransactionSelectionResult.SELECTED); + verifyTransactionSelection( + transactionSelector, mockTransaction2, TransactionSelectionResult.SELECTED); + } + + @Test + public void shouldSelectTransactionWhen_BlockCallDataSize_IsEqualTo_MaxBlockCallDataSize() { + var mockTransaction = mockTransactionOfCallDataSize(BLOCK_CALL_DATA_HALF_SIZE); + var mockTransaction2 = mockTransactionOfCallDataSize(BLOCK_CALL_DATA_HALF_SIZE); + verifyTransactionSelection( + transactionSelector, mockTransaction, TransactionSelectionResult.SELECTED); + verifyTransactionSelection( + transactionSelector, mockTransaction2, TransactionSelectionResult.SELECTED); + } + + @Test + public void + shouldNotSelectTransactionWhen_BlockCallDataSize_IsGreaterThan_MaxBlockCallDataSize() { + var mockTransaction = mockTransactionOfCallDataSize(BLOCK_CALL_DATA_HALF_SIZE); + var mockTransaction2 = mockTransactionOfCallDataSize(BLOCK_CALL_DATA_HALF_SIZE + 1); + verifyTransactionSelection( + transactionSelector, mockTransaction, TransactionSelectionResult.SELECTED); + verifyTransactionSelection( + transactionSelector, mockTransaction2, TransactionSelectionResult.BLOCK_FULL); + } + + @Test + public void shouldNotSelectAdditionalTransactionOnceBlockIsFull() { + var firstTransaction = mockTransactionOfCallDataSize(TX_CALL_DATA_SIZE); + var secondTransaction = mockTransactionOfCallDataSize(TX_CALL_DATA_SIZE); + var thirdTransaction = mockTransactionOfCallDataSize(TX_CALL_DATA_SIZE); + + verifyTransactionSelection( + transactionSelector, firstTransaction, TransactionSelectionResult.SELECTED); + verifyTransactionSelection( + transactionSelector, secondTransaction, TransactionSelectionResult.BLOCK_FULL); + verifyTransactionSelection( + transactionSelector, thirdTransaction, TransactionSelectionResult.BLOCK_FULL); + } + + private void verifyTransactionSelection( + final TransactionSelector selector, + final PendingTransaction transaction, + final TransactionSelectionResult expectedSelectionResult) { + var selectionResult = selector.evaluateTransactionPreProcessing(transaction); + assertThat(selectionResult).isEqualTo(expectedSelectionResult); + notifySelector(selector, transaction, selectionResult); + } + + private void notifySelector( + final TransactionSelector selector, + final PendingTransaction transaction, + final TransactionSelectionResult selectionResult) { + if (selectionResult.equals(TransactionSelectionResult.SELECTED)) { + selector.onTransactionSelected(transaction); + } else { + selector.onTransactionNotSelected(transaction, selectionResult); + } + } + + private PendingTransaction mockTransactionOfCallDataSize(final int size) { + PendingTransaction mockTransaction = mock(PendingTransaction.class); + Transaction transaction = mock(Transaction.class); + when(mockTransaction.getTransaction()).thenReturn(transaction); + when(transaction.getPayload()).thenReturn(Bytes.repeat((byte) 1, size)); + return mockTransaction; + } +} diff --git a/src/test/java/net/consensys/linea/sequencer/txselection/MaxTransactionCallDataSizeTransactionSelectorTest.java b/src/test/java/net/consensys/linea/sequencer/txselection/MaxTransactionCallDataSizeTransactionSelectorTest.java new file mode 100644 index 0000000000..78cc532657 --- /dev/null +++ b/src/test/java/net/consensys/linea/sequencer/txselection/MaxTransactionCallDataSizeTransactionSelectorTest.java @@ -0,0 +1,78 @@ +/* + * Copyright ConsenSys AG. + * + * 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; + +import static net.consensys.linea.sequencer.txselection.selectors.MaxTransactionCallDataTransactionSelector.CALL_DATA_TOO_BIG_INVALID_REASON; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import net.consensys.linea.sequencer.txselection.selectors.MaxTransactionCallDataTransactionSelector; +import org.apache.tuweni.bytes.Bytes; +import org.hyperledger.besu.datatypes.PendingTransaction; +import org.hyperledger.besu.datatypes.Transaction; +import org.hyperledger.besu.plugin.data.TransactionSelectionResult; +import org.hyperledger.besu.plugin.services.txselection.TransactionSelector; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class MaxTransactionCallDataSizeTransactionSelectorTest { + private static final int MAX_TX_CALL_DATA_SIZE = 10; + private TransactionSelector transactionSelector; + + @BeforeEach + public void initialize() { + transactionSelector = new MaxTransactionCallDataTransactionSelector(MAX_TX_CALL_DATA_SIZE); + } + + @Test + public void shouldSelectTransactionWhen_CallDataSize_IsLessThan_MaxTxCallDataSize() { + var mockTransaction = mockTransactionOfCallDataSize(MAX_TX_CALL_DATA_SIZE - 1); + verifyTransactionSelection( + transactionSelector, mockTransaction, TransactionSelectionResult.SELECTED); + } + + @Test + public void shouldSelectTransactionWhen_CallDataSize_IsEqualTo_MaxTxCallDataSize() { + var mockTransaction = mockTransactionOfCallDataSize(MAX_TX_CALL_DATA_SIZE); + verifyTransactionSelection( + transactionSelector, mockTransaction, TransactionSelectionResult.SELECTED); + } + + @Test + public void shouldNotSelectTransactionWhen_CallDataSize_IsGreaterThan_MaxTxCallDataSize() { + var mockTransaction = mockTransactionOfCallDataSize(MAX_TX_CALL_DATA_SIZE + 1); + verifyTransactionSelection( + transactionSelector, + mockTransaction, + TransactionSelectionResult.invalid(CALL_DATA_TOO_BIG_INVALID_REASON)); + } + + private void verifyTransactionSelection( + final TransactionSelector selector, + final PendingTransaction transaction, + final TransactionSelectionResult expectedSelectionResult) { + var selectionResult = selector.evaluateTransactionPreProcessing(transaction); + assertThat(selectionResult).isEqualTo(expectedSelectionResult); + } + + private PendingTransaction mockTransactionOfCallDataSize(final int size) { + PendingTransaction mockTransaction = mock(PendingTransaction.class); + Transaction transaction = mock(Transaction.class); + when(mockTransaction.getTransaction()).thenReturn(transaction); + when(transaction.getPayload()).thenReturn(Bytes.repeat((byte) 1, size)); + return mockTransaction; + } +}