Skip to content

Commit

Permalink
tests
Browse files Browse the repository at this point in the history
Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
  • Loading branch information
fab-10 committed Apr 12, 2024
1 parent f91b2d8 commit 31b21b0
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,15 @@ protected void internalReplaced(final PendingTransaction replacedTx) {
}

private boolean hasPriority(final PendingTransaction pendingTransaction) {
// check if there is space for that tx type
final var txType = pendingTransaction.getTransaction().getType();
if (txCountByType[txType.ordinal()]
>= poolConfig
.getMaxPrioritizedTransactionsByType()
.getOrDefault(txType, Integer.MAX_VALUE)) {
return false;
}

// if it does not pass the promotion filter, then has not priority
if (!promotionFilter(pendingTransaction)) {
return false;
Expand Down Expand Up @@ -129,6 +138,13 @@ public List<PendingTransaction> promote(
return List.of();
}

/**
* Here the max number of txs of a specific type that can be promoted, is defined by the
* configuration, so we return the difference between the configured max and the current count of
* txs for each time
*
* @return an array containing the max amount of txs that can be promoted for each type
*/
@Override
protected int[] getMaxPromotionsPerType() {
final var allTypes = TransactionType.values();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,14 @@ final void promoteTransactions() {
}
}

/**
* How many txs of a specified type can be promoted? This make sense when a max number of txs of a
* type can be included in a single block (ex. blob txs), to avoid filling the layer with more txs
* than the useful ones. By default, there are no limits, but each layer can define its own
* policy.
*
* @return an array containing the max amount of txs that can be promoted for each type
*/
protected int[] getMaxPromotionsPerType() {
return UNLIMITED_PROMOTIONS_PER_TYPE;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,14 +149,6 @@ private Wei calculateNextBlockBaseFee(final FeeMarket feeMarket, final BlockHead

@Override
protected boolean promotionFilter(final PendingTransaction pendingTransaction) {
// check if there is space for that tx type
final var txType = pendingTransaction.getTransaction().getType();
if (txCountByType[txType.ordinal()]
>= poolConfig
.getMaxPrioritizedTransactionsByType()
.getOrDefault(txType, Integer.MAX_VALUE)) {
return false;
}

// check if the tx is willing to pay at least the base fee
if (nextBlockBaseFee
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,13 @@ protected Transaction createEIP4844Transaction(
protected Transaction createTransaction(
final long nonce, final Wei maxGasPrice, final int payloadSize, final KeyPair keys) {

// ToDo 4844: include BLOB tx here
final TransactionType txType = TransactionType.values()[randomizeTxType.nextInt(3)];
final TransactionType txType = TransactionType.values()[randomizeTxType.nextInt(4)];

return createTransaction(txType, nonce, maxGasPrice, payloadSize, keys);
return switch (txType) {
case FRONTIER, ACCESS_LIST, EIP1559 ->
createTransaction(txType, nonce, maxGasPrice, payloadSize, keys);
case BLOB -> createTransaction(txType, nonce, maxGasPrice, payloadSize, 1, keys);
};
}

protected Transaction createTransaction(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
package org.hyperledger.besu.ethereum.eth.transactions.layered;

import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.datatypes.TransactionType.ACCESS_LIST;
import static org.hyperledger.besu.datatypes.TransactionType.BLOB;
import static org.hyperledger.besu.datatypes.TransactionType.EIP1559;
import static org.hyperledger.besu.datatypes.TransactionType.FRONTIER;
import static org.hyperledger.besu.ethereum.eth.transactions.layered.LayersTest.Sender.S1;
import static org.hyperledger.besu.ethereum.eth.transactions.layered.LayersTest.Sender.S2;
import static org.hyperledger.besu.ethereum.eth.transactions.layered.LayersTest.Sender.S3;
Expand All @@ -27,6 +31,7 @@

import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.MiningParameters;
Expand All @@ -51,7 +56,6 @@
import java.util.OptionalLong;
import java.util.stream.Stream;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
Expand All @@ -60,51 +64,29 @@ public class LayersTest extends BaseTransactionPoolTest {
private static final int MAX_PRIO_TRANSACTIONS = 3;
private static final int MAX_FUTURE_FOR_SENDER = 10;

private final TransactionPoolConfiguration poolConfig =
private static final TransactionPoolConfiguration DEFAULT_TX_POOL_CONFIG =
ImmutableTransactionPoolConfiguration.builder()
.maxPrioritizedTransactions(MAX_PRIO_TRANSACTIONS)
.maxPrioritizedTransactionsByType(Map.of(BLOB, 1))
.maxFutureBySender(MAX_FUTURE_FOR_SENDER)
.pendingTransactionsLayerMaxCapacityBytes(
new PendingTransaction.Remote(createEIP1559Transaction(0, KEYS1, 1)).memorySize() * 3)
new PendingTransaction.Remote(
new BaseTransactionPoolTest().createEIP1559Transaction(0, KEYS1, 1))
.memorySize()
* 3L)
.build();

private final TransactionPoolMetrics txPoolMetrics = new TransactionPoolMetrics(metricsSystem);

private final EvictCollectorLayer evictCollector = new EvictCollectorLayer(txPoolMetrics);
private final SparseTransactions sparseTransactions =
new SparseTransactions(
poolConfig,
evictCollector,
txPoolMetrics,
this::transactionReplacementTester,
new BlobCache());

private final ReadyTransactions readyTransactions =
new ReadyTransactions(
poolConfig,
sparseTransactions,
txPoolMetrics,
this::transactionReplacementTester,
new BlobCache());

private final BaseFeePrioritizedTransactions prioritizedTransactions =
new BaseFeePrioritizedTransactions(
poolConfig,
LayersTest::mockBlockHeader,
readyTransactions,
txPoolMetrics,
this::transactionReplacementTester,
FeeMarket.london(0L),
new BlobCache(),
MiningParameters.newDefault());

private final LayeredPendingTransactions pendingTransactions =
new LayeredPendingTransactions(poolConfig, prioritizedTransactions);

@AfterEach
void reset() {
pendingTransactions.reset();
}
private static final TransactionPoolConfiguration BLOB_TX_POOL_CONFIG =
ImmutableTransactionPoolConfiguration.builder()
.maxPrioritizedTransactions(MAX_PRIO_TRANSACTIONS)
.maxPrioritizedTransactionsByType(Map.of(BLOB, 1))
.maxFutureBySender(MAX_FUTURE_FOR_SENDER)
.pendingTransactionsLayerMaxCapacityBytes(
new PendingTransaction.Remote(
new BaseTransactionPoolTest().createEIP4844Transaction(0, KEYS1, 1, 1))
.memorySize()
* 3L)
.build();

@ParameterizedTest
@MethodSource("providerAddTransactions")
Expand Down Expand Up @@ -166,7 +148,51 @@ void prioritySenders(final Scenario scenario) {
assertScenario(scenario);
}

@ParameterizedTest
@MethodSource("providerMaxPrioritizedByType")
void maxPrioritizedByType(final Scenario scenario) {
assertScenario(scenario, BLOB_TX_POOL_CONFIG);
}

private void assertScenario(final Scenario scenario) {
assertScenario(scenario, DEFAULT_TX_POOL_CONFIG);
}

private void assertScenario(
final Scenario scenario, final TransactionPoolConfiguration poolConfig) {
final TransactionPoolMetrics txPoolMetrics = new TransactionPoolMetrics(metricsSystem);

final EvictCollectorLayer evictCollector = new EvictCollectorLayer(txPoolMetrics);
final SparseTransactions sparseTransactions =
new SparseTransactions(
poolConfig,
evictCollector,
txPoolMetrics,
(pt1, pt2) -> transactionReplacementTester(poolConfig, pt1, pt2),
new BlobCache());

final ReadyTransactions readyTransactions =
new ReadyTransactions(
poolConfig,
sparseTransactions,
txPoolMetrics,
(pt1, pt2) -> transactionReplacementTester(poolConfig, pt1, pt2),
new BlobCache());

final BaseFeePrioritizedTransactions prioritizedTransactions =
new BaseFeePrioritizedTransactions(
poolConfig,
LayersTest::mockBlockHeader,
readyTransactions,
txPoolMetrics,
(pt1, pt2) -> transactionReplacementTester(poolConfig, pt1, pt2),
FeeMarket.london(0L),
new BlobCache(),
MiningParameters.newDefault());

final LayeredPendingTransactions pendingTransactions =
new LayeredPendingTransactions(poolConfig, prioritizedTransactions);

scenario.execute(
pendingTransactions,
prioritizedTransactions,
Expand Down Expand Up @@ -1178,17 +1204,49 @@ static Stream<Arguments> providerPrioritySenders() {
.expectedDroppedForSender(S3, 0)));
}

static Stream<Arguments> providerMaxPrioritizedByType() {
return Stream.of(
Arguments.of(
new Scenario("first blob tx is prioritized")
.addForSender(S1, BLOB, 0)
.expectedPrioritizedForSender(S1, 0)),
Arguments.of(
new Scenario("multiple senders only first blob tx is prioritized")
.addForSender(S1, BLOB, 0)
.addForSender(S2, BLOB, 0)
.expectedPrioritizedForSender(S1, 0)
.expectedReadyForSender(S2, 0)),
Arguments.of(
new Scenario("same sender following blob txs are moved to ready")
.addForSender(S1, BLOB, 0, 1, 2)
.expectedPrioritizedForSender(S1, 0)
.expectedReadyForSender(S1, 1, 2)),
Arguments.of(
new Scenario("promoting txs respect prioritized count limit")
.addForSender(S1, BLOB, 0, 1, 2)
.expectedPrioritizedForSender(S1, 0)
.expectedReadyForSender(S1, 1, 2)
.confirmedForSenders(S1, 0)
.expectedPrioritizedForSender(S1, 1)
.expectedReadyForSender(S1, 2)),
Arguments.of(
new Scenario("promoting to ready is unbounded")
.addForSender(S1, BLOB, 0, 1, 2, 3, 4, 5, 6)
.expectedPrioritizedForSender(S1, 0)
.expectedReadyForSender(S1, 1, 2, 3)
.expectedSparseForSender(S1, 4, 5, 6)
.confirmedForSenders(S1, 3)
.expectedPrioritizedForSender(S1, 4)
.expectedReadyForSender(S1, 5, 6)
.expectedSparseForSenders()));
}

private static BlockHeader mockBlockHeader() {
final BlockHeader blockHeader = mock(BlockHeader.class);
when(blockHeader.getBaseFee()).thenReturn(Optional.of(Wei.ONE));
return blockHeader;
}

private boolean transactionReplacementTester(
final PendingTransaction pt1, final PendingTransaction pt2) {
return transactionReplacementTester(poolConfig, pt1, pt2);
}

private static boolean transactionReplacementTester(
final TransactionPoolConfiguration poolConfig,
final PendingTransaction pt1,
Expand Down Expand Up @@ -1233,10 +1291,14 @@ void accept(
}

Scenario addForSender(final Sender sender, final long... nonce) {
return addForSender(sender, EIP1559, nonce);
}

Scenario addForSender(final Sender sender, final TransactionType type, final long... nonce) {
Arrays.stream(nonce)
.forEach(
n -> {
final var pendingTx = getOrCreate(sender, n);
final var pendingTx = getOrCreate(sender, type, n);
actions.add(
(pending, prio, ready, sparse, dropped) -> {
final Account mockSender = mock(Account.class);
Expand Down Expand Up @@ -1288,22 +1350,49 @@ void execute(
assertExpectedDropped(dropped, lastExpectedDropped);
}

private PendingTransaction getOrCreate(final Sender sender, final long nonce) {
private PendingTransaction getOrCreate(
final Sender sender, final TransactionType type, final long nonce) {
return txsBySender
.get(sender)
.computeIfAbsent(nonce, n -> createEIP1559PendingTransactions(sender, n));
.computeIfAbsent(
nonce,
n ->
switch (type) {
case FRONTIER -> createFrontierPendingTransaction(sender, n);
case ACCESS_LIST -> createAccessListPendingTransaction(sender, n);
case EIP1559 -> createEIP1559PendingTransaction(sender, n);
case BLOB -> createBlobPendingTransaction(sender, n);
});
}

private PendingTransaction get(final Sender sender, final long nonce) {
return txsBySender.get(sender).get(nonce);
}

private PendingTransaction createEIP1559PendingTransactions(
private PendingTransaction createFrontierPendingTransaction(
final Sender sender, final long nonce) {
return createRemotePendingTransaction(
createTransaction(FRONTIER, nonce, Wei.ONE, 0, sender.key), sender.hasPriority);
}

private PendingTransaction createAccessListPendingTransaction(
final Sender sender, final long nonce) {
return createRemotePendingTransaction(
createTransaction(ACCESS_LIST, nonce, Wei.ONE, 0, sender.key), sender.hasPriority);
}

private PendingTransaction createEIP1559PendingTransaction(
final Sender sender, final long nonce) {
return createRemotePendingTransaction(
createEIP1559Transaction(nonce, sender.key, sender.gasFeeMultiplier), sender.hasPriority);
}

private PendingTransaction createBlobPendingTransaction(final Sender sender, final long nonce) {
return createRemotePendingTransaction(
createEIP4844Transaction(nonce, sender.key, sender.gasFeeMultiplier, 1),
sender.hasPriority);
}

public Scenario expectedPrioritizedForSender(final Sender sender, final long... nonce) {
lastExpectedPrioritized = expectedForSender(sender, nonce);
final var expectedCopy = List.copyOf(lastExpectedPrioritized);
Expand Down Expand Up @@ -1467,7 +1556,7 @@ public Scenario removeForSender(final Sender sender, final long... nonce) {
Arrays.stream(nonce)
.forEach(
n -> {
final var pendingTx = getOrCreate(sender, n);
final var pendingTx = getOrCreate(sender, EIP1559, n);
actions.add(
(pending, prio, ready, sparse, dropped) -> prio.remove(pendingTx, INVALIDATED));
});
Expand Down

0 comments on commit 31b21b0

Please sign in to comment.