Skip to content

Commit f0d2a66

Browse files
fab-10pinges
andauthored
Remove tx from pool when its score is lower than a configured value (#7576)
* Remove tx from pool when its score is lower than a configured value Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update besu/src/main/java/org/hyperledger/besu/cli/options/TransactionPoolOptions.java Co-authored-by: Stefan Pingel <16143240+pinges@users.noreply.github.com> Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Check for below min score after the penalization Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> --------- Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> Co-authored-by: Stefan Pingel <16143240+pinges@users.noreply.github.com>
1 parent dad05d4 commit f0d2a66

File tree

9 files changed

+77
-23
lines changed

9 files changed

+77
-23
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
### Additions and Improvements
1010
- Update Java and Gradle dependecies [#7571](https://github.com/hyperledger/besu/pull/7571)
11+
- Layered txpool: new options `--tx-pool-min-score` to remove a tx from pool when its score is lower than the specified value [#7576](https://github.com/hyperledger/besu/pull/7576)
1112

1213
### Bug fixes
1314
- Layered txpool: do not send notifications when moving tx between layers [#7539](https://github.com/hyperledger/besu/pull/7539)

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
@@ -160,6 +160,7 @@ static class Layered {
160160
private static final String TX_POOL_MAX_PRIORITIZED_BY_TYPE =
161161
"--tx-pool-max-prioritized-by-type";
162162
private static final String TX_POOL_MAX_FUTURE_BY_SENDER = "--tx-pool-max-future-by-sender";
163+
private static final String TX_POOL_MIN_SCORE = "--tx-pool-min-score";
163164

164165
@CommandLine.Option(
165166
names = {TX_POOL_LAYER_MAX_CAPACITY},
@@ -196,6 +197,15 @@ static class Layered {
196197
"Max number of future pending transactions allowed for a single sender (default: ${DEFAULT-VALUE})",
197198
arity = "1")
198199
Integer txPoolMaxFutureBySender = TransactionPoolConfiguration.DEFAULT_MAX_FUTURE_BY_SENDER;
200+
201+
@CommandLine.Option(
202+
names = {TX_POOL_MIN_SCORE},
203+
paramLabel = "<Byte>",
204+
description =
205+
"Remove a pending transaction from the txpool if its score is lower than this value."
206+
+ "Accepts values between -128 and 127 (default: ${DEFAULT-VALUE})",
207+
arity = "1")
208+
Byte minScore = TransactionPoolConfiguration.DEFAULT_TX_POOL_MIN_SCORE;
199209
}
200210

201211
@CommandLine.ArgGroup(
@@ -314,6 +324,7 @@ public static TransactionPoolOptions fromConfig(final TransactionPoolConfigurati
314324
options.layeredOptions.txPoolMaxPrioritizedByType =
315325
config.getMaxPrioritizedTransactionsByType();
316326
options.layeredOptions.txPoolMaxFutureBySender = config.getMaxFutureBySender();
327+
options.layeredOptions.minScore = config.getMinScore();
317328
options.sequencedOptions.txPoolLimitByAccountPercentage =
318329
config.getTxPoolLimitByAccountPercentage();
319330
options.sequencedOptions.txPoolMaxSize = config.getTxPoolMaxSize();
@@ -372,6 +383,7 @@ public TransactionPoolConfiguration toDomainObject() {
372383
.maxPrioritizedTransactions(layeredOptions.txPoolMaxPrioritized)
373384
.maxPrioritizedTransactionsByType(layeredOptions.txPoolMaxPrioritizedByType)
374385
.maxFutureBySender(layeredOptions.txPoolMaxFutureBySender)
386+
.minScore(layeredOptions.minScore)
375387
.txPoolLimitByAccountPercentage(sequencedOptions.txPoolLimitByAccountPercentage)
376388
.txPoolMaxSize(sequencedOptions.txPoolMaxSize)
377389
.pendingTxRetentionPeriod(sequencedOptions.pendingTxRetentionPeriod)

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,24 @@ public void maxPrioritizedTxsPerTypeWrongTxType() {
418418
"WRONG_TYPE=1");
419419
}
420420

421+
@Test
422+
public void minScoreWorks() {
423+
final byte minScore = -10;
424+
internalTestSuccess(
425+
config -> assertThat(config.getMinScore()).isEqualTo(minScore),
426+
"--tx-pool-min-score",
427+
Byte.toString(minScore));
428+
}
429+
430+
@Test
431+
public void minScoreNonByteValueReturnError() {
432+
final var overflowMinScore = Integer.toString(-300);
433+
internalTestFailure(
434+
"Invalid value for option '--tx-pool-min-score': '" + overflowMinScore + "' is not a byte",
435+
"--tx-pool-min-score",
436+
overflowMinScore);
437+
}
438+
421439
@Override
422440
protected TransactionPoolConfiguration createDefaultDomainObject() {
423441
return TransactionPoolConfiguration.DEFAULT;

besu/src/test/resources/everything_config.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ tx-pool-retention-hours=999
195195
tx-pool-max-size=1234
196196
tx-pool-limit-by-account-percentage=0.017
197197
tx-pool-min-gas-price=1000
198+
tx-pool-min-score=100
198199

199200
# Revert Reason
200201
revert-reason-enabled=false

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
@@ -80,6 +80,7 @@ enum Implementation {
8080
Implementation DEFAULT_TX_POOL_IMPLEMENTATION = Implementation.LAYERED;
8181
Set<Address> DEFAULT_PRIORITY_SENDERS = Set.of();
8282
Wei DEFAULT_TX_POOL_MIN_GAS_PRICE = Wei.of(1000);
83+
byte DEFAULT_TX_POOL_MIN_SCORE = -128;
8384

8485
TransactionPoolConfiguration DEFAULT = ImmutableTransactionPoolConfiguration.builder().build();
8586

@@ -173,6 +174,11 @@ default Wei getMinGasPrice() {
173174
return DEFAULT_TX_POOL_MIN_GAS_PRICE;
174175
}
175176

177+
@Value.Default
178+
default byte getMinScore() {
179+
return DEFAULT_TX_POOL_MIN_SCORE;
180+
}
181+
176182
@Value.Default
177183
default TransactionPoolValidatorService getTransactionPoolValidatorService() {
178184
return new TransactionPoolValidatorService() {

ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/AbstractTransactionsLayer.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import static org.hyperledger.besu.ethereum.eth.transactions.TransactionAddedResult.TRY_NEXT_LAYER;
2222
import static org.hyperledger.besu.ethereum.eth.transactions.layered.AddReason.MOVE;
2323
import static org.hyperledger.besu.ethereum.eth.transactions.layered.RemovalReason.LayerMoveReason.EVICTED;
24+
import static org.hyperledger.besu.ethereum.eth.transactions.layered.RemovalReason.PoolRemovalReason.BELOW_MIN_SCORE;
2425
import static org.hyperledger.besu.ethereum.eth.transactions.layered.RemovalReason.PoolRemovalReason.CONFIRMED;
2526
import static org.hyperledger.besu.ethereum.eth.transactions.layered.RemovalReason.PoolRemovalReason.CROSS_LAYER_REPLACED;
2627
import static org.hyperledger.besu.ethereum.eth.transactions.layered.RemovalReason.PoolRemovalReason.REPLACED;
@@ -481,6 +482,9 @@ public void penalize(final PendingTransaction penalizedTransaction) {
481482
if (pendingTransactions.containsKey(penalizedTransaction.getHash())) {
482483
internalPenalize(penalizedTransaction);
483484
metrics.incrementPenalized(penalizedTransaction, name());
485+
if (penalizedTransaction.getScore() < poolConfig.getMinScore()) {
486+
remove(penalizedTransaction, BELOW_MIN_SCORE);
487+
}
484488
} else {
485489
nextLayer.penalize(penalizedTransaction);
486490
}

ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayeredPendingTransactions.java

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
4343

4444
import java.util.ArrayDeque;
45-
import java.util.ArrayList;
4645
import java.util.HashSet;
4746
import java.util.List;
4847
import java.util.Map;
@@ -316,7 +315,6 @@ public synchronized List<Transaction> getPriorityTransactions() {
316315

317316
@Override
318317
public void selectTransactions(final PendingTransactions.TransactionSelector selector) {
319-
final List<PendingTransaction> penalizedTransactions = new ArrayList<>();
320318
final Set<Address> skipSenders = new HashSet<>();
321319

322320
final Map<Byte, List<SenderPendingTransactions>> candidateTxsByScore;
@@ -356,7 +354,12 @@ public void selectTransactions(final PendingTransactions.TransactionSelector sel
356354
}
357355

358356
if (selectionResult.penalize()) {
359-
penalizedTransactions.add(candidatePendingTx);
357+
ethScheduler.scheduleTxWorkerTask(
358+
() -> {
359+
synchronized (this) {
360+
prioritizedTransactions.penalize(candidatePendingTx);
361+
}
362+
});
360363
LOG.atTrace()
361364
.setMessage("Transaction {} penalized")
362365
.addArgument(candidatePendingTx::toTraceLog)
@@ -379,15 +382,6 @@ public void selectTransactions(final PendingTransactions.TransactionSelector sel
379382
}
380383
}
381384
}
382-
383-
ethScheduler.scheduleTxWorkerTask(
384-
() ->
385-
penalizedTransactions.forEach(
386-
penalizedTx -> {
387-
synchronized (this) {
388-
prioritizedTransactions.penalize(penalizedTx);
389-
}
390-
}));
391385
}
392386

393387
@Override

ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/RemovalReason.java

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,25 +52,30 @@ enum RemovedFrom {
5252
/** The reason why the tx has been removed from the pool */
5353
enum PoolRemovalReason implements RemovalReason {
5454
/** Tx removed since it is confirmed on chain, as part of an imported block. */
55-
CONFIRMED(),
55+
CONFIRMED,
5656
/** Tx removed since it has been replaced by another one added in the same layer. */
57-
REPLACED(),
57+
REPLACED,
5858
/** Tx removed since it has been replaced by another one added in another layer. */
59-
CROSS_LAYER_REPLACED(),
59+
CROSS_LAYER_REPLACED,
6060
/** Tx removed when the pool is full, to make space for new incoming txs. */
61-
DROPPED(),
61+
DROPPED,
6262
/**
6363
* Tx removed since found invalid after it was added to the pool, for example during txs
6464
* selection for a new block proposal.
6565
*/
66-
INVALIDATED(),
66+
INVALIDATED,
6767
/**
6868
* Special case, when for a sender, discrepancies are found between the world state view and the
6969
* pool view, then all the txs for this sender are removed and added again. Discrepancies, are
7070
* rare, and can happen during a short windows when a new block is being imported and the world
7171
* state being updated.
7272
*/
73-
RECONCILED();
73+
RECONCILED,
74+
/**
75+
* When a pending tx is penalized its score is decreased, if at some point its score is lower
76+
* than the configured minimum then the pending tx is removed from the pool.
77+
*/
78+
BELOW_MIN_SCORE;
7479

7580
private final String label;
7681

@@ -95,22 +100,22 @@ enum LayerMoveReason implements RemovalReason {
95100
* When the current layer is full, and this tx needs to be moved to the lower layer, in order to
96101
* free space.
97102
*/
98-
EVICTED(),
103+
EVICTED,
99104
/**
100105
* Specific to sequential layers, when a tx is removed because found invalid, then if the sender
101106
* has other txs with higher nonce, then a gap is created, and since sequential layers do not
102107
* permit gaps, txs following the invalid one need to be moved to lower layers.
103108
*/
104-
FOLLOW_INVALIDATED(),
109+
FOLLOW_INVALIDATED,
105110
/**
106111
* When a tx is moved to the upper layer, since it satisfies all the requirement to be promoted.
107112
*/
108-
PROMOTED(),
113+
PROMOTED,
109114
/**
110115
* When a tx is moved to the lower layer, since it, or a preceding one from the same sender,
111116
* does not respect anymore the requisites to stay in this layer.
112117
*/
113-
DEMOTED();
118+
DEMOTED;
114119

115120
private final String label;
116121

ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayersTest.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,14 @@ public class LayersTest extends BaseTransactionPoolTest {
7474
private static final int MAX_FUTURE_FOR_SENDER = 10;
7575
private static final Wei BASE_FEE = Wei.ONE;
7676
private static final Wei MIN_GAS_PRICE = BASE_FEE;
77+
private static final byte MIN_SCORE = 125;
7778

7879
private static final TransactionPoolConfiguration DEFAULT_TX_POOL_CONFIG =
7980
ImmutableTransactionPoolConfiguration.builder()
8081
.maxPrioritizedTransactions(MAX_PRIO_TRANSACTIONS)
8182
.maxPrioritizedTransactionsByType(Map.of(BLOB, 1))
8283
.maxFutureBySender(MAX_FUTURE_FOR_SENDER)
84+
.minScore(MIN_SCORE)
8385
.pendingTransactionsLayerMaxCapacityBytes(
8486
new PendingTransaction.Remote(
8587
new BaseTransactionPoolTest().createEIP1559Transaction(0, KEYS1, 1))
@@ -92,6 +94,7 @@ public class LayersTest extends BaseTransactionPoolTest {
9294
.maxPrioritizedTransactions(MAX_PRIO_TRANSACTIONS)
9395
.maxPrioritizedTransactionsByType(Map.of(BLOB, 1))
9496
.maxFutureBySender(MAX_FUTURE_FOR_SENDER)
97+
.minScore(MIN_SCORE)
9598
.pendingTransactionsLayerMaxCapacityBytes(
9699
new PendingTransaction.Remote(
97100
new BaseTransactionPoolTest().createEIP4844Transaction(0, KEYS1, 1, 1))
@@ -1293,7 +1296,17 @@ static Stream<Arguments> providerPenalized() {
12931296
.penalizeForSender(S2, 1)
12941297
.addForSender(S2, 2)
12951298
.expectedReadyForSenders(S1, 0, S1, 1, S2, 1)
1296-
.expectedSparseForSender(S2, 2)));
1299+
.expectedSparseForSender(S2, 2)),
1300+
Arguments.of(
1301+
new Scenario("remove below min score")
1302+
.addForSender(S1, 0) // score 127
1303+
.expectedPrioritizedForSender(S1, 0)
1304+
.penalizeForSender(S1, 0) // score 126
1305+
.expectedPrioritizedForSender(S1, 0)
1306+
.penalizeForSender(S1, 0) // score 125
1307+
.expectedPrioritizedForSender(S1, 0)
1308+
.penalizeForSender(S1, 0) // score 124, removed since decreased score < MIN_SCORE
1309+
.expectedPrioritizedForSenders()));
12971310
}
12981311

12991312
private static BlockHeader mockBlockHeader() {

0 commit comments

Comments
 (0)