Blob transaction replacement rule (#6874)

Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
pull/6877/head
Fabio Di Fabio 8 months ago committed by GitHub
parent b55ec65f0f
commit c93cfbeb87
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 12
      besu/src/main/java/org/hyperledger/besu/cli/options/TransactionPoolOptions.java
  3. 25
      besu/src/test/java/org/hyperledger/besu/cli/options/TransactionPoolOptionsTest.java
  4. 1
      besu/src/test/resources/everything_config.toml
  5. 3
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/AbstractIsolationTests.java
  6. 6
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolConfiguration.java
  7. 4
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactory.java
  8. 5
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolReplacementHandler.java
  9. 46
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionReplacementByFeeMarketRule.java
  10. 3
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/AbstractPendingTransactionsSorter.java
  11. 3
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/AbstractTransactionPoolTest.java
  12. 18
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/AbstractTransactionReplacementTest.java
  13. 90
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionReplacementByFeeMarketRuleTest.java
  14. 76
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionReplacementRulesTest.java
  15. 3
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/AbstractPrioritizedTransactionsTestBase.java
  16. 3
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayeredPendingTransactionsTest.java
  17. 3
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayersTest.java
  18. 3
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/ReplayTest.java

@ -34,6 +34,7 @@
- Experimental Snap Sync Server [#6640](https://github.com/hyperledger/besu/pull/6640)
- Upgrade Reference Tests to 13.2 [#6854](https://github.com/hyperledger/besu/pull/6854)
- Update Web3j dependencies [#6811](https://github.com/hyperledger/besu/pull/6811)
- Add `tx-pool-blob-price-bump` option to configure the price bump percentage required to replace blob transactions (by default 100%) [#6874](https://github.com/hyperledger/besu/pull/6874)
### Bug fixes
- Fix txpool dump/restore race condition [#6665](https://github.com/hyperledger/besu/pull/6665)

@ -52,6 +52,7 @@ public class TransactionPoolOptions implements CLIOptions<TransactionPoolConfigu
private static final String TX_POOL_ENABLE_SAVE_RESTORE = "--tx-pool-enable-save-restore";
private static final String TX_POOL_SAVE_FILE = "--tx-pool-save-file";
private static final String TX_POOL_PRICE_BUMP = "--tx-pool-price-bump";
private static final String TX_POOL_BLOB_PRICE_BUMP = "--tx-pool-blob-price-bump";
private static final String RPC_TX_FEECAP = "--rpc-tx-feecap";
private static final String STRICT_TX_REPLAY_PROTECTION_ENABLED_FLAG =
"--strict-tx-replay-protection-enabled";
@ -102,6 +103,15 @@ public class TransactionPoolOptions implements CLIOptions<TransactionPoolConfigu
arity = "1")
private Percentage priceBump = TransactionPoolConfiguration.DEFAULT_PRICE_BUMP;
@CommandLine.Option(
names = {TX_POOL_BLOB_PRICE_BUMP},
paramLabel = "<Percentage>",
converter = PercentageConverter.class,
description =
"Blob price bump percentage to replace an already existing transaction blob tx (default: ${DEFAULT-VALUE})",
arity = "1")
private Percentage blobPriceBump = TransactionPoolConfiguration.DEFAULT_BLOB_PRICE_BUMP;
@CommandLine.Option(
names = {RPC_TX_FEECAP},
description =
@ -277,6 +287,7 @@ public class TransactionPoolOptions implements CLIOptions<TransactionPoolConfigu
options.saveRestoreEnabled = config.getEnableSaveRestore();
options.noLocalPriority = config.getNoLocalPriority();
options.priceBump = config.getPriceBump();
options.blobPriceBump = config.getBlobPriceBump();
options.txFeeCap = config.getTxFeeCap();
options.saveFile = config.getSaveFile();
options.strictTxReplayProtectionEnabled = config.getStrictTransactionReplayProtectionEnabled();
@ -334,6 +345,7 @@ public class TransactionPoolOptions implements CLIOptions<TransactionPoolConfigu
.enableSaveRestore(saveRestoreEnabled)
.noLocalPriority(noLocalPriority)
.priceBump(priceBump)
.blobPriceBump(blobPriceBump)
.txFeeCap(txFeeCap)
.saveFile(saveFile)
.strictTransactionReplayProtectionEnabled(strictTxReplayProtectionEnabled)

@ -168,6 +168,31 @@ public class TransactionPoolOptionsTest
"101");
}
@Test
public void blobPriceBump() {
final Percentage blobPriceBump = Percentage.fromInt(50);
internalTestSuccess(
config -> assertThat(config.getBlobPriceBump()).isEqualTo(blobPriceBump),
"--tx-pool-blob-price-bump",
blobPriceBump.toString());
}
@Test
public void invalidBlobPriceBumpShouldFail() {
internalTestFailure(
"Invalid value: 101, should be a number between 0 and 100 inclusive",
"--tx-pool-blob-price-bump",
"101");
}
@Test
public void defaultBlobPriceBump() {
internalTestSuccess(
config ->
assertThat(config.getBlobPriceBump())
.isEqualTo(TransactionPoolConfiguration.DEFAULT_BLOB_PRICE_BUMP));
}
@Test
public void txFeeCap() {
final Wei txFeeCap = Wei.fromEth(2);

@ -174,6 +174,7 @@ privacy-flexible-groups-enabled=false
# Transaction Pool
tx-pool="layered"
tx-pool-price-bump=13
tx-pool-blob-price-bump=100
rpc-tx-feecap=2000000000000000000
strict-tx-replay-protection-enabled=true
tx-pool-no-local-priority=false

@ -116,7 +116,8 @@ public abstract class AbstractIsolationTests {
ImmutableTransactionPoolConfiguration.builder().txPoolMaxSize(100).build();
protected final TransactionPoolReplacementHandler transactionReplacementHandler =
new TransactionPoolReplacementHandler(poolConfiguration.getPriceBump());
new TransactionPoolReplacementHandler(
poolConfiguration.getPriceBump(), poolConfiguration.getBlobPriceBump());
protected final BiFunction<PendingTransaction, PendingTransaction, Boolean>
transactionReplacementTester =

@ -64,6 +64,7 @@ public interface TransactionPoolConfiguration {
int DEFAULT_TX_RETENTION_HOURS = 13;
boolean DEFAULT_STRICT_TX_REPLAY_PROTECTION_ENABLED = false;
Percentage DEFAULT_PRICE_BUMP = Percentage.fromInt(10);
Percentage DEFAULT_BLOB_PRICE_BUMP = Percentage.fromInt(100);
Wei DEFAULT_RPC_TX_FEE_CAP = Wei.fromEth(1);
boolean DEFAULT_NO_LOCAL_PRIORITY = false;
boolean DEFAULT_ENABLE_SAVE_RESTORE = false;
@ -102,6 +103,11 @@ public interface TransactionPoolConfiguration {
return DEFAULT_PRICE_BUMP;
}
@Value.Default
default Percentage getBlobPriceBump() {
return DEFAULT_BLOB_PRICE_BUMP;
}
@Value.Default
default Wei getTxFeeCap() {
return DEFAULT_RPC_TX_FEE_CAP;

@ -292,7 +292,9 @@ public class TransactionPoolFactory {
final MiningParameters miningParameters) {
final TransactionPoolReplacementHandler transactionReplacementHandler =
new TransactionPoolReplacementHandler(transactionPoolConfiguration.getPriceBump());
new TransactionPoolReplacementHandler(
transactionPoolConfiguration.getPriceBump(),
transactionPoolConfiguration.getBlobPriceBump());
final BiFunction<PendingTransaction, PendingTransaction, Boolean> transactionReplacementTester =
(t1, t2) ->

@ -26,11 +26,12 @@ import com.google.common.annotations.VisibleForTesting;
public class TransactionPoolReplacementHandler {
private final List<TransactionPoolReplacementRule> rules;
public TransactionPoolReplacementHandler(final Percentage priceBump) {
public TransactionPoolReplacementHandler(
final Percentage priceBump, final Percentage blobPriceBump) {
this(
asList(
new TransactionReplacementByGasPriceRule(priceBump),
new TransactionReplacementByFeeMarketRule(priceBump)));
new TransactionReplacementByFeeMarketRule(priceBump, blobPriceBump)));
}
@VisibleForTesting

@ -28,9 +28,12 @@ public class TransactionReplacementByFeeMarketRule implements TransactionPoolRep
private static final TransactionPriceCalculator EIP1559_CALCULATOR =
TransactionPriceCalculator.eip1559();
private final Percentage priceBump;
private final Percentage blobPriceBump;
public TransactionReplacementByFeeMarketRule(final Percentage priceBump) {
public TransactionReplacementByFeeMarketRule(
final Percentage priceBump, final Percentage blobPriceBump) {
this.priceBump = priceBump;
this.blobPriceBump = blobPriceBump;
}
@Override
@ -39,6 +42,16 @@ public class TransactionReplacementByFeeMarketRule implements TransactionPoolRep
final PendingTransaction newPendingTransaction,
final Optional<Wei> maybeBaseFee) {
return validExecutionPriceReplacement(
existingPendingTransaction, newPendingTransaction, maybeBaseFee)
&& validBlobPriceReplacement(existingPendingTransaction, newPendingTransaction);
}
private boolean validExecutionPriceReplacement(
final PendingTransaction existingPendingTransaction,
final PendingTransaction newPendingTransaction,
final Optional<Wei> maybeBaseFee) {
// bail early if basefee is absent or neither transaction supports 1559 fee market
if (maybeBaseFee.isEmpty()
|| !(isNotGasPriced(existingPendingTransaction) || isNotGasPriced(newPendingTransaction))) {
@ -65,6 +78,37 @@ public class TransactionReplacementByFeeMarketRule implements TransactionPoolRep
return false;
}
private boolean validBlobPriceReplacement(
final PendingTransaction existingPendingTransaction,
final PendingTransaction newPendingTransaction) {
final var existingType = existingPendingTransaction.getTransaction().getType();
final var newType = newPendingTransaction.getTransaction().getType();
if (existingType.supportsBlob() || newType.supportsBlob()) {
if (existingType.supportsBlob() && newType.supportsBlob()) {
final Wei replacementThreshold =
existingPendingTransaction
.getTransaction()
.getMaxFeePerBlobGas()
.orElseThrow()
.multiply(100 + blobPriceBump.getValue())
.divide(100);
return newPendingTransaction
.getTransaction()
.getMaxFeePerBlobGas()
.orElseThrow()
.compareTo(replacementThreshold)
>= 0;
}
// blob tx can only replace and be replaced by blob tx
return false;
}
// in case no blob tx, then we are fine
return true;
}
private Wei priceOf(final Transaction transaction, final Optional<Wei> maybeBaseFee) {
final TransactionPriceCalculator transactionPriceCalculator =
transaction.getType().supports1559FeeMarket() ? EIP1559_CALCULATOR : FRONTIER_CALCULATOR;

@ -110,7 +110,8 @@ public abstract class AbstractPendingTransactionsSorter implements PendingTransa
this.clock = clock;
this.chainHeadHeaderSupplier = chainHeadHeaderSupplier;
this.transactionReplacementHandler =
new TransactionPoolReplacementHandler(poolConfig.getPriceBump());
new TransactionPoolReplacementHandler(
poolConfig.getPriceBump(), poolConfig.getBlobPriceBump());
final LabelledMetric<Counter> transactionAddedCounter =
metricsSystem.createLabelledCounter(
BesuMetricCategory.TRANSACTION_POOL,

@ -267,7 +267,8 @@ public abstract class AbstractTransactionPoolTest {
final TransactionPoolConfiguration poolConfig = configBuilder.build();
final TransactionPoolReplacementHandler transactionReplacementHandler =
new TransactionPoolReplacementHandler(poolConfig.getPriceBump());
new TransactionPoolReplacementHandler(
poolConfig.getPriceBump(), poolConfig.getBlobPriceBump());
final BiFunction<PendingTransaction, PendingTransaction, Boolean> transactionReplacementTester =
(t1, t2) ->

@ -18,10 +18,12 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.VersionedHash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.Transaction;
import java.math.BigInteger;
import java.util.List;
public class AbstractTransactionReplacementTest {
protected static PendingTransaction frontierTx(final long price) {
@ -50,4 +52,20 @@ public class AbstractTransactionReplacementTest {
when(pendingTransaction.getTransaction()).thenReturn(transaction);
return pendingTransaction;
}
protected static PendingTransaction blobTx(
final long maxPriorityFeePerGas, final long maxFeePerGas, final long maxFeePerBlobGas) {
final PendingTransaction pendingTransaction = mock(PendingTransaction.class);
final Transaction transaction =
Transaction.builder()
.chainId(BigInteger.ZERO)
.type(TransactionType.BLOB)
.maxPriorityFeePerGas(Wei.of(maxPriorityFeePerGas))
.maxFeePerGas(Wei.of(maxFeePerGas))
.maxFeePerBlobGas(Wei.of(maxFeePerBlobGas))
.versionedHashes(List.of(VersionedHash.DEFAULT_VERSIONED_HASH))
.build();
when(pendingTransaction.getTransaction()).thenReturn(transaction);
return pendingTransaction;
}
}

@ -34,52 +34,60 @@ public class TransactionReplacementByFeeMarketRuleTest extends AbstractTransacti
new Object[][] {
// basefee absent
{frontierTx(5L), frontierTx(6L), empty(), 0, false},
{frontierTx(5L), frontierTx(5L), empty(), 0, false},
{frontierTx(5L), frontierTx(4L), empty(), 0, false},
{frontierTx(100L), frontierTx(105L), empty(), 10, false},
{frontierTx(100L), frontierTx(110L), empty(), 10, false},
{frontierTx(100L), frontierTx(111L), empty(), 10, false},
{frontierTx(5L), frontierTx(6L), empty(), 0, 100, false},
{frontierTx(5L), frontierTx(5L), empty(), 0, 100, false},
{frontierTx(5L), frontierTx(4L), empty(), 0, 100, false},
{frontierTx(100L), frontierTx(105L), empty(), 10, 100, false},
{frontierTx(100L), frontierTx(110L), empty(), 10, 100, false},
{frontierTx(100L), frontierTx(111L), empty(), 10, 100, false},
// basefee present
{frontierTx(5L), frontierTx(6L), Optional.of(Wei.of(3L)), 0, false},
{frontierTx(5L), frontierTx(5L), Optional.of(Wei.of(3L)), 0, false},
{frontierTx(5L), frontierTx(4L), Optional.of(Wei.of(3L)), 0, false},
{frontierTx(100L), frontierTx(105L), Optional.of(Wei.of(3L)), 10, false},
{frontierTx(100L), frontierTx(110L), Optional.of(Wei.of(3L)), 10, false},
{frontierTx(100L), frontierTx(111L), Optional.of(Wei.of(3L)), 10, false},
{frontierTx(5L), frontierTx(6L), Optional.of(Wei.of(3L)), 0, 100, false},
{frontierTx(5L), frontierTx(5L), Optional.of(Wei.of(3L)), 0, 100, false},
{frontierTx(5L), frontierTx(4L), Optional.of(Wei.of(3L)), 0, 100, false},
{frontierTx(100L), frontierTx(105L), Optional.of(Wei.of(3L)), 10, 100, false},
{frontierTx(100L), frontierTx(110L), Optional.of(Wei.of(3L)), 10, 100, false},
{frontierTx(100L), frontierTx(111L), Optional.of(Wei.of(3L)), 10, 100, false},
// eip1559 replacing frontier
{frontierTx(5L), eip1559Tx(3L, 6L), Optional.of(Wei.of(1L)), 0, false},
{frontierTx(5L), eip1559Tx(3L, 5L), Optional.of(Wei.of(3L)), 0, true},
{frontierTx(5L), eip1559Tx(3L, 6L), Optional.of(Wei.of(3L)), 0, true},
{frontierTx(5L), eip1559Tx(3L, 6L), Optional.of(Wei.of(1L)), 0, 100, false},
{frontierTx(5L), eip1559Tx(3L, 5L), Optional.of(Wei.of(3L)), 0, 100, true},
{frontierTx(5L), eip1559Tx(3L, 6L), Optional.of(Wei.of(3L)), 0, 100, true},
// frontier replacing 1559
{eip1559Tx(3L, 8L), frontierTx(7L), Optional.of(Wei.of(4L)), 0, true},
{eip1559Tx(3L, 8L), frontierTx(7L), Optional.of(Wei.of(5L)), 0, false},
{eip1559Tx(3L, 8L), frontierTx(8L), Optional.of(Wei.of(4L)), 0, true},
{eip1559Tx(3L, 8L), frontierTx(7L), Optional.of(Wei.of(4L)), 0, 100, true},
{eip1559Tx(3L, 8L), frontierTx(7L), Optional.of(Wei.of(5L)), 0, 100, false},
{eip1559Tx(3L, 8L), frontierTx(8L), Optional.of(Wei.of(4L)), 0, 100, true},
// eip1559 replacing eip1559
{eip1559Tx(3L, 6L), eip1559Tx(3L, 6L), Optional.of(Wei.of(3L)), 0, true},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(3L)), 0, true},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(4L)), 0, true},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(5L)), 0, true},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(6L)), 0, true},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(7L)), 0, true},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(8L)), 0, true},
{eip1559Tx(10L, 200L), eip1559Tx(10L, 200L), Optional.of(Wei.of(90L)), 10, false},
{eip1559Tx(10L, 200L), eip1559Tx(15L, 200L), Optional.of(Wei.of(90L)), 10, false},
{eip1559Tx(10L, 200L), eip1559Tx(20L, 200L), Optional.of(Wei.of(90L)), 10, true},
{eip1559Tx(10L, 200L), eip1559Tx(20L, 200L), Optional.of(Wei.of(200L)), 10, true},
{eip1559Tx(10L, 200L), eip1559Tx(10L, 220L), Optional.of(Wei.of(220L)), 10, true},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 6L), Optional.of(Wei.of(3L)), 0, 100, true},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(3L)), 0, 100, true},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(4L)), 0, 100, true},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(5L)), 0, 100, true},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(6L)), 0, 100, true},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(7L)), 0, 100, true},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(8L)), 0, 100, true},
{eip1559Tx(10L, 200L), eip1559Tx(10L, 200L), Optional.of(Wei.of(90L)), 10, 100, false},
{eip1559Tx(10L, 200L), eip1559Tx(15L, 200L), Optional.of(Wei.of(90L)), 10, 100, false},
{eip1559Tx(10L, 200L), eip1559Tx(20L, 200L), Optional.of(Wei.of(90L)), 10, 100, true},
{eip1559Tx(10L, 200L), eip1559Tx(20L, 200L), Optional.of(Wei.of(200L)), 10, 100, true},
{eip1559Tx(10L, 200L), eip1559Tx(10L, 220L), Optional.of(Wei.of(220L)), 10, 100, true},
// pathological, priority fee > max fee
{eip1559Tx(8L, 6L), eip1559Tx(3L, 6L), Optional.of(Wei.of(2L)), 0, false},
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(3L)), 0, true},
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(4L)), 0, true},
{eip1559Tx(8L, 6L), eip1559Tx(3L, 6L), Optional.of(Wei.of(2L)), 0, 100, false},
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(3L)), 0, 100, true},
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(4L)), 0, 100, true},
// pathological, eip1559 without basefee
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.empty(), 0, false},
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.empty(), 0, false},
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.empty(), 0, 100, false},
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.empty(), 0, 100, false},
// zero base fee market
{frontierTx(0L), frontierTx(0L), Optional.of(Wei.ZERO), 0, false},
{eip1559Tx(0L, 0L), frontierTx(0L), Optional.of(Wei.ZERO), 0, true},
{frontierTx(0L), eip1559Tx(0L, 0L), Optional.of(Wei.ZERO), 0, true},
{eip1559Tx(0L, 0L), eip1559Tx(0L, 0L), Optional.of(Wei.ZERO), 0, true},
{frontierTx(0L), frontierTx(0L), Optional.of(Wei.ZERO), 0, 100, false},
{eip1559Tx(0L, 0L), frontierTx(0L), Optional.of(Wei.ZERO), 0, 100, true},
{frontierTx(0L), eip1559Tx(0L, 0L), Optional.of(Wei.ZERO), 0, 100, true},
{eip1559Tx(0L, 0L), eip1559Tx(0L, 0L), Optional.of(Wei.ZERO), 0, 100, true},
// blob tx
{blobTx(10L, 200L, 1L), blobTx(20L, 220L, 1L), Optional.of(Wei.of(90L)), 10, 0, true},
{blobTx(10L, 200L, 1L), blobTx(20L, 220L, 2L), Optional.of(Wei.of(90L)), 10, 100, true},
{blobTx(10L, 200L, 1L), blobTx(20L, 220L, 1L), Optional.of(Wei.of(90L)), 10, 100, false},
{blobTx(10L, 200L, 2L), blobTx(20L, 220L, 3L), Optional.of(Wei.of(90L)), 10, 100, false},
// // blob tx must be replaced by blob tx only
{blobTx(3L, 6L, 1L), eip1559Tx(3L, 6L), Optional.of(Wei.of(3L)), 0, 0, false},
{eip1559Tx(3L, 6L), blobTx(3L, 6L, 1L), Optional.of(Wei.of(3L)), 0, 0, false},
});
}
@ -90,10 +98,12 @@ public class TransactionReplacementByFeeMarketRuleTest extends AbstractTransacti
final PendingTransaction newTx,
final Optional<Wei> baseFee,
final int priceBump,
final int blobPriceBump,
final boolean expected) {
assertThat(
new TransactionReplacementByFeeMarketRule(Percentage.fromInt(priceBump))
new TransactionReplacementByFeeMarketRule(
Percentage.fromInt(priceBump), Percentage.fromInt(blobPriceBump))
.shouldReplace(oldTx, newTx, baseFee))
.isEqualTo(expected);
}

