Use PendingTransaction in BlockTransactionSelector (#5966)

Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
pull/5974/head
Fabio Di Fabio 1 year ago committed by GitHub
parent dc47867054
commit 987d33c63a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 39
      datatypes/src/main/java/org/hyperledger/besu/datatypes/PendingTransaction.java
  2. 33
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java
  3. 11
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/AbstractTransactionSelector.java
  4. 9
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlobPriceTransactionSelector.java
  5. 12
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlockSizeTransactionSelector.java
  6. 16
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/PriceTransactionSelector.java
  7. 10
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/ProcessingResultTransactionSelector.java
  8. 8
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java
  9. 73
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/AccountTransactionOrderTest.java
  10. 11
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransaction.java
  11. 2
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransactions.java
  12. 3
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayeredPendingTransactions.java
  13. 15
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/AbstractPendingTransactionsSorter.java
  14. 24
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/AccountTransactionOrder.java
  15. 34
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayeredPendingTransactionsTest.java
  16. 28
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/AbstractPendingTransactionsTestBase.java
  17. 75
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/AccountTransactionOrderTest.java
  18. 2
      plugin-api/build.gradle
  19. 7
      plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/TransactionSelector.java

@ -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();
}

@ -27,6 +27,7 @@ import org.hyperledger.besu.ethereum.core.MutableWorldState;
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;
@ -142,7 +143,8 @@ public class BlockTransactionSelector {
pendingTransaction -> {
final var res = evaluateTransaction(pendingTransaction);
if (!res.selected()) {
transactionSelectionResults.updateNotSelected(pendingTransaction, res);
transactionSelectionResults.updateNotSelected(
pendingTransaction.getTransaction(), res);
}
return res;
});
@ -165,7 +167,7 @@ public class BlockTransactionSelector {
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);
}
@ -182,12 +184,16 @@ public class BlockTransactionSelector {
* 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;
}
@ -209,7 +215,7 @@ public class BlockTransactionSelector {
blockSelectionContext.blobGasPrice());
var transactionWithProcessingContextResult =
evaluateTransactionPostProcessing(transaction, effectiveResult);
evaluateTransactionPostProcessing(pendingTransaction, effectiveResult);
if (!transactionWithProcessingContextResult.selected()) {
return transactionWithProcessingContextResult;
}
@ -243,16 +249,17 @@ public class BlockTransactionSelector {
* 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;
@ -261,7 +268,8 @@ public class BlockTransactionSelector {
// 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;
@ -277,18 +285,19 @@ public class BlockTransactionSelector {
* 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;

@ -16,7 +16,7 @@ package org.hyperledger.besu.ethereum.blockcreation.txselection.selectors;
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;
@ -34,24 +34,25 @@ public abstract class AbstractTransactionSelector {
/**
* 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);
}

@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.blockcreation.txselection.selectors;
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;
@ -38,14 +39,14 @@ public class BlobPriceTransactionSelector extends AbstractTransactionSelector {
/**
* 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;
@ -53,7 +54,7 @@ public class BlobPriceTransactionSelector extends AbstractTransactionSelector {
@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.

@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.blockcreation.txselection.selectors;
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;
@ -39,19 +40,20 @@ public class BlockSizeTransactionSelector extends AbstractTransactionSelector {
* 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");
@ -68,7 +70,7 @@ public class BlockSizeTransactionSelector extends AbstractTransactionSelector {
@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.

@ -18,6 +18,7 @@ import org.hyperledger.besu.datatypes.Wei;
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;
@ -40,14 +41,14 @@ public class PriceTransactionSelector extends AbstractTransactionSelector {
* 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;
@ -55,7 +56,7 @@ public class PriceTransactionSelector extends AbstractTransactionSelector {
@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.
@ -65,14 +66,15 @@ public class PriceTransactionSelector extends AbstractTransactionSelector {
/**
* 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

@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.blockcreation.txselection.selectors;
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;
@ -40,7 +41,8 @@ public class ProcessingResultTransactionSelector extends AbstractTransactionSele
@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;
@ -51,20 +53,20 @@ public class ProcessingResultTransactionSelector extends AbstractTransactionSele
* 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;
}

@ -552,10 +552,10 @@ public abstract class AbstractBlockTransactionSelectorTest {
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;
};
@ -572,7 +572,7 @@ public abstract class AbstractBlockTransactionSelectorTest {
transactionSelectorFactory);
transactionPool.addRemoteTransactions(
List.of(selected, notSelectedInvalid, notSelectedTransient));
List.of(selected, notSelectedTransient, notSelectedInvalid));
final TransactionSelectionResults transactionSelectionResults =
selector.buildTransactionListForBlock();

@ -1,73 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* 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.core;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
public class AccountTransactionOrderTest {
private static final KeyPair KEYS = SignatureAlgorithmFactory.getInstance().generateKeyPair();
private final Transaction transaction1 = transaction(1);
private final Transaction transaction2 = transaction(2);
private final Transaction transaction3 = transaction(3);
private final Transaction transaction4 = transaction(4);
private final AccountTransactionOrder accountTransactionOrder =
new AccountTransactionOrder(
Stream.of(transaction1, transaction2, transaction3, transaction4));
@Test
public void shouldProcessATransactionImmediatelyIfItsTheLowestNonce() {
assertThat(accountTransactionOrder.transactionsToProcess(transaction1))
.containsExactly(transaction1);
}
@Test
public void shouldDeferProcessingATransactionIfItIsNotTheLowestNonce() {
assertThat(accountTransactionOrder.transactionsToProcess(transaction2)).isEmpty();
}
@Test
public void shouldProcessDeferredTransactionsAfterPrerequisiteIsProcessed() {
assertThat(accountTransactionOrder.transactionsToProcess(transaction2)).isEmpty();
assertThat(accountTransactionOrder.transactionsToProcess(transaction3)).isEmpty();
assertThat(accountTransactionOrder.transactionsToProcess(transaction1))
.containsExactly(transaction1, transaction2, transaction3);
}
@Test
public void shouldNotProcessDeferredTransactionsThatAreNotYetDue() {
assertThat(accountTransactionOrder.transactionsToProcess(transaction2)).isEmpty();
assertThat(accountTransactionOrder.transactionsToProcess(transaction4)).isEmpty();
assertThat(accountTransactionOrder.transactionsToProcess(transaction1))
.containsExactly(transaction1, transaction2);
assertThat(accountTransactionOrder.transactionsToProcess(transaction3))
.containsExactly(transaction3, transaction4);
}
private Transaction transaction(final int nonce) {
return new TransactionTestFixture().nonce(nonce).createTransaction(KEYS);
}
}

@ -28,7 +28,8 @@ import java.util.concurrent.atomic.AtomicLong;
* Tracks the additional metadata associated with transactions to enable prioritization for mining
* and deciding which transactions to drop when the transaction pool reaches its size limit.
*/
public abstract class PendingTransaction {
public abstract class PendingTransaction
implements org.hyperledger.besu.datatypes.PendingTransaction {
static final int NOT_INITIALIZED = -1;
static final int FRONTIER_BASE_MEMORY_SIZE = 944;
static final int ACCESS_LIST_BASE_MEMORY_SIZE = 944;
@ -52,6 +53,7 @@ public abstract class PendingTransaction {
this.sequence = TRANSACTIONS_ADDED.getAndIncrement();
}
@Override
public Transaction getTransaction() {
return transaction;
}
@ -72,12 +74,11 @@ public abstract class PendingTransaction {
return transaction.getSender();
}
public abstract boolean isReceivedFromLocalSource();
public Hash getHash() {
return transaction.getHash();
}
@Override
public long getAddedAt() {
return addedAt;
}
@ -184,6 +185,8 @@ public abstract class PendingTransaction {
+ addedAt
+ ", sequence="
+ sequence
+ ", isLocal="
+ isReceivedFromLocalSource()
+ '}';
}
@ -192,6 +195,8 @@ public abstract class PendingTransaction {
+ sequence
+ ", addedAt: "
+ addedAt
+ ", isLocal="
+ isReceivedFromLocalSource()
+ ", "
+ transaction.toTraceLog()
+ "}";

@ -88,6 +88,6 @@ public interface PendingTransactions {
@FunctionalInterface
interface TransactionSelector {
TransactionSelectionResult evaluateTransaction(Transaction transaction);
TransactionSelectionResult evaluateTransaction(PendingTransaction pendingTransaction);
}
}

@ -342,8 +342,7 @@ public class LayeredPendingTransactions implements PendingTransactions {
.forEach(
candidatePendingTx -> {
alreadyChecked.add(candidatePendingTx.getHash());
final var res =
selector.evaluateTransaction(candidatePendingTx.getTransaction());
final var res = selector.evaluateTransaction(candidatePendingTx);
LOG.atTrace()
.setMessage("Selection result {} for transaction {}")

@ -22,7 +22,6 @@ import static org.hyperledger.besu.ethereum.eth.transactions.TransactionAddedRes
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.core.AccountTransactionOrder;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
@ -259,16 +258,15 @@ public abstract class AbstractPendingTransactionsSorter implements PendingTransa
accountTransactions.computeIfAbsent(
highestPriorityPendingTransaction.getSender(), this::createSenderTransactionOrder);
for (final Transaction transactionToProcess :
accountTransactionOrder.transactionsToProcess(
highestPriorityPendingTransaction.getTransaction())) {
for (final PendingTransaction transactionToProcess :
accountTransactionOrder.transactionsToProcess(highestPriorityPendingTransaction)) {
final TransactionSelectionResult result =
selector.evaluateTransaction(transactionToProcess);
if (result.discard()) {
transactionsToRemove.add(transactionToProcess);
transactionsToRemove.add(transactionToProcess.getTransaction());
transactionsToRemove.addAll(
signalInvalidAndGetDependentTransactions(transactionToProcess));
signalInvalidAndGetDependentTransactions(transactionToProcess.getTransaction()));
}
if (result.stop()) {
@ -283,10 +281,7 @@ public abstract class AbstractPendingTransactionsSorter implements PendingTransa
private AccountTransactionOrder createSenderTransactionOrder(final Address address) {
return new AccountTransactionOrder(
transactionsBySender
.get(address)
.streamPendingTransactions()
.map(PendingTransaction::getTransaction));
transactionsBySender.get(address).streamPendingTransactions());
}
private TransactionAddedResult addTransactionForSenderAndNonce(

@ -12,7 +12,9 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.core;
package org.hyperledger.besu.ethereum.eth.transactions.sorter;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import java.util.ArrayList;
import java.util.Comparator;
@ -23,12 +25,14 @@ import java.util.stream.Stream;
public class AccountTransactionOrder {
private static final Comparator<Transaction> SORT_BY_NONCE =
Comparator.comparing(Transaction::getNonce);
private final NavigableSet<Transaction> transactionsForSender = new TreeSet<>(SORT_BY_NONCE);
private final NavigableSet<Transaction> deferredTransactions = new TreeSet<>(SORT_BY_NONCE);
private static final Comparator<PendingTransaction> SORT_BY_NONCE =
Comparator.comparing(PendingTransaction::getNonce);
private final NavigableSet<PendingTransaction> transactionsForSender =
new TreeSet<>(SORT_BY_NONCE);
private final NavigableSet<PendingTransaction> deferredTransactions =
new TreeSet<>(SORT_BY_NONCE);
public AccountTransactionOrder(final Stream<Transaction> senderTransactions) {
public AccountTransactionOrder(final Stream<PendingTransaction> senderTransactions) {
senderTransactions.forEach(this.transactionsForSender::add);
}
@ -44,14 +48,14 @@ public class AccountTransactionOrder {
* order. Must be from the sender this instance is ordering.
* @return the transactions from this sender that are now due to be processed, in order.
*/
public Iterable<Transaction> transactionsToProcess(
final Transaction nextTransactionInPriorityOrder) {
public Iterable<PendingTransaction> transactionsToProcess(
final PendingTransaction nextTransactionInPriorityOrder) {
deferredTransactions.add(nextTransactionInPriorityOrder);
final List<Transaction> transactionsToApply = new ArrayList<>();
final List<PendingTransaction> transactionsToApply = new ArrayList<>();
while (!deferredTransactions.isEmpty()
&& !transactionsForSender.isEmpty()
&& deferredTransactions.first().equals(transactionsForSender.first())) {
final Transaction transaction = deferredTransactions.first();
final PendingTransaction transaction = deferredTransactions.first();
transactionsToApply.add(transaction);
deferredTransactions.remove(transaction);
transactionsForSender.remove(transaction);

@ -302,8 +302,8 @@ public class LayeredPendingTransactionsTest extends BaseTransactionPoolTest {
final List<Transaction> parsedTransactions = new ArrayList<>();
pendingTransactions.selectTransactions(
transaction -> {
parsedTransactions.add(transaction);
pendingTX -> {
parsedTransactions.add(pendingTX.getTransaction());
return selectionResult;
});
@ -322,8 +322,8 @@ public class LayeredPendingTransactionsTest extends BaseTransactionPoolTest {
final List<Transaction> parsedTransactions = new ArrayList<>();
pendingTransactions.selectTransactions(
transaction -> {
parsedTransactions.add(transaction);
pendingTx -> {
parsedTransactions.add(pendingTx.getTransaction());
return SELECTED;
});
@ -342,8 +342,8 @@ public class LayeredPendingTransactionsTest extends BaseTransactionPoolTest {
final List<Transaction> parsedTransactions = new ArrayList<>();
pendingTransactions.selectTransactions(
transaction -> {
parsedTransactions.add(transaction);
pendingTx -> {
parsedTransactions.add(pendingTx.getTransaction());
return SELECTED;
});
@ -363,8 +363,8 @@ public class LayeredPendingTransactionsTest extends BaseTransactionPoolTest {
final List<Transaction> iterationOrder = new ArrayList<>(3);
pendingTransactions.selectTransactions(
transaction -> {
iterationOrder.add(transaction);
pendingTx -> {
iterationOrder.add(pendingTx.getTransaction());
return SELECTED;
});
@ -387,10 +387,10 @@ public class LayeredPendingTransactionsTest extends BaseTransactionPoolTest {
final List<Transaction> iterationOrder = new ArrayList<>(3);
pendingTransactions.selectTransactions(
transaction -> {
iterationOrder.add(transaction);
pendingTx -> {
iterationOrder.add(pendingTx.getTransaction());
// pretending that the 2nd tx of the 1st sender is not selected
return transaction.getNonce() == 1 ? skipSelectionResult : SELECTED;
return pendingTx.getNonce() == 1 ? skipSelectionResult : SELECTED;
});
// the 3rd tx of the 1st must not be processed, since the 2nd is skipped
@ -420,8 +420,8 @@ public class LayeredPendingTransactionsTest extends BaseTransactionPoolTest {
final List<Transaction> iterationOrder = new ArrayList<>(2);
pendingTransactions.selectTransactions(
transaction -> {
iterationOrder.add(transaction);
pendingTx -> {
iterationOrder.add(pendingTx.getTransaction());
return SELECTED;
});
@ -435,8 +435,8 @@ public class LayeredPendingTransactionsTest extends BaseTransactionPoolTest {
final List<Transaction> parsedTransactions = new ArrayList<>(1);
pendingTransactions.selectTransactions(
transaction -> {
parsedTransactions.add(transaction);
pendingTx -> {
parsedTransactions.add(pendingTx.getTransaction());
return TransactionSelectionResult.invalid(UPFRONT_COST_EXCEEDS_BALANCE.name());
});
@ -455,8 +455,8 @@ public class LayeredPendingTransactionsTest extends BaseTransactionPoolTest {
final List<Transaction> parsedTransactions = new ArrayList<>(1);
pendingTransactions.selectTransactions(
transaction -> {
parsedTransactions.add(transaction);
pendingTx -> {
parsedTransactions.add(pendingTx.getTransaction());
return TransactionSelectionResult.invalidTransient(
GAS_PRICE_BELOW_CURRENT_BASE_FEE.name());
});

@ -315,8 +315,8 @@ public abstract class AbstractPendingTransactionsTestBase {
final List<Transaction> parsedTransactions = Lists.newArrayList();
transactions.selectTransactions(
transaction -> {
parsedTransactions.add(transaction);
pendingTx -> {
parsedTransactions.add(pendingTx.getTransaction());
return TransactionSelectionResult.BLOCK_OCCUPANCY_ABOVE_THRESHOLD;
});
@ -331,8 +331,8 @@ public abstract class AbstractPendingTransactionsTestBase {
final List<Transaction> parsedTransactions = Lists.newArrayList();
transactions.selectTransactions(
transaction -> {
parsedTransactions.add(transaction);
pendingTx -> {
parsedTransactions.add(pendingTx.getTransaction());
return SELECTED;
});
@ -351,8 +351,8 @@ public abstract class AbstractPendingTransactionsTestBase {
final List<Transaction> parsedTransactions = Lists.newArrayList();
transactions.selectTransactions(
transaction -> {
parsedTransactions.add(transaction);
pendingTx -> {
parsedTransactions.add(pendingTx.getTransaction());
return SELECTED;
});
@ -366,8 +366,8 @@ public abstract class AbstractPendingTransactionsTestBase {
final List<Transaction> parsedTransactions = Lists.newArrayList();
transactions.selectTransactions(
transaction -> {
parsedTransactions.add(transaction);
pendingTx -> {
parsedTransactions.add(pendingTx.getTransaction());
return TransactionSelectionResult.invalid(
TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE.name());
});
@ -535,8 +535,8 @@ public abstract class AbstractPendingTransactionsTestBase {
final List<Transaction> iterationOrder = new ArrayList<>();
transactions.selectTransactions(
transaction -> {
iterationOrder.add(transaction);
pendingTx -> {
iterationOrder.add(pendingTx.getTransaction());
return SELECTED;
});
@ -553,8 +553,8 @@ public abstract class AbstractPendingTransactionsTestBase {
final List<Transaction> iterationOrder = new ArrayList<>();
transactions.selectTransactions(
transaction -> {
iterationOrder.add(transaction);
pendingTx -> {
iterationOrder.add(pendingTx.getTransaction());
return SELECTED;
});
@ -575,8 +575,8 @@ public abstract class AbstractPendingTransactionsTestBase {
final List<Transaction> iterationOrder = new ArrayList<>();
transactions.selectTransactions(
transaction -> {
iterationOrder.add(transaction);
pendingTx -> {
iterationOrder.add(pendingTx.getTransaction());
return SELECTED;
});

@ -0,0 +1,75 @@
/*
* Copyright ConsenSys AG.
*
* 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.eth.transactions.sorter;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
public class AccountTransactionOrderTest {
private static final KeyPair KEYS = SignatureAlgorithmFactory.getInstance().generateKeyPair();
private final PendingTransaction pendingTx1 = new PendingTransaction.Remote((transaction(1)));
private final PendingTransaction pendingTx2 = new PendingTransaction.Remote((transaction(2)));
private final PendingTransaction pendingTx3 = new PendingTransaction.Remote((transaction(3)));
private final PendingTransaction pendingTx4 = new PendingTransaction.Remote((transaction(4)));
private final AccountTransactionOrder accountTransactionOrder =
new AccountTransactionOrder(Stream.of(pendingTx1, pendingTx2, pendingTx3, pendingTx4));
@Test
public void shouldProcessATransactionImmediatelyIfItsTheLowestNonce() {
assertThat(accountTransactionOrder.transactionsToProcess(pendingTx1))
.containsExactly(pendingTx1);
}
@Test
public void shouldDeferProcessingATransactionIfItIsNotTheLowestNonce() {
assertThat(accountTransactionOrder.transactionsToProcess(pendingTx2)).isEmpty();
}
@Test
public void shouldProcessDeferredTransactionsAfterPrerequisiteIsProcessed() {
assertThat(accountTransactionOrder.transactionsToProcess(pendingTx2)).isEmpty();
assertThat(accountTransactionOrder.transactionsToProcess(pendingTx3)).isEmpty();
assertThat(accountTransactionOrder.transactionsToProcess(pendingTx1))
.containsExactly(pendingTx1, pendingTx2, pendingTx3);
}
@Test
public void shouldNotProcessDeferredTransactionsThatAreNotYetDue() {
assertThat(accountTransactionOrder.transactionsToProcess(pendingTx2)).isEmpty();
assertThat(accountTransactionOrder.transactionsToProcess(pendingTx4)).isEmpty();
assertThat(accountTransactionOrder.transactionsToProcess(pendingTx1))
.containsExactly(pendingTx1, pendingTx2);
assertThat(accountTransactionOrder.transactionsToProcess(pendingTx3))
.containsExactly(pendingTx3, pendingTx4);
}
private Transaction transaction(final int nonce) {
return new TransactionTestFixture().nonce(nonce).createTransaction(KEYS);
}
}

@ -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 = 'ON5/4jw14IPAL/Civ3ld6tvwrLsGS9eI38w5C0xRzdY='
knownHash = 'gfZY0boUMYJoAHwou3eEhcz7A/xFvJKnjMUONZ6hY3I='
}
check.dependsOn('checkAPIChanges')

@ -15,7 +15,7 @@
package org.hyperledger.besu.plugin.services.txselection;
import org.hyperledger.besu.datatypes.Transaction;
import org.hyperledger.besu.datatypes.PendingTransaction;
import org.hyperledger.besu.plugin.Unstable;
import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
@ -26,8 +26,9 @@ public interface TransactionSelector {
* 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 transaction candidate transaction
* @param pendingTransaction candidate transaction
* @return TransactionSelectionResult that indicates whether to include the transaction
*/
TransactionSelectionResult evaluateTransactionPreProcessing(Transaction transaction);
TransactionSelectionResult evaluateTransactionPreProcessing(
PendingTransaction pendingTransaction);
}

Loading…
Cancel
Save