|
|
|
@ -125,6 +125,15 @@ public class BlockTransactionSelector { |
|
|
|
|
.orElse(AllAcceptingTransactionSelector.INSTANCE); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private List<AbstractTransactionSelector> createTransactionSelectors( |
|
|
|
|
final BlockSelectionContext context) { |
|
|
|
|
return List.of( |
|
|
|
|
new BlockSizeTransactionSelector(context), |
|
|
|
|
new PriceTransactionSelector(context), |
|
|
|
|
new BlobPriceTransactionSelector(context), |
|
|
|
|
new ProcessingResultTransactionSelector(context)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Builds a list of transactions for a block by iterating over all transactions in the |
|
|
|
|
* PendingTransactions pool. This operation can be long-running and, if executed in a separate |
|
|
|
@ -139,16 +148,7 @@ public class BlockTransactionSelector { |
|
|
|
|
.setMessage("Transaction pool stats {}") |
|
|
|
|
.addArgument(blockSelectionContext.transactionPool().logStats()) |
|
|
|
|
.log(); |
|
|
|
|
blockSelectionContext |
|
|
|
|
.transactionPool() |
|
|
|
|
.selectTransactions( |
|
|
|
|
pendingTransaction -> { |
|
|
|
|
final var res = evaluateTransaction(pendingTransaction); |
|
|
|
|
if (!res.selected()) { |
|
|
|
|
updateTransactionRejected(pendingTransaction, res); |
|
|
|
|
} |
|
|
|
|
return res; |
|
|
|
|
}); |
|
|
|
|
blockSelectionContext.transactionPool().selectTransactions(this::evaluateTransaction); |
|
|
|
|
LOG.atTrace() |
|
|
|
|
.setMessage("Transaction selection result {}") |
|
|
|
|
.addArgument(transactionSelectionResults::toTraceLog) |
|
|
|
@ -167,105 +167,40 @@ public class BlockTransactionSelector { |
|
|
|
|
*/ |
|
|
|
|
public TransactionSelectionResults evaluateTransactions(final List<Transaction> transactions) { |
|
|
|
|
transactions.forEach( |
|
|
|
|
transaction -> { |
|
|
|
|
var pendingTransaction = new PendingTransaction.Local(transaction); |
|
|
|
|
final var res = evaluateTransaction(pendingTransaction); |
|
|
|
|
if (!res.selected()) { |
|
|
|
|
updateTransactionRejected(pendingTransaction, res); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
transaction -> evaluateTransaction(new PendingTransaction.Local(transaction))); |
|
|
|
|
return transactionSelectionResults; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
/** |
|
|
|
|
* Passed into the PendingTransactions, and is called on each transaction until sufficient |
|
|
|
|
* transactions are found which fill a block worth of gas. |
|
|
|
|
* |
|
|
|
|
* This function will continue to be called until the block under construction is suitably |
|
|
|
|
* full (in terms of gasLimit) and the provided transaction's gasLimit does not fit within |
|
|
|
|
* the space remaining in the block. |
|
|
|
|
* transactions are found which fill a block worth of gas. This function will continue to be |
|
|
|
|
* called until the block under construction is suitably full (in terms of gasLimit) and the |
|
|
|
|
* provided transaction's gasLimit does not fit within the space remaining in the block. |
|
|
|
|
* |
|
|
|
|
* @param pendingTransaction The transaction to be evaluated. |
|
|
|
|
* @return The result of the transaction evaluation process. |
|
|
|
|
* @throws CancellationException if the transaction selection process is cancelled. |
|
|
|
|
*/ |
|
|
|
|
private TransactionSelectionResult evaluateTransaction( |
|
|
|
|
final PendingTransaction pendingTransaction) { |
|
|
|
|
if (isCancelled.get()) { |
|
|
|
|
throw new CancellationException("Cancelled during transaction selection."); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
final Transaction transaction = pendingTransaction.getTransaction(); |
|
|
|
|
checkCancellation(); |
|
|
|
|
|
|
|
|
|
TransactionSelectionResult selectionResult = |
|
|
|
|
evaluateTransactionPreProcessing(pendingTransaction); |
|
|
|
|
TransactionSelectionResult selectionResult = evaluatePreProcessing(pendingTransaction); |
|
|
|
|
if (!selectionResult.selected()) { |
|
|
|
|
return selectionResult; |
|
|
|
|
return handleTransactionNotSelected(pendingTransaction, selectionResult); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
final WorldUpdater worldStateUpdater = worldState.updater(); |
|
|
|
|
final BlockHashLookup blockHashLookup = |
|
|
|
|
new CachingBlockHashLookup(blockSelectionContext.processableBlockHeader(), blockchain); |
|
|
|
|
|
|
|
|
|
final TransactionProcessingResult effectiveResult = |
|
|
|
|
transactionProcessor.processTransaction( |
|
|
|
|
blockchain, |
|
|
|
|
worldStateUpdater, |
|
|
|
|
blockSelectionContext.processableBlockHeader(), |
|
|
|
|
transaction, |
|
|
|
|
blockSelectionContext.miningBeneficiary(), |
|
|
|
|
blockHashLookup, |
|
|
|
|
false, |
|
|
|
|
TransactionValidationParams.mining(), |
|
|
|
|
blockSelectionContext.blobGasPrice()); |
|
|
|
|
final TransactionProcessingResult processingResult = |
|
|
|
|
processTransaction(pendingTransaction, worldStateUpdater); |
|
|
|
|
|
|
|
|
|
var transactionWithProcessingContextResult = |
|
|
|
|
evaluateTransactionPostProcessing(pendingTransaction, effectiveResult); |
|
|
|
|
if (!transactionWithProcessingContextResult.selected()) { |
|
|
|
|
return transactionWithProcessingContextResult; |
|
|
|
|
var postProcessingSelectionResult = |
|
|
|
|
evaluatePostProcessing(pendingTransaction, processingResult); |
|
|
|
|
if (!postProcessingSelectionResult.selected()) { |
|
|
|
|
return handleTransactionNotSelected(pendingTransaction, postProcessingSelectionResult); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
final long gasUsedByTransaction = transaction.getGasLimit() - effectiveResult.getGasRemaining(); |
|
|
|
|
final long cumulativeGasUsed = |
|
|
|
|
transactionSelectionResults.getCumulativeGasUsed() + gasUsedByTransaction; |
|
|
|
|
|
|
|
|
|
worldStateUpdater.commit(); |
|
|
|
|
final TransactionReceipt receipt = |
|
|
|
|
transactionReceiptFactory.create( |
|
|
|
|
transaction.getType(), effectiveResult, worldState, cumulativeGasUsed); |
|
|
|
|
|
|
|
|
|
final long blobGasUsed = |
|
|
|
|
blockSelectionContext.gasCalculator().blobGasCost(transaction.getBlobCount()); |
|
|
|
|
|
|
|
|
|
updateTransactionSelected(pendingTransaction, receipt, gasUsedByTransaction, blobGasUsed); |
|
|
|
|
|
|
|
|
|
LOG.atTrace() |
|
|
|
|
.setMessage("Selected {} for block creation") |
|
|
|
|
.addArgument(transaction::toTraceLog) |
|
|
|
|
.log(); |
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
return handleTransactionSelected(pendingTransaction, processingResult, worldStateUpdater); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -277,15 +212,13 @@ public class BlockTransactionSelector { |
|
|
|
|
* @param pendingTransaction The transaction to be evaluated. |
|
|
|
|
* @return The result of the transaction selection process. |
|
|
|
|
*/ |
|
|
|
|
private TransactionSelectionResult evaluateTransactionPreProcessing( |
|
|
|
|
private TransactionSelectionResult evaluatePreProcessing( |
|
|
|
|
final PendingTransaction pendingTransaction) { |
|
|
|
|
|
|
|
|
|
// Process the transaction through internal selectors
|
|
|
|
|
for (var selector : transactionSelectors) { |
|
|
|
|
TransactionSelectionResult result = |
|
|
|
|
selector.evaluateTransactionPreProcessing( |
|
|
|
|
pendingTransaction, transactionSelectionResults); |
|
|
|
|
// If the transaction is not selected by any internal selector, return the result
|
|
|
|
|
if (!result.equals(TransactionSelectionResult.SELECTED)) { |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
@ -303,16 +236,14 @@ public class BlockTransactionSelector { |
|
|
|
|
* @param processingResult The result of the transaction processing. |
|
|
|
|
* @return The result of the transaction selection process. |
|
|
|
|
*/ |
|
|
|
|
private TransactionSelectionResult evaluateTransactionPostProcessing( |
|
|
|
|
private TransactionSelectionResult evaluatePostProcessing( |
|
|
|
|
final PendingTransaction pendingTransaction, |
|
|
|
|
final TransactionProcessingResult processingResult) { |
|
|
|
|
|
|
|
|
|
// Process the transaction through internal selectors
|
|
|
|
|
for (var selector : transactionSelectors) { |
|
|
|
|
TransactionSelectionResult result = |
|
|
|
|
selector.evaluateTransactionPostProcessing( |
|
|
|
|
pendingTransaction, transactionSelectionResults, processingResult); |
|
|
|
|
// If the transaction is not selected by any selector, return the result
|
|
|
|
|
if (!result.equals(TransactionSelectionResult.SELECTED)) { |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
@ -321,12 +252,94 @@ public class BlockTransactionSelector { |
|
|
|
|
pendingTransaction, processingResult); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private List<AbstractTransactionSelector> createTransactionSelectors( |
|
|
|
|
final BlockSelectionContext context) { |
|
|
|
|
return List.of( |
|
|
|
|
new BlockSizeTransactionSelector(context), |
|
|
|
|
new PriceTransactionSelector(context), |
|
|
|
|
new BlobPriceTransactionSelector(context), |
|
|
|
|
new ProcessingResultTransactionSelector(context)); |
|
|
|
|
/** |
|
|
|
|
* Processes a transaction |
|
|
|
|
* |
|
|
|
|
* @param pendingTransaction The transaction to be processed. |
|
|
|
|
* @param worldStateUpdater The world state updater. |
|
|
|
|
* @return The result of the transaction processing. |
|
|
|
|
*/ |
|
|
|
|
private TransactionProcessingResult processTransaction( |
|
|
|
|
final PendingTransaction pendingTransaction, final WorldUpdater worldStateUpdater) { |
|
|
|
|
final BlockHashLookup blockHashLookup = |
|
|
|
|
new CachingBlockHashLookup(blockSelectionContext.processableBlockHeader(), blockchain); |
|
|
|
|
return transactionProcessor.processTransaction( |
|
|
|
|
blockchain, |
|
|
|
|
worldStateUpdater, |
|
|
|
|
blockSelectionContext.processableBlockHeader(), |
|
|
|
|
pendingTransaction.getTransaction(), |
|
|
|
|
blockSelectionContext.miningBeneficiary(), |
|
|
|
|
blockHashLookup, |
|
|
|
|
false, |
|
|
|
|
TransactionValidationParams.mining(), |
|
|
|
|
blockSelectionContext.blobGasPrice()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Handles a selected transaction by committing the world state updates, creating a transaction |
|
|
|
|
* receipt, updating the TransactionSelectionResults with the selected transaction, and notifying |
|
|
|
|
* the external transaction selector. |
|
|
|
|
* |
|
|
|
|
* @param pendingTransaction The pending transaction. |
|
|
|
|
* @param processingResult The result of the transaction processing. |
|
|
|
|
* @param worldStateUpdater The world state updater. |
|
|
|
|
* @return The result of the transaction selection process. |
|
|
|
|
*/ |
|
|
|
|
private TransactionSelectionResult handleTransactionSelected( |
|
|
|
|
final PendingTransaction pendingTransaction, |
|
|
|
|
final TransactionProcessingResult processingResult, |
|
|
|
|
final WorldUpdater worldStateUpdater) { |
|
|
|
|
worldStateUpdater.commit(); |
|
|
|
|
final Transaction transaction = pendingTransaction.getTransaction(); |
|
|
|
|
|
|
|
|
|
final long gasUsedByTransaction = |
|
|
|
|
transaction.getGasLimit() - processingResult.getGasRemaining(); |
|
|
|
|
final long cumulativeGasUsed = |
|
|
|
|
transactionSelectionResults.getCumulativeGasUsed() + gasUsedByTransaction; |
|
|
|
|
final long blobGasUsed = |
|
|
|
|
blockSelectionContext.gasCalculator().blobGasCost(transaction.getBlobCount()); |
|
|
|
|
|
|
|
|
|
final TransactionReceipt receipt = |
|
|
|
|
transactionReceiptFactory.create( |
|
|
|
|
transaction.getType(), processingResult, worldState, cumulativeGasUsed); |
|
|
|
|
|
|
|
|
|
logTransactionSelection(pendingTransaction.getTransaction()); |
|
|
|
|
|
|
|
|
|
transactionSelectionResults.updateSelected( |
|
|
|
|
pendingTransaction.getTransaction(), receipt, gasUsedByTransaction, blobGasUsed); |
|
|
|
|
externalTransactionSelector.onTransactionSelected(pendingTransaction); |
|
|
|
|
|
|
|
|
|
return TransactionSelectionResult.SELECTED; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Handles the scenario when a transaction is not selected. It updates the |
|
|
|
|
* TransactionSelectionResults with the unselected transaction, and notifies the external |
|
|
|
|
* transaction selector. |
|
|
|
|
* |
|
|
|
|
* @param pendingTransaction The unselected pending transaction. |
|
|
|
|
* @param selectionResult The result of the transaction selection process. |
|
|
|
|
* @return The result of the transaction selection process. |
|
|
|
|
*/ |
|
|
|
|
private TransactionSelectionResult handleTransactionNotSelected( |
|
|
|
|
final PendingTransaction pendingTransaction, |
|
|
|
|
final TransactionSelectionResult selectionResult) { |
|
|
|
|
transactionSelectionResults.updateNotSelected( |
|
|
|
|
pendingTransaction.getTransaction(), selectionResult); |
|
|
|
|
externalTransactionSelector.onTransactionNotSelected(pendingTransaction, selectionResult); |
|
|
|
|
return selectionResult; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void checkCancellation() { |
|
|
|
|
if (isCancelled.get()) { |
|
|
|
|
throw new CancellationException("Cancelled during transaction selection."); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void logTransactionSelection(final Transaction transaction) { |
|
|
|
|
LOG.atTrace() |
|
|
|
|
.setMessage("Selected {} for block creation") |
|
|
|
|
.addArgument(transaction::toTraceLog) |
|
|
|
|
.log(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|