@ -37,46 +37,54 @@ public class TransactionReplacementRulesTest extends AbstractTransactionReplacem
new Object[][] {
// TransactionReplacementByGasPriceRule
// basefee absent
{frontierTx(5L), frontierTx(6L), empty(), 0, true},
{frontierTx(5L), frontierTx(5L), empty(), 0, true},
{frontierTx(5L), frontierTx(4L), empty(), 0, false},
{frontierTx(100L), frontierTx(105L), empty(), 10, false},
{frontierTx(100L), frontierTx(110L), empty(), 10, true},
{frontierTx(100L), frontierTx(111L), empty(), 10, true},
{frontierTx(5L), frontierTx(6L), empty(), 0, 100, true},
{frontierTx(5L), frontierTx(5L), empty(), 0, 100, true},
{frontierTx(5L), frontierTx(4L), empty(), 0, 100, false},
{frontierTx(100L), frontierTx(105L), empty(), 10, 100, false},
{frontierTx(100L), frontierTx(110L), empty(), 10, 100, true},
{frontierTx(100L), frontierTx(111L), empty(), 10, 100, true},
// basefee present
{frontierTx(5L), frontierTx(6L), Optional.of(Wei.of(3L)), 0, true},
{frontierTx(5L), frontierTx(5L), Optional.of(Wei.of(3L)), 0, true},
{frontierTx(5L), frontierTx(4L), Optional.of(Wei.of(3L)), 0, false},
{frontierTx(100L), frontierTx(105L), Optional.of(Wei.of(3L)), 10, false},
{frontierTx(100L), frontierTx(110L), Optional.of(Wei.of(3L)), 10, true},
{frontierTx(100L), frontierTx(111L), Optional.of(Wei.of(3L)), 10, true},
{frontierTx(5L), frontierTx(6L), Optional.of(Wei.of(3L)), 0, 100, true},
{frontierTx(5L), frontierTx(5L), Optional.of(Wei.of(3L)), 0, 100, true},
{frontierTx(5L), frontierTx(4L), Optional.of(Wei.of(3L)), 0, 100, false},
{frontierTx(100L), frontierTx(105L), Optional.of(Wei.of(3L)), 10, 100, false},
{frontierTx(100L), frontierTx(110L), Optional.of(Wei.of(3L)), 10, 100, true},
{frontierTx(100L), frontierTx(111L), Optional.of(Wei.of(3L)), 10, 100, true},
// TransactionReplacementByFeeMarketRule
// eip1559 replacing frontier
{frontierTx(5L), eip1559Tx(3L, 6L), Optional.of(Wei.of(1L)), 0, false},
{frontierTx(5L), eip1559Tx(3L, 5L), Optional.of(Wei.of(3L)), 0, true},
{frontierTx(5L), eip1559Tx(3L, 6L), Optional.of(Wei.of(3L)), 0, true},
{frontierTx(5L), eip1559Tx(3L, 6L), Optional.of(Wei.of(1L)), 0, 100, false},
{frontierTx(5L), eip1559Tx(3L, 5L), Optional.of(Wei.of(3L)), 0, 100, true},
{frontierTx(5L), eip1559Tx(3L, 6L), Optional.of(Wei.of(3L)), 0, 100, true},
// frontier replacing 1559
{eip1559Tx(3L, 8L), frontierTx(6L), Optional.of(Wei.of(4L)), 0, false},
{eip1559Tx(3L, 8L), frontierTx(7L), Optional.of(Wei.of(4L)), 0, true},
{eip1559Tx(3L, 8L), frontierTx(8L), Optional.of(Wei.of(4L)), 0, true},
{eip1559Tx(3L, 8L), frontierTx(6L), Optional.of(Wei.of(4L)), 0, 100, false},
{eip1559Tx(3L, 8L), frontierTx(7L), Optional.of(Wei.of(4L)), 0, 100, true},
{eip1559Tx(3L, 8L), frontierTx(8L), Optional.of(Wei.of(4L)), 0, 100, true},
// eip1559 replacing eip1559
{eip1559Tx(3L, 6L), eip1559Tx(3L, 6L), Optional.of(Wei.of(3L)), 0, true},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(3L)), 0, true},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(4L)), 0, true},
{eip1559Tx(10L, 200L), eip1559Tx(10L, 200L), Optional.of(Wei.of(90L)), 10, false},
{eip1559Tx(10L, 200L), eip1559Tx(15L, 200L), Optional.of(Wei.of(90L)), 10, false},
{eip1559Tx(10L, 200L), eip1559Tx(21L, 200L), Optional.of(Wei.of(90L)), 10, true},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 6L), Optional.of(Wei.of(3L)), 0, 100, true},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(3L)), 0, 100, true},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(4L)), 0, 100, true},
{eip1559Tx(10L, 200L), eip1559Tx(10L, 200L), Optional.of(Wei.of(90L)), 10, 100, false},
{eip1559Tx(10L, 200L), eip1559Tx(15L, 200L), Optional.of(Wei.of(90L)), 10, 100, false},
{eip1559Tx(10L, 200L), eip1559Tx(21L, 200L), Optional.of(Wei.of(90L)), 10, 100, true},
// pathological, priority fee > max fee
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(3L)), 0, true},
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(4L)), 0, true},
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(3L)), 0, 100, true},
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(4L)), 0, 100, true},
// pathological, eip1559 without basefee
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.empty(), 0, false},
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.empty(), 0, false},
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.empty(), 0, 100, false},
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.empty(), 0, 100, false},
// zero base fee market
{frontierTx(0L), frontierTx(0L), Optional.of(Wei.ZERO), 0, true},
{eip1559Tx(0L, 0L), frontierTx(0L), Optional.of(Wei.ZERO), 0, true},
{frontierTx(0L), eip1559Tx(0L, 0L), Optional.of(Wei.ZERO), 0, true},
{eip1559Tx(0L, 0L), eip1559Tx(0L, 0L), Optional.of(Wei.ZERO), 0, true},
{frontierTx(0L), frontierTx(0L), Optional.of(Wei.ZERO), 0, 100, true},
{eip1559Tx(0L, 0L), frontierTx(0L), Optional.of(Wei.ZERO), 0, 100, true},
{frontierTx(0L), eip1559Tx(0L, 0L), Optional.of(Wei.ZERO), 0, 100, true},
{eip1559Tx(0L, 0L), eip1559Tx(0L, 0L), Optional.of(Wei.ZERO), 0, 100, true},
// blob tx
{blobTx(3L, 6L, 1L), blobTx(3L, 6L, 1L), Optional.of(Wei.of(3L)), 0, 0, true},
{blobTx(3L, 6L, 1L), blobTx(3L, 6L, 2L), Optional.of(Wei.of(3L)), 0, 100, true},
{blobTx(10L, 200L, 1L), blobTx(10L, 200L, 1L), Optional.of(Wei.of(90L)), 10, 100, false},
{blobTx(10L, 200L, 2L), blobTx(15L, 200L, 3L), Optional.of(Wei.of(90L)), 10, 100, false},
// blob tx must be replaced by blob tx only
{blobTx(3L, 6L, 1L), eip1559Tx(3L, 6L), Optional.of(Wei.of(3L)), 0, 0, false},
{eip1559Tx(3L, 6L), blobTx(3L, 6L, 1L), Optional.of(Wei.of(3L)), 0, 0, false},
});
}
@ -87,12 +95,14 @@ public class TransactionReplacementRulesTest extends AbstractTransactionReplacem
final PendingTransaction newTx,
final Optional<Wei> baseFee,
final int priceBump,
final int blobPriceBump,
final boolean expected) {
BlockHeader mockHeader = mock(BlockHeader.class);
when(mockHeader.getBaseFee()).thenReturn(baseFee);
assertThat(
new TransactionPoolReplacementHandler(Percentage.fromInt(priceBump))
new TransactionPoolReplacementHandler(
Percentage.fromInt(priceBump), Percentage.fromInt(blobPriceBump))
.shouldReplace(oldTx, newTx, mockHeader))
.isEqualTo(expected);
}

