Skip to content

Commit

Permalink
Detailed transaction selection stats for layered txpool (hyperledger#…
Browse files Browse the repository at this point in the history
…5492)


Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
  • Loading branch information
fab-10 authored May 30, 2023
1 parent f19fb64 commit 925b915
Show file tree
Hide file tree
Showing 10 changed files with 277 additions and 113 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.blockcreation.BlockTransactionSelector.TransactionSelectionResults;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockBody;
import org.hyperledger.besu.ethereum.core.BlockHeader;
Expand Down Expand Up @@ -172,7 +173,7 @@ protected BlockCreationResult createBlock(
final List<BlockHeader> ommers = maybeOmmers.orElse(selectOmmers());

throwIfStopped();
final BlockTransactionSelector.TransactionSelectionResults transactionResults =
final TransactionSelectionResults transactionResults =
selectTransactions(
processableBlockHeader,
disposableWorldState,
Expand All @@ -181,6 +182,8 @@ protected BlockCreationResult createBlock(
dataGasPrice,
newProtocolSpec);

transactionResults.logSelectionStats();

throwIfStopped();

final Optional<WithdrawalsProcessor> maybeWithdrawalsProcessor =
Expand Down Expand Up @@ -257,8 +260,7 @@ protected BlockCreationResult createBlock(
}

private DataGas computeExcessDataGas(
BlockTransactionSelector.TransactionSelectionResults transactionResults,
ProtocolSpec newProtocolSpec) {
TransactionSelectionResults transactionResults, ProtocolSpec newProtocolSpec) {

if (newProtocolSpec.getFeeMarket().implementsDataFee()) {
final var gasCalculator = newProtocolSpec.getGasCalculator();
Expand All @@ -276,7 +278,7 @@ private DataGas computeExcessDataGas(
return null;
}

private BlockTransactionSelector.TransactionSelectionResults selectTransactions(
private TransactionSelectionResults selectTransactions(
final ProcessableBlockHeader processableBlockHeader,
final MutableWorldState disposableWorldState,
final Optional<List<Transaction>> transactions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions.TransactionSelectionResult;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionSelectionResult;
import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
Expand All @@ -43,6 +43,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CancellationException;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -71,50 +72,17 @@
* not cleared between executions of buildTransactionListForBlock().
*/
public class BlockTransactionSelector {
public static class TransactionValidationResult {
private final Transaction transaction;
private final ValidationResult<TransactionInvalidReason> validationResult;

public TransactionValidationResult(
final Transaction transaction,
final ValidationResult<TransactionInvalidReason> validationResult) {
this.transaction = transaction;
this.validationResult = validationResult;
}

public Transaction getTransaction() {
return transaction;
}

public ValidationResult<TransactionInvalidReason> getValidationResult() {
return validationResult;
}

@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TransactionValidationResult that = (TransactionValidationResult) o;
return Objects.equals(transaction, that.transaction)
&& Objects.equals(validationResult, that.validationResult);
}

@Override
public int hashCode() {
return Objects.hash(transaction, validationResult);
}
}
public record TransactionValidationResult(
Transaction transaction, ValidationResult<TransactionInvalidReason> validationResult) {}

public static class TransactionSelectionResults {
private final List<Transaction> transactions = Lists.newArrayList();
private final Map<TransactionType, List<Transaction>> transactionsByType =
new EnumMap<>(TransactionType.class);
private final List<TransactionReceipt> receipts = Lists.newArrayList();
private final List<TransactionValidationResult> invalidTransactions = Lists.newArrayList();
private final List<TransactionSelectionResult> selectionResults = Lists.newArrayList();
private long cumulativeGasUsed = 0;
private long cumulativeDataGasUsed = 0;

Expand Down Expand Up @@ -170,6 +138,41 @@ public List<TransactionValidationResult> getInvalidTransactions() {
return invalidTransactions;
}

public void addSelectionResult(final TransactionSelectionResult res) {
selectionResults.add(res);
}

public void logSelectionStats() {
if (LOG.isDebugEnabled()) {
final Map<TransactionSelectionResult, Long> selectionStats =
selectionResults.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

LOG.debug(
"Selection stats: Totals[Evaluated={}, Selected={}, Skipped={}, Dropped={}]; Detailed[{}]",
selectionResults.size(),
selectionStats.entrySet().stream()
.filter(e -> e.getKey().selected())
.map(Map.Entry::getValue)
.mapToInt(Long::intValue)
.sum(),
selectionStats.entrySet().stream()
.filter(e -> !e.getKey().selected())
.map(Map.Entry::getValue)
.mapToInt(Long::intValue)
.sum(),
selectionStats.entrySet().stream()
.filter(e -> e.getKey().discard())
.map(Map.Entry::getValue)
.mapToInt(Long::intValue)
.sum(),
selectionStats.entrySet().stream()
.map(e -> e.getKey().toString() + "=" + e.getValue())
.sorted()
.collect(Collectors.joining(", ")));
}
}

@Override
public boolean equals(final Object o) {
if (this == o) {
Expand Down Expand Up @@ -265,9 +268,13 @@ public TransactionSelectionResults buildTransactionListForBlock() {
.addArgument(pendingTransactions.logStats())
.log();
pendingTransactions.selectTransactions(
pendingTransaction -> evaluateTransaction(pendingTransaction, false));
pendingTransaction -> {
final var res = evaluateTransaction(pendingTransaction, false);
transactionSelectionResult.addSelectionResult(res);
return res;
});
LOG.atTrace()
.setMessage("Transaction selection result result {}")
.setMessage("Transaction selection result {}")
.addArgument(transactionSelectionResult::toTraceLog)
.log();
return transactionSelectionResult;
Expand All @@ -280,7 +287,9 @@ public TransactionSelectionResults buildTransactionListForBlock() {
* @return The {@code TransactionSelectionResults} results of transaction evaluation.
*/
public TransactionSelectionResults evaluateTransactions(final List<Transaction> transactions) {
transactions.forEach(transaction -> evaluateTransaction(transaction, true));
transactions.forEach(
transaction ->
transactionSelectionResult.addSelectionResult(evaluateTransaction(transaction, true)));
return transactionSelectionResult;
}

Expand All @@ -306,20 +315,20 @@ private TransactionSelectionResult evaluateTransaction(
.log();
if (blockOccupancyAboveThreshold()) {
LOG.trace("Block occupancy above threshold, completing operation");
return TransactionSelectionResult.COMPLETE_OPERATION;
return TransactionSelectionResult.BLOCK_OCCUPANCY_ABOVE_THRESHOLD;
} else if (blockFull()) {
LOG.trace("Block full, completing operation");
return TransactionSelectionResult.COMPLETE_OPERATION;
return TransactionSelectionResult.BLOCK_FULL;
} else {
return TransactionSelectionResult.CONTINUE;
return TransactionSelectionResult.TX_TOO_LARGE_FOR_REMAINING_GAS;
}
}

if (transactionCurrentPriceBelowMin(transaction)) {
return TransactionSelectionResult.CONTINUE;
return TransactionSelectionResult.CURRENT_TX_PRICE_BELOW_MIN;
}
if (transactionDataPriceBelowMin(transaction)) {
return TransactionSelectionResult.CONTINUE;
return TransactionSelectionResult.DATA_PRICE_BELOW_CURRENT_MIN;
}

final WorldUpdater worldStateUpdater = worldState.updater();
Expand Down Expand Up @@ -354,7 +363,7 @@ private TransactionSelectionResult evaluateTransaction(
return transactionSelectionResultForInvalidResult(
transaction, effectiveResult.getValidationResult());
}
return TransactionSelectionResult.CONTINUE;
return TransactionSelectionResult.SELECTED;
}

private boolean transactionDataPriceBelowMin(final Transaction transaction) {
Expand Down Expand Up @@ -405,15 +414,15 @@ private TransactionSelectionResult transactionSelectionResultForInvalidResult(
.addArgument(invalidReason)
.addArgument(transaction::toTraceLog)
.log();
return TransactionSelectionResult.CONTINUE;
return TransactionSelectionResult.invalidTransient(invalidReason.name());
}
// If the transaction was invalid for any other reason, delete it, and continue.
LOG.atTrace()
.setMessage("Delete invalid transaction {}, reason {}")
.addArgument(transaction::toTraceLog)
.addArgument(invalidReason)
.log();
return TransactionSelectionResult.DELETE_TRANSACTION_AND_CONTINUE;
return TransactionSelectionResult.invalid(invalidReason.name());
}

private boolean isTransientValidationError(final TransactionInvalidReason invalidReason) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ public abstract class AbstractBlockTransactionSelectorTest {
protected static final double MIN_OCCUPANCY_100_PERCENT = 1;
protected static final KeyPair keyPair =
SignatureAlgorithmFactory.getInstance().generateKeyPair();

protected final MetricsSystem metricsSystem = new NoOpMetricsSystem();

protected final Blockchain blockchain = new ReferenceTestBlockchain();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,6 @@ default void signalInvalidAndRemoveDependentTransactions(final Transaction trans

boolean isLocalSender(Address sender);

enum TransactionSelectionResult {
DELETE_TRANSACTION_AND_CONTINUE,
CONTINUE,
COMPLETE_OPERATION
}

@FunctionalInterface
interface TransactionSelector {
TransactionSelectionResult evaluateTransaction(Transaction transaction);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,29 +349,22 @@ public synchronized void selectTransactions(
.forEach(
candidatePendingTx -> {
alreadyChecked.add(candidatePendingTx.getHash());
switch (selector.evaluateTransaction(
candidatePendingTx.getTransaction())) {
case CONTINUE:
LOG.atTrace()
.setMessage("CONTINUE: Transaction {}")
.addArgument(candidatePendingTx::toTraceLog)
.log();
break;
case DELETE_TRANSACTION_AND_CONTINUE:
invalidTransactions.add(candidatePendingTx);
LOG.atTrace()
.setMessage("DELETE_TRANSACTION_AND_CONTINUE: Transaction {}")
.addArgument(candidatePendingTx::toTraceLog)
.log();
logTransactionForReplayDelete(candidatePendingTx);
break;
case COMPLETE_OPERATION:
completed.set(true);
LOG.atTrace()
.setMessage("COMPLETE_OPERATION: Transaction {}")
.addArgument(candidatePendingTx::toTraceLog)
.log();
break;
final var res =
selector.evaluateTransaction(candidatePendingTx.getTransaction());

LOG.atTrace()
.setMessage("Selection result {} for transaction {}")
.addArgument(res)
.addArgument(candidatePendingTx::toTraceLog)
.log();

if (res.discard()) {
invalidTransactions.add(candidatePendingTx);
logTransactionForReplayDelete(candidatePendingTx);
}

if (res.stop()) {
completed.set(true);
}
}));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.hyperledger.besu.ethereum.eth.transactions.TransactionAddedResult;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolReplacementHandler;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionSelectionResult;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.account.AccountState;
Expand Down Expand Up @@ -263,19 +264,16 @@ public void selectTransactions(final TransactionSelector selector) {
highestPriorityPendingTransaction.getTransaction())) {
final TransactionSelectionResult result =
selector.evaluateTransaction(transactionToProcess);
switch (result) {
case DELETE_TRANSACTION_AND_CONTINUE:
transactionsToRemove.add(transactionToProcess);
transactionsToRemove.addAll(
signalInvalidAndGetDependentTransactions(transactionToProcess));
break;
case CONTINUE:
break;
case COMPLETE_OPERATION:
transactionsToRemove.forEach(this::removeTransaction);
return;
default:
throw new RuntimeException("Illegal value for TransactionSelectionResult.");

if (result.discard()) {
transactionsToRemove.add(transactionToProcess);
transactionsToRemove.addAll(
signalInvalidAndGetDependentTransactions(transactionToProcess));
}

if (result.stop()) {
transactionsToRemove.forEach(this::removeTransaction);
return;
}
}
}
Expand Down
Loading

0 comments on commit 925b915

Please sign in to comment.