From e8a0428a2774229329bbb1b1113f818dae902651 Mon Sep 17 00:00:00 2001 From: Gabriel-Trintinalia Date: Tue, 10 Oct 2023 13:02:41 +1100 Subject: [PATCH] [Plugin API] - TransactionSelector - Notify plugins when transaction is selected/rejected (#6005) Signed-off-by: Gabriel-Trintinalia --- .../txselection/BlockTransactionSelector.java | 35 +++++++++++++--- .../AbstractBlockTransactionSelectorTest.java | 42 +++++++++++++++++++ plugin-api/build.gradle | 2 +- .../txselection/TransactionSelector.java | 13 ++++++ 4 files changed, 85 insertions(+), 7 deletions(-) diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java index 580b5be8bf..f55ea96364 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java @@ -145,8 +145,7 @@ public class BlockTransactionSelector { pendingTransaction -> { final var res = evaluateTransaction(pendingTransaction); if (!res.selected()) { - transactionSelectionResults.updateNotSelected( - pendingTransaction.getTransaction(), res); + updateTransactionRejected(pendingTransaction, res); } return res; }); @@ -169,9 +168,10 @@ public class BlockTransactionSelector { public TransactionSelectionResults evaluateTransactions(final List transactions) { transactions.forEach( transaction -> { - final var res = evaluateTransaction(new PendingTransaction.Local(transaction)); + var pendingTransaction = new PendingTransaction.Local(transaction); + final var res = evaluateTransaction(pendingTransaction); if (!res.selected()) { - transactionSelectionResults.updateNotSelected(transaction, res); + updateTransactionRejected(pendingTransaction, res); } }); return transactionSelectionResults; @@ -234,8 +234,7 @@ public class BlockTransactionSelector { final long blobGasUsed = blockSelectionContext.gasCalculator().blobGasCost(transaction.getBlobCount()); - transactionSelectionResults.updateSelected( - transaction, receipt, gasUsedByTransaction, blobGasUsed); + updateTransactionSelected(pendingTransaction, receipt, gasUsedByTransaction, blobGasUsed); LOG.atTrace() .setMessage("Selected {} for block creation") @@ -245,6 +244,30 @@ public class BlockTransactionSelector { return TransactionSelectionResult.SELECTED; } + private void updateTransactionSelected( + final PendingTransaction pendingTransaction, + final TransactionReceipt receipt, + final long gasUsedByTransaction, + final long blobGasUsed) { + + transactionSelectionResults.updateSelected( + pendingTransaction.getTransaction(), receipt, gasUsedByTransaction, blobGasUsed); + + // notify external selector if any + externalTransactionSelector.onTransactionSelected(pendingTransaction); + } + + private void updateTransactionRejected( + final PendingTransaction pendingTransaction, + final TransactionSelectionResult processingResult) { + + transactionSelectionResults.updateNotSelected( + pendingTransaction.getTransaction(), processingResult); + + // notify external selector if any + externalTransactionSelector.onTransactionRejected(pendingTransaction); + } + /** * This method evaluates a transaction by pre-processing it through a series of selectors. It * first processes the transaction through internal selectors, and if the transaction is selected, diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java index 15b99b3461..b17eb8b524 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java @@ -19,6 +19,9 @@ import static org.assertj.core.api.Assertions.entry; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.hyperledger.besu.config.GenesisConfigFile; @@ -33,6 +36,7 @@ import org.hyperledger.besu.ethereum.GasLimitCalculator; import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockTransactionSelector; import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionSelectionResults; +import org.hyperledger.besu.ethereum.blockcreation.txselection.selectors.AllAcceptingTransactionSelector; import org.hyperledger.besu.ethereum.chain.DefaultBlockchain; import org.hyperledger.besu.ethereum.chain.GenesisState; import org.hyperledger.besu.ethereum.chain.MutableBlockchain; @@ -84,6 +88,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Answers; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; @@ -659,6 +664,43 @@ public abstract class AbstractBlockTransactionSelectorTest { .containsOnly(entry(notSelected, TransactionSelectionResult.invalidTransient("Invalid"))); } + @Test + public void transactionSelectionPluginShouldBeNotifiedWhenTransactionSelectionCompletes() { + final TransactionSelectorFactory transactionSelectorFactory = + mock(TransactionSelectorFactory.class); + TransactionSelector transactionSelector = spy(AllAcceptingTransactionSelector.INSTANCE); + when(transactionSelectorFactory.create()).thenReturn(transactionSelector); + + final Transaction transaction = createTransaction(0, Wei.of(10), 21_000); + ensureTransactionIsValid(transaction, 21_000, 0); + + final Transaction invalidTransaction = createTransaction(1, Wei.of(10), 21_000); + ensureTransactionIsInvalid( + invalidTransaction, TransactionInvalidReason.PLUGIN_TX_VALIDATOR_INVALIDATED); + transactionPool.addRemoteTransactions(List.of(transaction, invalidTransaction)); + + createBlockSelectorWithTxSelPlugin( + transactionProcessor, + createBlock(300_000), + Wei.ZERO, + AddressHelpers.ofValue(1), + Wei.ZERO, + MIN_OCCUPANCY_80_PERCENT, + transactionSelectorFactory) + .buildTransactionListForBlock(); + + ArgumentCaptor argumentCaptor = + ArgumentCaptor.forClass(PendingTransaction.class); + + verify(transactionSelector).onTransactionSelected(argumentCaptor.capture()); + PendingTransaction selected = argumentCaptor.getValue(); + assertThat(selected.getTransaction()).isEqualTo(transaction); + + verify(transactionSelector).onTransactionRejected(argumentCaptor.capture()); + PendingTransaction rejectedTransaction = argumentCaptor.getValue(); + assertThat(rejectedTransaction.getTransaction()).isEqualTo(invalidTransaction); + } + @Test public void transactionWithIncorrectNonceRemainsInPoolAndNotSelected() { final ProcessableBlockHeader blockHeader = createBlock(5_000_000); diff --git a/plugin-api/build.gradle b/plugin-api/build.gradle index c4111a633d..231fd953d3 100644 --- a/plugin-api/build.gradle +++ b/plugin-api/build.gradle @@ -69,7 +69,7 @@ Calculated : ${currentHash} tasks.register('checkAPIChanges', FileStateChecker) { description = "Checks that the API for the Plugin-API project does not change without deliberate thought" files = sourceSets.main.allJava.files - knownHash = '8NVdDoCnMQiibEZUsZuemZU+wm3W1LPklcQkiKsriKs=' + knownHash = '+7wo9cABKEFyYvjtpDFAOXqVKBAkffdnb433hT0VQ7I=' } check.dependsOn('checkAPIChanges') diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/TransactionSelector.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/TransactionSelector.java index 76d1a4b2db..87ec0471bb 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/TransactionSelector.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/TransactionSelector.java @@ -43,4 +43,17 @@ public interface TransactionSelector { */ TransactionSelectionResult evaluateTransactionPostProcessing( PendingTransaction pendingTransaction, TransactionProcessingResult processingResult); + + /** + * Method called when a transaction is selected to be added to a block. + * + * @param pendingTransaction The transaction that has been selected. + */ + default void onTransactionSelected(final PendingTransaction pendingTransaction) {} + /** + * Method called when a transaction is rejected to be added to a block. + * + * @param pendingTransaction The transaction that has been rejected. + */ + default void onTransactionRejected(final PendingTransaction pendingTransaction) {} }