@ -78,7 +78,8 @@ public abstract class AbstractPrioritizedTransactionsTestBase extends BaseTransa
final PendingTransaction pt1,
final PendingTransaction pt2) {
final TransactionPoolReplacementHandler transactionReplacementHandler =
new TransactionPoolReplacementHandler(poolConfig.getPriceBump());
new TransactionPoolReplacementHandler(
poolConfig.getPriceBump(), poolConfig.getBlobPriceBump());
return transactionReplacementHandler.shouldReplace(pt1, pt2, mockBlockHeader());
}

@ -108,7 +108,8 @@ public class LayeredPendingTransactionsTest extends BaseTransactionPoolTest {
final BiFunction<PendingTransaction, PendingTransaction, Boolean> transactionReplacementTester =
(t1, t2) ->
new TransactionPoolReplacementHandler(poolConf.getPriceBump())
new TransactionPoolReplacementHandler(
poolConf.getPriceBump(), poolConfig.getBlobPriceBump())
.shouldReplace(t1, t2, mockBlockHeader());
final EvictCollectorLayer evictCollector = new EvictCollectorLayer(txPoolMetrics);

@ -1194,7 +1194,8 @@ public class LayersTest extends BaseTransactionPoolTest {
final PendingTransaction pt1,
final PendingTransaction pt2) {
final TransactionPoolReplacementHandler transactionReplacementHandler =
new TransactionPoolReplacementHandler(poolConfig.getPriceBump());
new TransactionPoolReplacementHandler(
poolConfig.getPriceBump(), poolConfig.getBlobPriceBump());
return transactionReplacementHandler.shouldReplace(pt1, pt2, mockBlockHeader());
}

@ -305,7 +305,8 @@ public class ReplayTest {
final PendingTransaction pt1,
final PendingTransaction pt2) {
final TransactionPoolReplacementHandler transactionReplacementHandler =
new TransactionPoolReplacementHandler(poolConfig.getPriceBump());
new TransactionPoolReplacementHandler(
poolConfig.getPriceBump(), poolConfig.getBlobPriceBump());
return transactionReplacementHandler.shouldReplace(pt1, pt2, currBlockHeader);
}
}

Loading…
Cancel
Save