Skip to content

Commit

Permalink
Use PendingTransaction in BlockTransactionSelector (hyperledger#5966)
Browse files Browse the repository at this point in the history
Signed-off-by: Fabio Di Fabio <[email protected]>
  • Loading branch information
fab-10 authored Oct 2, 2023
1 parent dc47867 commit 987d33c
Show file tree
Hide file tree
Showing 19 changed files with 237 additions and 175 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* 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.datatypes;

/** Represent a transaction that has not confirmed yet, and stays in the transaction pool */
public interface PendingTransaction {
/**
* Get the underlying transaction
*
* @return the underlying transaction
*/
Transaction getTransaction();

/**
* Has this transaction been received from the RPC API?
*
* @return true if it is a local sent transaction
*/
boolean isReceivedFromLocalSource();

/**
* Timestamp in millisecond when this transaction has been added to the pool
*
* @return timestamp
*/
long getAddedAt();
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
Expand Down Expand Up @@ -142,7 +143,8 @@ public TransactionSelectionResults buildTransactionListForBlock() {
pendingTransaction -> {
final var res = evaluateTransaction(pendingTransaction);
if (!res.selected()) {
transactionSelectionResults.updateNotSelected(pendingTransaction, res);
transactionSelectionResults.updateNotSelected(
pendingTransaction.getTransaction(), res);
}
return res;
});
Expand All @@ -165,7 +167,7 @@ public TransactionSelectionResults buildTransactionListForBlock() {
public TransactionSelectionResults evaluateTransactions(final List<Transaction> transactions) {
transactions.forEach(
transaction -> {
final var res = evaluateTransaction(transaction);
final var res = evaluateTransaction(new PendingTransaction.Local(transaction));
if (!res.selected()) {
transactionSelectionResults.updateNotSelected(transaction, res);
}
Expand All @@ -182,12 +184,16 @@ public TransactionSelectionResults evaluateTransactions(final List<Transaction>
* the space remaining in the block.
*
*/
private TransactionSelectionResult evaluateTransaction(final Transaction transaction) {
private TransactionSelectionResult evaluateTransaction(
final PendingTransaction pendingTransaction) {
if (isCancelled.get()) {
throw new CancellationException("Cancelled during transaction selection.");
}

TransactionSelectionResult selectionResult = evaluateTransactionPreProcessing(transaction);
final Transaction transaction = pendingTransaction.getTransaction();

TransactionSelectionResult selectionResult =
evaluateTransactionPreProcessing(pendingTransaction);
if (!selectionResult.selected()) {
return selectionResult;
}
Expand All @@ -209,7 +215,7 @@ private TransactionSelectionResult evaluateTransaction(final Transaction transac
blockSelectionContext.blobGasPrice());

var transactionWithProcessingContextResult =
evaluateTransactionPostProcessing(transaction, effectiveResult);
evaluateTransactionPostProcessing(pendingTransaction, effectiveResult);
if (!transactionWithProcessingContextResult.selected()) {
return transactionWithProcessingContextResult;
}
Expand Down Expand Up @@ -243,16 +249,17 @@ private TransactionSelectionResult evaluateTransaction(final Transaction transac
* it then processes it through external selectors. If the transaction is selected by all
* selectors, it returns SELECTED.
*
* @param transaction The transaction to be evaluated.
* @param pendingTransaction The transaction to be evaluated.
* @return The result of the transaction selection process.
*/
private TransactionSelectionResult evaluateTransactionPreProcessing(
final Transaction transaction) {
final PendingTransaction pendingTransaction) {

// Process the transaction through internal selectors
for (var selector : transactionSelectors) {
TransactionSelectionResult result =
selector.evaluateTransactionPreProcessing(transaction, transactionSelectionResults);
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 @@ -261,7 +268,8 @@ private TransactionSelectionResult evaluateTransactionPreProcessing(

// Process the transaction through external selectors
for (var selector : externalTransactionSelectors) {
TransactionSelectionResult result = selector.evaluateTransactionPreProcessing(transaction);
TransactionSelectionResult result =
selector.evaluateTransactionPreProcessing(pendingTransaction);
// If the transaction is not selected by any external selector, return the result
if (!result.equals(TransactionSelectionResult.SELECTED)) {
return result;
Expand All @@ -277,18 +285,19 @@ private TransactionSelectionResult evaluateTransactionPreProcessing(
* whether the transaction should be included in a block. If the transaction is selected by all
* selectors, it returns SELECTED.
*
* @param transaction The transaction to be evaluated.
* @param pendingTransaction The transaction to be evaluated.
* @param processingResult The result of the transaction processing.
* @return The result of the transaction selection process.
*/
private TransactionSelectionResult evaluateTransactionPostProcessing(
final Transaction transaction, final TransactionProcessingResult processingResult) {
final PendingTransaction pendingTransaction,
final TransactionProcessingResult processingResult) {

// Process the transaction through internal selectors
for (var selector : transactionSelectors) {
TransactionSelectionResult result =
selector.evaluateTransactionPostProcessing(
transaction, transactionSelectionResults, processingResult);
pendingTransaction, transactionSelectionResults, processingResult);
// If the transaction is not selected by any selector, return the result
if (!result.equals(TransactionSelectionResult.SELECTED)) {
return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext;
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;

Expand All @@ -34,24 +34,25 @@ public AbstractTransactionSelector(final BlockSelectionContext context) {
/**
* Evaluates a transaction in the context of other transactions in the same block.
*
* @param transaction The transaction to be evaluated within a block.
* @param pendingTransaction The transaction to be evaluated within a block.
* @param blockTransactionResults The results of other transaction evaluations in the same block.
* @return The result of the transaction evaluation
*/
public abstract TransactionSelectionResult evaluateTransactionPreProcessing(
final Transaction transaction, final TransactionSelectionResults blockTransactionResults);
final PendingTransaction pendingTransaction,
final TransactionSelectionResults blockTransactionResults);

/**
* Evaluates a transaction considering other transactions in the same block and a transaction
* processing result.
*
* @param transaction The transaction to be evaluated.
* @param pendingTransaction The transaction to be evaluated.
* @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 Transaction transaction,
final PendingTransaction pendingTransaction,
final TransactionSelectionResults blockTransactionResults,
final TransactionProcessingResult processingResult);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext;
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;

Expand All @@ -38,22 +39,22 @@ public BlobPriceTransactionSelector(final BlockSelectionContext context) {
/**
* Evaluates a transaction considering its blob price.
*
* @param transaction The transaction to be evaluated.
* @param pendingTransaction The transaction to be evaluated.
* @param ignored The results of other transaction evaluations in the same block.
* @return The result of the transaction selection.
*/
@Override
public TransactionSelectionResult evaluateTransactionPreProcessing(
final Transaction transaction, final TransactionSelectionResults ignored) {
if (transactionBlobPriceBelowMin(transaction)) {
final PendingTransaction pendingTransaction, final TransactionSelectionResults ignored) {
if (transactionBlobPriceBelowMin(pendingTransaction.getTransaction())) {
return TransactionSelectionResult.BLOB_PRICE_BELOW_CURRENT_MIN;
}
return TransactionSelectionResult.SELECTED;
}

@Override
public TransactionSelectionResult evaluateTransactionPostProcessing(
final Transaction transaction,
final PendingTransaction pendingTransaction,
final TransactionSelectionResults blockTransactionResults,
final TransactionProcessingResult processingResult) {
// All necessary checks were done in the pre-processing method, so nothing to do here.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext;
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;

Expand All @@ -39,19 +40,20 @@ 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 transaction The transaction to be evaluated.
* @param pendingTransaction The transaction to be evaluated.
* @param transactionSelectionResults The results of other transaction evaluations in the same
* block.
* @return The result of the transaction selection.
*/
@Override
public TransactionSelectionResult evaluateTransactionPreProcessing(
final Transaction transaction,
final PendingTransaction pendingTransaction,
final TransactionSelectionResults transactionSelectionResults) {
if (transactionTooLargeForBlock(transaction, transactionSelectionResults)) {
if (transactionTooLargeForBlock(
pendingTransaction.getTransaction(), transactionSelectionResults)) {
LOG.atTrace()
.setMessage("Transaction {} too large to select for block creation")
.addArgument(transaction::toTraceLog)
.addArgument(pendingTransaction::toTraceLog)
.log();
if (blockOccupancyAboveThreshold(transactionSelectionResults)) {
LOG.trace("Block occupancy above threshold, completing operation");
Expand All @@ -68,7 +70,7 @@ public TransactionSelectionResult evaluateTransactionPreProcessing(

@Override
public TransactionSelectionResult evaluateTransactionPostProcessing(
final Transaction transaction,
final PendingTransaction pendingTransaction,
final TransactionSelectionResults blockTransactionResults,
final TransactionProcessingResult processingResult) {
// All necessary checks were done in the pre-processing method, so nothing to do here.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext;
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;

Expand All @@ -40,22 +41,22 @@ 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 transaction The transaction to be evaluated.
* @param pendingTransaction The transaction to be evaluated.
* @param ignored The results of other transaction evaluations in the same block.
* @return The result of the transaction selection.
*/
@Override
public TransactionSelectionResult evaluateTransactionPreProcessing(
final Transaction transaction, final TransactionSelectionResults ignored) {
if (transactionCurrentPriceBelowMin(transaction)) {
final PendingTransaction pendingTransaction, final TransactionSelectionResults ignored) {
if (transactionCurrentPriceBelowMin(pendingTransaction)) {
return TransactionSelectionResult.CURRENT_TX_PRICE_BELOW_MIN;
}
return TransactionSelectionResult.SELECTED;
}

@Override
public TransactionSelectionResult evaluateTransactionPostProcessing(
final Transaction transaction,
final PendingTransaction pendingTransaction,
final TransactionSelectionResults blockTransactionResults,
final TransactionProcessingResult processingResult) {
// All necessary checks were done in the pre-processing method, so nothing to do here.
Expand All @@ -65,14 +66,15 @@ public TransactionSelectionResult evaluateTransactionPostProcessing(
/**
* Checks if the transaction's current price is below the minimum.
*
* @param transaction The transaction to be checked.
* @param pendingTransaction The transaction to be checked.
* @return True if the transaction's current price is below the minimum, false otherwise.
*/
private boolean transactionCurrentPriceBelowMin(final Transaction transaction) {
private boolean transactionCurrentPriceBelowMin(final PendingTransaction pendingTransaction) {
final Transaction transaction = pendingTransaction.getTransaction();
// Here we only care about EIP1159 since for Frontier and local transactions the checks
// that we do when accepting them in the pool are enough
if (transaction.getType().supports1559FeeMarket()
&& !context.transactionPool().isLocalSender(transaction.getSender())) {
&& !pendingTransaction.isReceivedFromLocalSource()) {

// For EIP1559 transactions, the price is dynamic and depends on network conditions, so we can
// only calculate at this time the current minimum price the transaction is willing to pay
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext;
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;
Expand All @@ -40,7 +41,8 @@ public ProcessingResultTransactionSelector(final BlockSelectionContext context)

@Override
public TransactionSelectionResult evaluateTransactionPreProcessing(
final Transaction transaction, final TransactionSelectionResults blockTransactionResults) {
final PendingTransaction pendingTransaction,
final TransactionSelectionResults blockTransactionResults) {
// All checks depend on processingResult and will be done in the post-processing method, so
// nothing to do here.
return TransactionSelectionResult.SELECTED;
Expand All @@ -51,20 +53,20 @@ public TransactionSelectionResult evaluateTransactionPreProcessing(
* result. If the processing result is invalid, it determines the selection result for the invalid
* result.
*
* @param transaction The transaction to be evaluated.
* @param pendingTransaction The transaction to be evaluated.
* @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 Transaction transaction,
final PendingTransaction pendingTransaction,
final TransactionSelectionResults blockTransactionResults,
final TransactionProcessingResult processingResult) {

if (processingResult.isInvalid()) {
return transactionSelectionResultForInvalidResult(
transaction, processingResult.getValidationResult());
pendingTransaction.getTransaction(), processingResult.getValidationResult());
}
return TransactionSelectionResult.SELECTED;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -552,10 +552,10 @@ public void transactionSelectionPluginShouldWork() {

final TransactionSelectorFactory transactionSelectorFactory =
() ->
(tx) -> {
if (tx.equals(notSelectedTransient))
pendingTx -> {
if (pendingTx.getTransaction().equals(notSelectedTransient))
return TransactionSelectionResult.invalidTransient("transient");
if (tx.equals(notSelectedInvalid))
if (pendingTx.getTransaction().equals(notSelectedInvalid))
return TransactionSelectionResult.invalid("invalid");
return TransactionSelectionResult.SELECTED;
};
Expand All @@ -572,7 +572,7 @@ public void transactionSelectionPluginShouldWork() {
transactionSelectorFactory);

transactionPool.addRemoteTransactions(
List.of(selected, notSelectedInvalid, notSelectedTransient));
List.of(selected, notSelectedTransient, notSelectedInvalid));

final TransactionSelectionResults transactionSelectionResults =
selector.buildTransactionListForBlock();
Expand Down
Loading

0 comments on commit 987d33c

Please sign in to comment.