Skip to content

Commit 393d666

Browse files
fab-10jflo
authored andcommitted
Blob transaction replacement rule (besu-eth#6874)
Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
1 parent d3b690e commit 393d666

File tree

18 files changed

+221
-84
lines changed

18 files changed

+221
-84
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
- Experimental Snap Sync Server [#6640](https://github.com/hyperledger/besu/pull/6640)
3535
- Upgrade Reference Tests to 13.2 [#6854](https://github.com/hyperledger/besu/pull/6854)
3636
- Update Web3j dependencies [#6811](https://github.com/hyperledger/besu/pull/6811)
37+
- 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)
3738

3839
### Bug fixes
3940
- Fix txpool dump/restore race condition [#6665](https://github.com/hyperledger/besu/pull/6665)

besu/src/main/java/org/hyperledger/besu/cli/options/TransactionPoolOptions.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public class TransactionPoolOptions implements CLIOptions<TransactionPoolConfigu
5252
private static final String TX_POOL_ENABLE_SAVE_RESTORE = "--tx-pool-enable-save-restore";
5353
private static final String TX_POOL_SAVE_FILE = "--tx-pool-save-file";
5454
private static final String TX_POOL_PRICE_BUMP = "--tx-pool-price-bump";
55+
private static final String TX_POOL_BLOB_PRICE_BUMP = "--tx-pool-blob-price-bump";
5556
private static final String RPC_TX_FEECAP = "--rpc-tx-feecap";
5657
private static final String STRICT_TX_REPLAY_PROTECTION_ENABLED_FLAG =
5758
"--strict-tx-replay-protection-enabled";
@@ -102,6 +103,15 @@ public class TransactionPoolOptions implements CLIOptions<TransactionPoolConfigu
102103
arity = "1")
103104
private Percentage priceBump = TransactionPoolConfiguration.DEFAULT_PRICE_BUMP;
104105

106+
@CommandLine.Option(
107+
names = {TX_POOL_BLOB_PRICE_BUMP},
108+
paramLabel = "<Percentage>",
109+
converter = PercentageConverter.class,
110+
description =
111+
"Blob price bump percentage to replace an already existing transaction blob tx (default: ${DEFAULT-VALUE})",
112+
arity = "1")
113+
private Percentage blobPriceBump = TransactionPoolConfiguration.DEFAULT_BLOB_PRICE_BUMP;
114+
105115
@CommandLine.Option(
106116
names = {RPC_TX_FEECAP},
107117
description =
@@ -277,6 +287,7 @@ public static TransactionPoolOptions fromConfig(final TransactionPoolConfigurati
277287
options.saveRestoreEnabled = config.getEnableSaveRestore();
278288
options.noLocalPriority = config.getNoLocalPriority();
279289
options.priceBump = config.getPriceBump();
290+
options.blobPriceBump = config.getBlobPriceBump();
280291
options.txFeeCap = config.getTxFeeCap();
281292
options.saveFile = config.getSaveFile();
282293
options.strictTxReplayProtectionEnabled = config.getStrictTransactionReplayProtectionEnabled();
@@ -334,6 +345,7 @@ public TransactionPoolConfiguration toDomainObject() {
334345
.enableSaveRestore(saveRestoreEnabled)
335346
.noLocalPriority(noLocalPriority)
336347
.priceBump(priceBump)
348+
.blobPriceBump(blobPriceBump)
337349
.txFeeCap(txFeeCap)
338350
.saveFile(saveFile)
339351
.strictTransactionReplayProtectionEnabled(strictTxReplayProtectionEnabled)

besu/src/test/java/org/hyperledger/besu/cli/options/TransactionPoolOptionsTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,31 @@ public void invalidPriceBumpShouldFail() {
168168
"101");
169169
}
170170

171+
@Test
172+
public void blobPriceBump() {
173+
final Percentage blobPriceBump = Percentage.fromInt(50);
174+
internalTestSuccess(
175+
config -> assertThat(config.getBlobPriceBump()).isEqualTo(blobPriceBump),
176+
"--tx-pool-blob-price-bump",
177+
blobPriceBump.toString());
178+
}
179+
180+
@Test
181+
public void invalidBlobPriceBumpShouldFail() {
182+
internalTestFailure(
183+
"Invalid value: 101, should be a number between 0 and 100 inclusive",
184+
"--tx-pool-blob-price-bump",
185+
"101");
186+
}
187+
188+
@Test
189+
public void defaultBlobPriceBump() {
190+
internalTestSuccess(
191+
config ->
192+
assertThat(config.getBlobPriceBump())
193+
.isEqualTo(TransactionPoolConfiguration.DEFAULT_BLOB_PRICE_BUMP));
194+
}
195+
171196
@Test
172197
public void txFeeCap() {
173198
final Wei txFeeCap = Wei.fromEth(2);

besu/src/test/resources/everything_config.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ privacy-flexible-groups-enabled=false
174174
# Transaction Pool
175175
tx-pool="layered"
176176
tx-pool-price-bump=13
177+
tx-pool-blob-price-bump=100
177178
rpc-tx-feecap=2000000000000000000
178179
strict-tx-replay-protection-enabled=true
179180
tx-pool-no-local-priority=false

ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/AbstractIsolationTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ public abstract class AbstractIsolationTests {
116116
ImmutableTransactionPoolConfiguration.builder().txPoolMaxSize(100).build();
117117

118118
protected final TransactionPoolReplacementHandler transactionReplacementHandler =
119-
new TransactionPoolReplacementHandler(poolConfiguration.getPriceBump());
119+
new TransactionPoolReplacementHandler(
120+
poolConfiguration.getPriceBump(), poolConfiguration.getBlobPriceBump());
120121

121122
protected final BiFunction<PendingTransaction, PendingTransaction, Boolean>
122123
transactionReplacementTester =

ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolConfiguration.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ enum Implementation {
6464
int DEFAULT_TX_RETENTION_HOURS = 13;
6565
boolean DEFAULT_STRICT_TX_REPLAY_PROTECTION_ENABLED = false;
6666
Percentage DEFAULT_PRICE_BUMP = Percentage.fromInt(10);
67+
Percentage DEFAULT_BLOB_PRICE_BUMP = Percentage.fromInt(100);
6768
Wei DEFAULT_RPC_TX_FEE_CAP = Wei.fromEth(1);
6869
boolean DEFAULT_NO_LOCAL_PRIORITY = false;
6970
boolean DEFAULT_ENABLE_SAVE_RESTORE = false;
@@ -102,6 +103,11 @@ default Percentage getPriceBump() {
102103
return DEFAULT_PRICE_BUMP;
103104
}
104105

106+
@Value.Default
107+
default Percentage getBlobPriceBump() {
108+
return DEFAULT_BLOB_PRICE_BUMP;
109+
}
110+
105111
@Value.Default
106112
default Wei getTxFeeCap() {
107113
return DEFAULT_RPC_TX_FEE_CAP;

ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactory.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,9 @@ private static PendingTransactions createLayeredPendingTransactions(
292292
final MiningParameters miningParameters) {
293293

294294
final TransactionPoolReplacementHandler transactionReplacementHandler =
295-
new TransactionPoolReplacementHandler(transactionPoolConfiguration.getPriceBump());
295+
new TransactionPoolReplacementHandler(
296+
transactionPoolConfiguration.getPriceBump(),
297+
transactionPoolConfiguration.getBlobPriceBump());
296298

297299
final BiFunction<PendingTransaction, PendingTransaction, Boolean> transactionReplacementTester =
298300
(t1, t2) ->

ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolReplacementHandler.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@
2626
public class TransactionPoolReplacementHandler {
2727
private final List<TransactionPoolReplacementRule> rules;
2828

29-
public TransactionPoolReplacementHandler(final Percentage priceBump) {
29+
public TransactionPoolReplacementHandler(
30+
final Percentage priceBump, final Percentage blobPriceBump) {
3031
this(
3132
asList(
3233
new TransactionReplacementByGasPriceRule(priceBump),
33-
new TransactionReplacementByFeeMarketRule(priceBump)));
34+
new TransactionReplacementByFeeMarketRule(priceBump, blobPriceBump)));
3435
}
3536

3637
@VisibleForTesting

ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionReplacementByFeeMarketRule.java

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,12 @@ public class TransactionReplacementByFeeMarketRule implements TransactionPoolRep
2828
private static final TransactionPriceCalculator EIP1559_CALCULATOR =
2929
TransactionPriceCalculator.eip1559();
3030
private final Percentage priceBump;
31+
private final Percentage blobPriceBump;
3132

32-
public TransactionReplacementByFeeMarketRule(final Percentage priceBump) {
33+
public TransactionReplacementByFeeMarketRule(
34+
final Percentage priceBump, final Percentage blobPriceBump) {
3335
this.priceBump = priceBump;
36+
this.blobPriceBump = blobPriceBump;
3437
}
3538

3639
@Override
@@ -39,6 +42,16 @@ public boolean shouldReplace(
3942
final PendingTransaction newPendingTransaction,
4043
final Optional<Wei> maybeBaseFee) {
4144

45+
return validExecutionPriceReplacement(
46+
existingPendingTransaction, newPendingTransaction, maybeBaseFee)
47+
&& validBlobPriceReplacement(existingPendingTransaction, newPendingTransaction);
48+
}
49+
50+
private boolean validExecutionPriceReplacement(
51+
final PendingTransaction existingPendingTransaction,
52+
final PendingTransaction newPendingTransaction,
53+
final Optional<Wei> maybeBaseFee) {
54+
4255
// bail early if basefee is absent or neither transaction supports 1559 fee market
4356
if (maybeBaseFee.isEmpty()
4457
|| !(isNotGasPriced(existingPendingTransaction) || isNotGasPriced(newPendingTransaction))) {
@@ -65,6 +78,37 @@ public boolean shouldReplace(
6578
return false;
6679
}
6780

81+
private boolean validBlobPriceReplacement(
82+
final PendingTransaction existingPendingTransaction,
83+
final PendingTransaction newPendingTransaction) {
84+
85+
final var existingType = existingPendingTransaction.getTransaction().getType();
86+
final var newType = newPendingTransaction.getTransaction().getType();
87+
88+
if (existingType.supportsBlob() || newType.supportsBlob()) {
89+
if (existingType.supportsBlob() && newType.supportsBlob()) {
90+
final Wei replacementThreshold =
91+
existingPendingTransaction
92+
.getTransaction()
93+
.getMaxFeePerBlobGas()
94+
.orElseThrow()
95+
.multiply(100 + blobPriceBump.getValue())
96+
.divide(100);
97+
return newPendingTransaction
98+
.getTransaction()
99+
.getMaxFeePerBlobGas()
100+
.orElseThrow()
101+
.compareTo(replacementThreshold)
102+
>= 0;
103+
}
104+
// blob tx can only replace and be replaced by blob tx
105+
return false;
106+
}
107+
108+
// in case no blob tx, then we are fine
109+
return true;
110+
}
111+
68112
private Wei priceOf(final Transaction transaction, final Optional<Wei> maybeBaseFee) {
69113
final TransactionPriceCalculator transactionPriceCalculator =
70114
transaction.getType().supports1559FeeMarket() ? EIP1559_CALCULATOR : FRONTIER_CALCULATOR;

ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/AbstractPendingTransactionsSorter.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ public AbstractPendingTransactionsSorter(
110110
this.clock = clock;
111111
this.chainHeadHeaderSupplier = chainHeadHeaderSupplier;
112112
this.transactionReplacementHandler =
113-
new TransactionPoolReplacementHandler(poolConfig.getPriceBump());
113+
new TransactionPoolReplacementHandler(
114+
poolConfig.getPriceBump(), poolConfig.getBlobPriceBump());
114115
final LabelledMetric<Counter> transactionAddedCounter =
115116
metricsSystem.createLabelledCounter(
116117
BesuMetricCategory.TRANSACTION_POOL,

0 commit comments

Comments
 (0)