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 4a0707e76f7..944cf426d73 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 @@ -228,26 +228,42 @@ private TransactionSelectionResult evaluateTransaction( final PendingTransaction pendingTransaction) { checkCancellation(); - final Stopwatch evaluationTimer = Stopwatch.createStarted(); + final TransactionEvaluationContext evaluationContext = + createTransactionEvaluationContext(pendingTransaction); - TransactionSelectionResult selectionResult = evaluatePreProcessing(pendingTransaction); + TransactionSelectionResult selectionResult = evaluatePreProcessing(evaluationContext); if (!selectionResult.selected()) { - return handleTransactionNotSelected(pendingTransaction, selectionResult, evaluationTimer); + return handleTransactionNotSelected(evaluationContext, selectionResult); } final WorldUpdater txWorldStateUpdater = blockWorldStateUpdater.updater(); final TransactionProcessingResult processingResult = processTransaction(pendingTransaction, txWorldStateUpdater); - var postProcessingSelectionResult = - evaluatePostProcessing(pendingTransaction, processingResult); + var postProcessingSelectionResult = evaluatePostProcessing(evaluationContext, processingResult); if (postProcessingSelectionResult.selected()) { - return handleTransactionSelected( - pendingTransaction, processingResult, txWorldStateUpdater, evaluationTimer); + return handleTransactionSelected(evaluationContext, processingResult, txWorldStateUpdater); } return handleTransactionNotSelected( - pendingTransaction, postProcessingSelectionResult, txWorldStateUpdater, evaluationTimer); + evaluationContext, postProcessingSelectionResult, txWorldStateUpdater); + } + + private TransactionEvaluationContext createTransactionEvaluationContext( + final PendingTransaction pendingTransaction) { + final Wei transactionGasPriceInBlock = + blockSelectionContext + .feeMarket() + .getTransactionPriceCalculator() + .price( + pendingTransaction.getTransaction(), + blockSelectionContext.processableBlockHeader().getBaseFee()); + + return new TransactionEvaluationContext( + pendingTransaction, + Stopwatch.createStarted(), + transactionGasPriceInBlock, + blockSelectionContext.miningParameters().getMinTransactionGasPrice()); } /** @@ -256,21 +272,20 @@ private TransactionSelectionResult evaluateTransaction( * it then processes it through external selectors. If the transaction is selected by all * selectors, it returns SELECTED. * - * @param pendingTransaction The transaction to be evaluated. + * @param evaluationContext The current selection session data. * @return The result of the transaction selection process. */ private TransactionSelectionResult evaluatePreProcessing( - final PendingTransaction pendingTransaction) { + final TransactionEvaluationContext evaluationContext) { for (var selector : transactionSelectors) { TransactionSelectionResult result = - selector.evaluateTransactionPreProcessing( - pendingTransaction, transactionSelectionResults); + selector.evaluateTransactionPreProcessing(evaluationContext, transactionSelectionResults); if (!result.equals(SELECTED)) { return result; } } - return pluginTransactionSelector.evaluateTransactionPreProcessing(pendingTransaction); + return pluginTransactionSelector.evaluateTransactionPreProcessing(evaluationContext); } /** @@ -279,24 +294,24 @@ private TransactionSelectionResult evaluatePreProcessing( * whether the transaction should be included in a block. If the transaction is selected by all * selectors, it returns SELECTED. * - * @param pendingTransaction The transaction to be evaluated. + * @param evaluationContext The current selection session data. * @param processingResult The result of the transaction processing. * @return The result of the transaction selection process. */ private TransactionSelectionResult evaluatePostProcessing( - final PendingTransaction pendingTransaction, + final TransactionEvaluationContext evaluationContext, final TransactionProcessingResult processingResult) { for (var selector : transactionSelectors) { TransactionSelectionResult result = selector.evaluateTransactionPostProcessing( - pendingTransaction, transactionSelectionResults, processingResult); + evaluationContext, transactionSelectionResults, processingResult); if (!result.equals(SELECTED)) { return result; } } return pluginTransactionSelector.evaluateTransactionPostProcessing( - pendingTransaction, processingResult); + evaluationContext, processingResult); } /** @@ -328,18 +343,16 @@ private TransactionProcessingResult processTransaction( * receipt, updating the TransactionSelectionResults with the selected transaction, and notifying * the external transaction selector. * - * @param pendingTransaction The pending transaction. + * @param evaluationContext The current selection session data. * @param processingResult The result of the transaction processing. * @param txWorldStateUpdater The world state updater. - * @param evaluationTimer tracks the evaluation elapsed time * @return The result of the transaction selection process. */ private TransactionSelectionResult handleTransactionSelected( - final PendingTransaction pendingTransaction, + final TransactionEvaluationContext evaluationContext, final TransactionProcessingResult processingResult, - final WorldUpdater txWorldStateUpdater, - final Stopwatch evaluationTimer) { - final Transaction transaction = pendingTransaction.getTransaction(); + final WorldUpdater txWorldStateUpdater) { + final Transaction transaction = evaluationContext.getTransaction(); final long gasUsedByTransaction = transaction.getGasLimit() - processingResult.getGasRemaining(); @@ -363,13 +376,14 @@ private TransactionSelectionResult handleTransactionSelected( transaction.getType(), processingResult, worldState, cumulativeGasUsed); transactionSelectionResults.updateSelected( - pendingTransaction.getTransaction(), receipt, gasUsedByTransaction, blobGasUsed); + transaction, receipt, gasUsedByTransaction, blobGasUsed); } } if (tooLate) { // even if this tx passed all the checks, it is too late to include it in this block, // so we need to treat it as not selected + final var evaluationTimer = evaluationContext.getEvaluationTimer(); // check if this tx took too much to evaluate, and in case remove it from the pool final TransactionSelectionResult timeoutSelectionResult; @@ -395,15 +409,15 @@ private TransactionSelectionResult handleTransactionSelected( // do not rely on the presence of this result, since by the time it is added, the code // reading it could have been already executed by another thread return handleTransactionNotSelected( - pendingTransaction, timeoutSelectionResult, txWorldStateUpdater, evaluationTimer); + evaluationContext, timeoutSelectionResult, txWorldStateUpdater); } - pluginTransactionSelector.onTransactionSelected(pendingTransaction, processingResult); + pluginTransactionSelector.onTransactionSelected(evaluationContext, processingResult); blockWorldStateUpdater = worldState.updater(); LOG.atTrace() .setMessage("Selected {} for block creation, evaluated in {}") .addArgument(transaction::toTraceLog) - .addArgument(evaluationTimer) + .addArgument(evaluationContext.getPendingTransaction()) .log(); return SELECTED; } @@ -413,36 +427,35 @@ private TransactionSelectionResult handleTransactionSelected( * TransactionSelectionResults with the unselected transaction, and notifies the external * transaction selector. * - * @param pendingTransaction The unselected pending transaction. + * @param evaluationContext The current selection session data. * @param selectionResult The result of the transaction selection process. - * @param evaluationTimer tracks the evaluation elapsed time * @return The result of the transaction selection process. */ private TransactionSelectionResult handleTransactionNotSelected( - final PendingTransaction pendingTransaction, - final TransactionSelectionResult selectionResult, - final Stopwatch evaluationTimer) { + final TransactionEvaluationContext evaluationContext, + final TransactionSelectionResult selectionResult) { + + final var pendingTransaction = evaluationContext.getPendingTransaction(); transactionSelectionResults.updateNotSelected( - pendingTransaction.getTransaction(), selectionResult); - pluginTransactionSelector.onTransactionNotSelected(pendingTransaction, selectionResult); + evaluationContext.getTransaction(), selectionResult); + pluginTransactionSelector.onTransactionNotSelected(evaluationContext, selectionResult); LOG.atTrace() .setMessage("Not selected {} for block creation with result {}, evaluated in {}") .addArgument(pendingTransaction::toTraceLog) .addArgument(selectionResult) - .addArgument(evaluationTimer) + .addArgument(evaluationContext.getEvaluationTimer()) .log(); return selectionResult; } private TransactionSelectionResult handleTransactionNotSelected( - final PendingTransaction pendingTransaction, + final TransactionEvaluationContext evaluationContext, final TransactionSelectionResult selectionResult, - final WorldUpdater txWorldStateUpdater, - final Stopwatch evaluationTimer) { + final WorldUpdater txWorldStateUpdater) { txWorldStateUpdater.revert(); - return handleTransactionNotSelected(pendingTransaction, selectionResult, evaluationTimer); + return handleTransactionNotSelected(evaluationContext, selectionResult); } private void checkCancellation() { diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/TransactionEvaluationContext.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/TransactionEvaluationContext.java new file mode 100644 index 00000000000..20609ebdc84 --- /dev/null +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/TransactionEvaluationContext.java @@ -0,0 +1,66 @@ +/* + * Copyright Hyperledger Besu Contributors. + * + * 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 org.hyperledger.besu.ethereum.blockcreation.txselection; + +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction; + +import com.google.common.base.Stopwatch; + +public class TransactionEvaluationContext + implements org.hyperledger.besu.plugin.services.txselection.TransactionEvaluationContext< + PendingTransaction> { + private final org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction + pendingTransaction; + private final Stopwatch evaluationTimer; + private final Wei transactionGasPrice; + private final Wei minGasPrice; + + public TransactionEvaluationContext( + final PendingTransaction pendingTransaction, + final Stopwatch evaluationTimer, + final Wei transactionGasPrice, + final Wei minGasPrice) { + this.pendingTransaction = pendingTransaction; + this.evaluationTimer = evaluationTimer; + this.transactionGasPrice = transactionGasPrice; + this.minGasPrice = minGasPrice; + } + + public Transaction getTransaction() { + return pendingTransaction.getTransaction(); + } + + @Override + public PendingTransaction getPendingTransaction() { + return pendingTransaction; + } + + @Override + public Stopwatch getEvaluationTimer() { + return evaluationTimer; + } + + @Override + public Wei getTransactionGasPrice() { + return transactionGasPrice; + } + + @Override + public Wei getMinGasPrice() { + return minGasPrice; + } +} diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/AbstractTransactionSelector.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/AbstractTransactionSelector.java index 205e803e820..0e7f610a1f3 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/AbstractTransactionSelector.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/AbstractTransactionSelector.java @@ -15,8 +15,8 @@ package org.hyperledger.besu.ethereum.blockcreation.txselection.selectors; import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext; +import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionEvaluationContext; import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionSelectionResults; -import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.plugin.data.TransactionSelectionResult; @@ -34,25 +34,25 @@ public AbstractTransactionSelector(final BlockSelectionContext context) { /** * Evaluates a transaction in the context of other transactions in the same block. * - * @param pendingTransaction The transaction to be evaluated within a block. + * @param evaluationContext The current selection session data. * @param blockTransactionResults The results of other transaction evaluations in the same block. * @return The result of the transaction evaluation */ public abstract TransactionSelectionResult evaluateTransactionPreProcessing( - final PendingTransaction pendingTransaction, + final TransactionEvaluationContext evaluationContext, final TransactionSelectionResults blockTransactionResults); /** * Evaluates a transaction considering other transactions in the same block and a transaction * processing result. * - * @param pendingTransaction The transaction to be evaluated. + * @param evaluationContext The current selection session data. * @param blockTransactionResults The results of other transaction evaluations in the same block. * @param processingResult The result of transaction processing. * @return The result of the transaction evaluation */ public abstract TransactionSelectionResult evaluateTransactionPostProcessing( - final PendingTransaction pendingTransaction, + final TransactionEvaluationContext evaluationContext, final TransactionSelectionResults blockTransactionResults, final TransactionProcessingResult processingResult); } diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/AllAcceptingTransactionSelector.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/AllAcceptingTransactionSelector.java index 9032ddca575..a2b8d57ff29 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/AllAcceptingTransactionSelector.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/AllAcceptingTransactionSelector.java @@ -18,6 +18,7 @@ import org.hyperledger.besu.plugin.data.TransactionProcessingResult; import org.hyperledger.besu.plugin.data.TransactionSelectionResult; import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector; +import org.hyperledger.besu.plugin.services.txselection.TransactionEvaluationContext; /** A TransactionSelector that unconditionally selects all transactions. */ public class AllAcceptingTransactionSelector implements PluginTransactionSelector { @@ -29,25 +30,25 @@ private AllAcceptingTransactionSelector() {} /** * Always selects the transaction in the pre-processing stage. * - * @param pendingTransaction The transaction to be evaluated. + * @param evaluationContext The current selection context. * @return Always SELECTED. */ @Override public TransactionSelectionResult evaluateTransactionPreProcessing( - final PendingTransaction pendingTransaction) { + final TransactionEvaluationContext evaluationContext) { return TransactionSelectionResult.SELECTED; } /** * Always selects the transaction in the post-processing stage. * - * @param pendingTransaction The transaction to be evaluated. + * @param evaluationContext The current selection context. * @param processingResult The result of the transaction processing. * @return Always SELECTED. */ @Override public TransactionSelectionResult evaluateTransactionPostProcessing( - final PendingTransaction pendingTransaction, + final TransactionEvaluationContext evaluationContext, final TransactionProcessingResult processingResult) { return TransactionSelectionResult.SELECTED; } diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlobPriceTransactionSelector.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlobPriceTransactionSelector.java index 56a17c28434..96135bea214 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlobPriceTransactionSelector.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlobPriceTransactionSelector.java @@ -15,9 +15,9 @@ package org.hyperledger.besu.ethereum.blockcreation.txselection.selectors; import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext; +import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionEvaluationContext; import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionSelectionResults; import org.hyperledger.besu.ethereum.core.Transaction; -import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.plugin.data.TransactionSelectionResult; @@ -39,14 +39,15 @@ public BlobPriceTransactionSelector(final BlockSelectionContext context) { /** * Evaluates a transaction considering its blob price. * - * @param pendingTransaction The transaction to be evaluated. + * @param evaluationContext The current selection session data. * @param ignored The results of other transaction evaluations in the same block. * @return The result of the transaction selection. */ @Override public TransactionSelectionResult evaluateTransactionPreProcessing( - final PendingTransaction pendingTransaction, final TransactionSelectionResults ignored) { - if (transactionBlobPriceBelowMin(pendingTransaction.getTransaction())) { + final TransactionEvaluationContext evaluationContext, + final TransactionSelectionResults ignored) { + if (transactionBlobPriceBelowMin(evaluationContext.getTransaction())) { return TransactionSelectionResult.BLOB_PRICE_BELOW_CURRENT_MIN; } return TransactionSelectionResult.SELECTED; @@ -54,7 +55,7 @@ public TransactionSelectionResult evaluateTransactionPreProcessing( @Override public TransactionSelectionResult evaluateTransactionPostProcessing( - final PendingTransaction pendingTransaction, + final TransactionEvaluationContext evaluationContext, final TransactionSelectionResults blockTransactionResults, final TransactionProcessingResult processingResult) { // All necessary checks were done in the pre-processing method, so nothing to do here. diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlockSizeTransactionSelector.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlockSizeTransactionSelector.java index 96f357546f3..168107f5950 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlockSizeTransactionSelector.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlockSizeTransactionSelector.java @@ -15,9 +15,9 @@ package org.hyperledger.besu.ethereum.blockcreation.txselection.selectors; import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext; +import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionEvaluationContext; import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionSelectionResults; import org.hyperledger.besu.ethereum.core.Transaction; -import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.plugin.data.TransactionSelectionResult; @@ -40,20 +40,21 @@ public BlockSizeTransactionSelector(final BlockSelectionContext context) { * Evaluates a transaction considering other transactions in the same block. If the transaction is * too large for the block returns a selection result based on block occupancy. * - * @param pendingTransaction The transaction to be evaluated. + * @param evaluationContext The current selection session data. * @param transactionSelectionResults The results of other transaction evaluations in the same * block. * @return The result of the transaction selection. */ @Override public TransactionSelectionResult evaluateTransactionPreProcessing( - final PendingTransaction pendingTransaction, + final TransactionEvaluationContext evaluationContext, final TransactionSelectionResults transactionSelectionResults) { + if (transactionTooLargeForBlock( - pendingTransaction.getTransaction(), transactionSelectionResults)) { + evaluationContext.getTransaction(), transactionSelectionResults)) { LOG.atTrace() .setMessage("Transaction {} too large to select for block creation") - .addArgument(pendingTransaction::toTraceLog) + .addArgument(evaluationContext.getPendingTransaction()::toTraceLog) .log(); if (blockOccupancyAboveThreshold(transactionSelectionResults)) { LOG.trace("Block occupancy above threshold, completing operation"); @@ -70,7 +71,7 @@ public TransactionSelectionResult evaluateTransactionPreProcessing( @Override public TransactionSelectionResult evaluateTransactionPostProcessing( - final PendingTransaction pendingTransaction, + final TransactionEvaluationContext evaluationContext, final TransactionSelectionResults blockTransactionResults, final TransactionProcessingResult processingResult) { // All necessary checks were done in the pre-processing method, so nothing to do here. diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/MinPriorityFeePerGasTransactionSelector.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/MinPriorityFeePerGasTransactionSelector.java index 91085f4f020..1a46242ffe2 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/MinPriorityFeePerGasTransactionSelector.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/MinPriorityFeePerGasTransactionSelector.java @@ -16,6 +16,7 @@ import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext; +import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionEvaluationContext; import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionSelectionResults; import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; @@ -36,7 +37,7 @@ public MinPriorityFeePerGasTransactionSelector(final BlockSelectionContext conte /** * Evaluates a transaction before processing. * - * @param pendingTransaction The transaction to be evaluated. + * @param evaluationContext The current selection session data. * @param transactionSelectionResults The results of other transaction evaluations in the same * block. * @return TransactionSelectionResult. If the priority fee is below the minimum, it returns an @@ -44,9 +45,9 @@ public MinPriorityFeePerGasTransactionSelector(final BlockSelectionContext conte */ @Override public TransactionSelectionResult evaluateTransactionPreProcessing( - final PendingTransaction pendingTransaction, + final TransactionEvaluationContext evaluationContext, final TransactionSelectionResults transactionSelectionResults) { - if (isPriorityFeePriceBelowMinimum(pendingTransaction)) { + if (isPriorityFeePriceBelowMinimum(evaluationContext.getPendingTransaction())) { return TransactionSelectionResult.PRIORITY_FEE_PER_GAS_BELOW_CURRENT_MIN; } return TransactionSelectionResult.SELECTED; @@ -74,13 +75,13 @@ private boolean isPriorityFeePriceBelowMinimum(final PendingTransaction pendingT /** * No evaluation is performed post-processing. * - * @param pendingTransaction The processed transaction. + * @param evaluationContext The current selection session data. * @param processingResult The result of the transaction processing. * @return Always returns SELECTED. */ @Override public TransactionSelectionResult evaluateTransactionPostProcessing( - final PendingTransaction pendingTransaction, + final TransactionEvaluationContext evaluationContext, final TransactionSelectionResults blockTransactionResults, final TransactionProcessingResult processingResult) { return TransactionSelectionResult.SELECTED; diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/PriceTransactionSelector.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/PriceTransactionSelector.java index defd75cb77c..d677ea9358f 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/PriceTransactionSelector.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/PriceTransactionSelector.java @@ -14,10 +14,9 @@ */ package org.hyperledger.besu.ethereum.blockcreation.txselection.selectors; -import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext; +import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionEvaluationContext; import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionSelectionResults; -import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.plugin.data.TransactionSelectionResult; @@ -41,14 +40,15 @@ public PriceTransactionSelector(final BlockSelectionContext context) { * Evaluates a transaction considering its price. If the transaction's current price is below the * minimum, it returns a selection result indicating the reason. * - * @param pendingTransaction The transaction to be evaluated. + * @param evaluationContext The current selection session data. * @param ignored The results of other transaction evaluations in the same block. * @return The result of the transaction selection. */ @Override public TransactionSelectionResult evaluateTransactionPreProcessing( - final PendingTransaction pendingTransaction, final TransactionSelectionResults ignored) { - if (transactionCurrentPriceBelowMin(pendingTransaction)) { + final TransactionEvaluationContext evaluationContext, + final TransactionSelectionResults ignored) { + if (transactionCurrentPriceBelowMin(evaluationContext)) { return TransactionSelectionResult.CURRENT_TX_PRICE_BELOW_MIN; } return TransactionSelectionResult.SELECTED; @@ -56,7 +56,7 @@ public TransactionSelectionResult evaluateTransactionPreProcessing( @Override public TransactionSelectionResult evaluateTransactionPostProcessing( - final PendingTransaction pendingTransaction, + final TransactionEvaluationContext evaluationContext, final TransactionSelectionResults blockTransactionResults, final TransactionProcessingResult processingResult) { // All necessary checks were done in the pre-processing method, so nothing to do here. @@ -66,30 +66,25 @@ public TransactionSelectionResult evaluateTransactionPostProcessing( /** * Checks if the transaction's current price is below the minimum. * - * @param pendingTransaction The transaction to be checked. + * @param evaluationContext The current selection session data. * @return True if the transaction's current price is below the minimum, false otherwise. */ - private boolean transactionCurrentPriceBelowMin(final PendingTransaction pendingTransaction) { - final Transaction transaction = pendingTransaction.getTransaction(); + private boolean transactionCurrentPriceBelowMin( + final TransactionEvaluationContext evaluationContext) { + final PendingTransaction pendingTransaction = evaluationContext.getPendingTransaction(); // Priority txs are exempt from this check if (!pendingTransaction.hasPriority()) { - // since the minGasPrice can change at runtime, we need to recheck it everytime - final Wei transactionGasPriceInBlock = - context - .feeMarket() - .getTransactionPriceCalculator() - .price(transaction, context.processableBlockHeader().getBaseFee()); if (context .miningParameters() .getMinTransactionGasPrice() - .compareTo(transactionGasPriceInBlock) + .compareTo(evaluationContext.getTransactionGasPrice()) > 0) { LOG.atTrace() .setMessage( "Current gas price of {} is {} and lower than the configured minimum {}, skipping") .addArgument(pendingTransaction::toTraceLog) - .addArgument(transactionGasPriceInBlock::toHumanReadableString) + .addArgument(evaluationContext.getTransactionGasPrice()::toHumanReadableString) .addArgument( context.miningParameters().getMinTransactionGasPrice()::toHumanReadableString) .log(); diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/ProcessingResultTransactionSelector.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/ProcessingResultTransactionSelector.java index 8a2778eda03..bb89b965a39 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/ProcessingResultTransactionSelector.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/ProcessingResultTransactionSelector.java @@ -15,9 +15,9 @@ package org.hyperledger.besu.ethereum.blockcreation.txselection.selectors; import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext; +import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionEvaluationContext; import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionSelectionResults; import org.hyperledger.besu.ethereum.core.Transaction; -import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction; import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason; @@ -41,7 +41,7 @@ public ProcessingResultTransactionSelector(final BlockSelectionContext context) @Override public TransactionSelectionResult evaluateTransactionPreProcessing( - final PendingTransaction pendingTransaction, + final TransactionEvaluationContext evaluationContext, final TransactionSelectionResults blockTransactionResults) { // All checks depend on processingResult and will be done in the post-processing method, so // nothing to do here. @@ -53,20 +53,20 @@ public TransactionSelectionResult evaluateTransactionPreProcessing( * result. If the processing result is invalid, it determines the selection result for the invalid * result. * - * @param pendingTransaction The transaction to be evaluated. + * @param evaluationContext The current selection session data. * @param blockTransactionResults The results of other transaction evaluations in the same block. * @param processingResult The processing result of the transaction. * @return The result of the transaction selection. */ @Override public TransactionSelectionResult evaluateTransactionPostProcessing( - final PendingTransaction pendingTransaction, + final TransactionEvaluationContext evaluationContext, final TransactionSelectionResults blockTransactionResults, final TransactionProcessingResult processingResult) { if (processingResult.isInvalid()) { return transactionSelectionResultForInvalidResult( - pendingTransaction.getTransaction(), processingResult.getValidationResult()); + evaluationContext.getTransaction(), processingResult.getValidationResult()); } return TransactionSelectionResult.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 4e7b3eae5eb..0b0b2cd7831 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 @@ -83,6 +83,7 @@ import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector; import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelectorFactory; +import org.hyperledger.besu.plugin.services.txselection.TransactionEvaluationContext; import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; import org.hyperledger.besu.util.number.Percentage; @@ -581,17 +582,25 @@ public void transactionSelectionPluginShouldWork_PreProcessing() { new PluginTransactionSelector() { @Override public TransactionSelectionResult evaluateTransactionPreProcessing( - final PendingTransaction pendingTransaction) { - if (pendingTransaction.getTransaction().equals(notSelectedTransient)) + final TransactionEvaluationContext + evaluationContext) { + if (evaluationContext + .getPendingTransaction() + .getTransaction() + .equals(notSelectedTransient)) return PluginTransactionSelectionResult.GENERIC_PLUGIN_INVALID_TRANSIENT; - if (pendingTransaction.getTransaction().equals(notSelectedInvalid)) + if (evaluationContext + .getPendingTransaction() + .getTransaction() + .equals(notSelectedInvalid)) return PluginTransactionSelectionResult.GENERIC_PLUGIN_INVALID; return SELECTED; } @Override public TransactionSelectionResult evaluateTransactionPostProcessing( - final PendingTransaction pendingTransaction, + final TransactionEvaluationContext + evaluationContext, final org.hyperledger.besu.plugin.data.TransactionProcessingResult processingResult) { return SELECTED; @@ -645,13 +654,15 @@ public void transactionSelectionPluginShouldWork_PostProcessing() { new PluginTransactionSelector() { @Override public TransactionSelectionResult evaluateTransactionPreProcessing( - final PendingTransaction pendingTransaction) { + final TransactionEvaluationContext + evaluationContext) { return SELECTED; } @Override public TransactionSelectionResult evaluateTransactionPostProcessing( - final PendingTransaction pendingTransaction, + final TransactionEvaluationContext + evaluationContext, final org.hyperledger.besu.plugin.data.TransactionProcessingResult processingResult) { // the transaction with max gas +1 should fail @@ -711,13 +722,14 @@ public void transactionSelectionPluginShouldBeNotifiedWhenTransactionSelectionCo selector.buildTransactionListForBlock(); - ArgumentCaptor argumentCaptor = - ArgumentCaptor.forClass(PendingTransaction.class); + @SuppressWarnings("unchecked") + ArgumentCaptor> argumentCaptor = + ArgumentCaptor.forClass(TransactionEvaluationContext.class); // selected transaction must be notified to the selector verify(transactionSelector) .onTransactionSelected(argumentCaptor.capture(), any(TransactionProcessingResult.class)); - PendingTransaction selected = argumentCaptor.getValue(); + PendingTransaction selected = argumentCaptor.getValue().getPendingTransaction(); assertThat(selected.getTransaction()).isEqualTo(transaction); // unselected transaction must be notified to the selector with correct reason @@ -725,7 +737,7 @@ public void transactionSelectionPluginShouldBeNotifiedWhenTransactionSelectionCo .onTransactionNotSelected( argumentCaptor.capture(), eq(TransactionSelectionResult.invalid(invalidReason.toString()))); - PendingTransaction rejectedTransaction = argumentCaptor.getValue(); + PendingTransaction rejectedTransaction = argumentCaptor.getValue().getPendingTransaction(); assertThat(rejectedTransaction.getTransaction()).isEqualTo(invalidTransaction); } @@ -931,9 +943,10 @@ private void internalBlockSelectionTimeoutSimulation( final BiFunction> tooLate = (p, t) -> invocation -> { - final org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction ptx = - invocation.getArgument(0); - if (ptx.getTransaction().equals(p)) { + final org.hyperledger.besu.ethereum.blockcreation.txselection + .TransactionEvaluationContext + ctx = invocation.getArgument(0); + if (ctx.getTransaction().equals(p)) { Thread.sleep(t); } else { Thread.sleep(fastProcessingTxTime); diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/MinPriorityFeePerGasTransactionSelectorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/MinPriorityFeePerGasTransactionSelectorTest.java index a86dc73ee46..94d3abb105c 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/MinPriorityFeePerGasTransactionSelectorTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/MinPriorityFeePerGasTransactionSelectorTest.java @@ -21,6 +21,7 @@ import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext; +import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionEvaluationContext; import org.hyperledger.besu.ethereum.blockcreation.txselection.selectors.AbstractTransactionSelector; import org.hyperledger.besu.ethereum.blockcreation.txselection.selectors.MinPriorityFeePerGasTransactionSelector; import org.hyperledger.besu.ethereum.core.MiningParameters; @@ -29,6 +30,7 @@ import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction; import org.hyperledger.besu.plugin.data.TransactionSelectionResult; +import com.google.common.base.Stopwatch; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -56,44 +58,47 @@ public void initialize() { @Test public void shouldNotSelectWhen_PriorityFeePerGas_IsLessThan_MinPriorityFeePerGas() { - var transaction = mockTransactionWithPriorityFee(minPriorityFeeParameter - 1); + var transaction = mockTransactionEvaluationContext(minPriorityFeeParameter - 1); assertSelectionResult( transaction, TransactionSelectionResult.PRIORITY_FEE_PER_GAS_BELOW_CURRENT_MIN); } @Test public void shouldSelectWhen_PriorityFeePerGas_IsEqual_MinPriorityFeePerGas() { - var transaction = mockTransactionWithPriorityFee(minPriorityFeeParameter); + var transaction = mockTransactionEvaluationContext(minPriorityFeeParameter); assertSelectionResult(transaction, TransactionSelectionResult.SELECTED); } @Test public void shouldSelectWhen_PriorityFeePerGas_IsGreaterThan_MinPriorityFeePerGas() { - var transaction = mockTransactionWithPriorityFee(minPriorityFeeParameter + 1); + var transaction = mockTransactionEvaluationContext(minPriorityFeeParameter + 1); assertSelectionResult(transaction, TransactionSelectionResult.SELECTED); } @Test public void shouldSelectWhenPrioritySender() { - var prioritySenderTransaction = mockTransactionWithPriorityFee(minPriorityFeeParameter - 1); + final var evaluationContext = mockTransactionEvaluationContext(minPriorityFeeParameter - 1); assertSelectionResult( - prioritySenderTransaction, - TransactionSelectionResult.PRIORITY_FEE_PER_GAS_BELOW_CURRENT_MIN); - when(prioritySenderTransaction.hasPriority()).thenReturn(true); - assertSelectionResult(prioritySenderTransaction, TransactionSelectionResult.SELECTED); + evaluationContext, TransactionSelectionResult.PRIORITY_FEE_PER_GAS_BELOW_CURRENT_MIN); + when(evaluationContext.getPendingTransaction().hasPriority()).thenReturn(true); + assertSelectionResult(evaluationContext, TransactionSelectionResult.SELECTED); } private void assertSelectionResult( - final PendingTransaction transaction, final TransactionSelectionResult expectedResult) { - var actualResult = transactionSelector.evaluateTransactionPreProcessing(transaction, null); + final TransactionEvaluationContext evaluationContext, + final TransactionSelectionResult expectedResult) { + var actualResult = + transactionSelector.evaluateTransactionPreProcessing(evaluationContext, null); assertThat(actualResult).isEqualTo(expectedResult); } - private PendingTransaction mockTransactionWithPriorityFee(final int priorityFeePerGas) { - PendingTransaction mockTransaction = mock(PendingTransaction.class); + private TransactionEvaluationContext mockTransactionEvaluationContext( + final int priorityFeePerGas) { + PendingTransaction pendingTransaction = mock(PendingTransaction.class); Transaction transaction = mock(Transaction.class); - when(mockTransaction.getTransaction()).thenReturn(transaction); + when(pendingTransaction.getTransaction()).thenReturn(transaction); when(transaction.getEffectivePriorityFeePerGas(any())).thenReturn(Wei.of(priorityFeePerGas)); - return mockTransaction; + return new TransactionEvaluationContext( + pendingTransaction, Stopwatch.createStarted(), Wei.ONE, Wei.ONE); } } diff --git a/plugin-api/build.gradle b/plugin-api/build.gradle index d271094604d..1dcfd00f7ee 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 = 'N583pqJipDs4kJkgL0cPq9PBsYdsLzvUlu2I8Kk+w7g=' + knownHash = 'IGq+V3KaStHCRFkeK3KwPxJYKO4RX9YM1O4JYITk8S8=' } check.dependsOn('checkAPIChanges') diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/PluginTransactionSelector.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/PluginTransactionSelector.java index 716a05e7d8b..3d152c370ca 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/PluginTransactionSelector.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/PluginTransactionSelector.java @@ -39,39 +39,40 @@ default BlockAwareOperationTracer getOperationTracer() { * Method called to decide whether a transaction is added to a block. The result can also indicate * that no further transactions can be added to the block. * - * @param pendingTransaction candidate transaction + * @param evaluationContext The current selection context * @return TransactionSelectionResult that indicates whether to include the transaction */ TransactionSelectionResult evaluateTransactionPreProcessing( - PendingTransaction pendingTransaction); + TransactionEvaluationContext evaluationContext); /** * Method called to decide whether a processed transaction is added to a block. The result can * also indicate that no further transactions can be added to the block. * - * @param pendingTransaction candidate transaction + * @param evaluationContext The current selection context * @param processingResult the transaction processing result * @return TransactionSelectionResult that indicates whether to include the transaction */ TransactionSelectionResult evaluateTransactionPostProcessing( - PendingTransaction pendingTransaction, TransactionProcessingResult processingResult); + TransactionEvaluationContext evaluationContext, + TransactionProcessingResult processingResult); /** * Method called when a transaction is selected to be added to a block. * - * @param pendingTransaction The transaction that has been selected. + * @param evaluationContext The current selection context * @param processingResult The result of processing the selected transaction. */ default void onTransactionSelected( - final PendingTransaction pendingTransaction, + final TransactionEvaluationContext evaluationContext, final TransactionProcessingResult processingResult) {} /** * Method called when a transaction is not selected to be added to a block. * - * @param pendingTransaction The transaction that has not been selected. + * @param evaluationContext The current selection context * @param transactionSelectionResult The transaction selection result */ default void onTransactionNotSelected( - final PendingTransaction pendingTransaction, + final TransactionEvaluationContext evaluationContext, final TransactionSelectionResult transactionSelectionResult) {} } diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/TransactionEvaluationContext.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/TransactionEvaluationContext.java new file mode 100644 index 00000000000..83aa7b91e7c --- /dev/null +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/TransactionEvaluationContext.java @@ -0,0 +1,57 @@ +/* + * Copyright Hyperledger Besu Contributors. + * + * 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 org.hyperledger.besu.plugin.services.txselection; + +import org.hyperledger.besu.datatypes.PendingTransaction; +import org.hyperledger.besu.datatypes.Wei; + +import com.google.common.base.Stopwatch; + +/** + * This interface defines the context for evaluating a transaction. It provides methods to get the + * pending transaction, the evaluation timer, and the transaction gas price. + * + * @param the type of the pending transaction + */ +public interface TransactionEvaluationContext { + + /** + * Gets the pending transaction. + * + * @return the pending transaction + */ + PT getPendingTransaction(); + + /** + * Gets the stopwatch used for timing the evaluation. + * + * @return the evaluation timer + */ + Stopwatch getEvaluationTimer(); + + /** + * Gets the gas price of the transaction. + * + * @return the transaction gas price + */ + Wei getTransactionGasPrice(); + + /** + * Gets the min gas price for block inclusion + * + * @return the min gas price + */ + Wei getMinGasPrice(); +}