Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Plugin API] - TransactionSelector - Send TransactionSelectionResult to the plugin when not transaction is not selected #6010

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,15 @@ public 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
Expand All @@ -139,16 +148,7 @@ public TransactionSelectionResults buildTransactionListForBlock() {
.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)
Expand All @@ -167,105 +167,40 @@ public TransactionSelectionResults buildTransactionListForBlock() {
*/
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);
}

/**
Expand All @@ -277,15 +212,13 @@ private void updateTransactionRejected(
* @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;
}
Expand All @@ -303,16 +236,14 @@ private TransactionSelectionResult evaluateTransactionPreProcessing(
* @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;
}
Expand All @@ -321,12 +252,94 @@ private TransactionSelectionResult evaluateTransactionPostProcessing(
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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -674,9 +674,10 @@ public void transactionSelectionPluginShouldBeNotifiedWhenTransactionSelectionCo
final Transaction transaction = createTransaction(0, Wei.of(10), 21_000);
ensureTransactionIsValid(transaction, 21_000, 0);

final TransactionInvalidReason invalidReason =
TransactionInvalidReason.PLUGIN_TX_VALIDATOR_INVALIDATED;
final Transaction invalidTransaction = createTransaction(1, Wei.of(10), 21_000);
ensureTransactionIsInvalid(
invalidTransaction, TransactionInvalidReason.PLUGIN_TX_VALIDATOR_INVALIDATED);
ensureTransactionIsInvalid(invalidTransaction, invalidReason);
transactionPool.addRemoteTransactions(List.of(transaction, invalidTransaction));

createBlockSelectorWithTxSelPlugin(
Expand All @@ -692,11 +693,16 @@ public void transactionSelectionPluginShouldBeNotifiedWhenTransactionSelectionCo
ArgumentCaptor<PendingTransaction> argumentCaptor =
ArgumentCaptor.forClass(PendingTransaction.class);

// selected transaction must be notified to the selector
verify(transactionSelector).onTransactionSelected(argumentCaptor.capture());
PendingTransaction selected = argumentCaptor.getValue();
assertThat(selected.getTransaction()).isEqualTo(transaction);

verify(transactionSelector).onTransactionRejected(argumentCaptor.capture());
// unselected transaction must be notified to the selector with correct reason
verify(transactionSelector)
.onTransactionNotSelected(
argumentCaptor.capture(),
eq(TransactionSelectionResult.invalid(invalidReason.toString())));
PendingTransaction rejectedTransaction = argumentCaptor.getValue();
assertThat(rejectedTransaction.getTransaction()).isEqualTo(invalidTransaction);
}
Expand Down
2 changes: 1 addition & 1 deletion plugin-api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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 = '+7wo9cABKEFyYvjtpDFAOXqVKBAkffdnb433hT0VQ7I='
knownHash = '8Ce6NSYQa20k6YnlRXHZ4huLrSvZ6dUpyyD5dptYBHQ='
}
check.dependsOn('checkAPIChanges')

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,11 @@
*/
default void onTransactionSelected(final PendingTransaction pendingTransaction) {}
/**
* Method called when a transaction is rejected to be added to a block.
* Method called when a transaction is not selected to be added to a block.
*
* @param pendingTransaction The transaction that has been rejected.
* @param pendingTransaction The transaction that has not been selected.
*/
default void onTransactionRejected(final PendingTransaction pendingTransaction) {}
default void onTransactionNotSelected(
final PendingTransaction pendingTransaction,
Fixed Show fixed Hide fixed

Check notice

Code scanning / CodeQL

Useless parameter Note

The parameter 'pendingTransaction' is never used.
final TransactionSelectionResult selectionResult) {}
Fixed Show fixed Hide fixed
}
Loading