-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Description
Describe the feature
we currently trigger poolupdates in two places:
reth/crates/transaction-pool/src/pool/txpool.rs
Lines 578 to 593 in 6d4a1a3
// update block info | |
let block_hash = block_info.last_seen_block_hash; | |
self.set_block_info(block_info); | |
// Remove all transaction that were included in the block | |
let mut removed_txs_count = 0; | |
for tx_hash in &mined_transactions { | |
if self.prune_transaction_by_hash(tx_hash).is_some() { | |
removed_txs_count += 1; | |
} | |
} | |
// Update removed transactions metric | |
self.metrics.removed_transactions.increment(removed_txs_count); | |
let UpdateOutcome { promoted, discarded } = self.update_accounts(changed_senders); |
Place 1: Basefee/Blobfee-triggered updates (lines 580-580)
// update block info
let block_hash = block_info.last_seen_block_hash;
self.set_block_info(block_info);
This calls set_block_info()
which internally calls:
update_basefee()
→ can trigger promotions from BaseFee pool to Pending when basefee decreasesupdate_blob_fee()
→ can trigger promotions/demotions for blob transactions
Place 2: Account/sender-triggered updates (lines 593-593)
// Update removed transactions metric
self.metrics.removed_transactions.increment(removed_txs_count);
let UpdateOutcome { promoted, discarded } = self.update_accounts(changed_senders);
This calls update_accounts()
which:
- Processes sender balance/nonce changes
- Triggers sub-pool updates based on account state changes
The Problem
Both places can update the same transactions independently:
- Basefee decrease → promotes transactions from BaseFee → Pending via
enforce_basefee_with()
- Sender changes → same transactions might get updated again via
update_accounts()
This creates overlapping/duplicate tracking in the promoted
and discarded
outcome sets, leading to inaccurate metrics and potential inconsistent pool state.
where a lower basefee can trigger a promotion:
reth/crates/transaction-pool/src/pool/txpool.rs
Lines 295 to 310 in 6d4a1a3
Ordering::Less => { | |
// Base fee decreased: recheck BaseFee and promote. | |
// Invariants: | |
// - BaseFee contains only non-blob txs (blob txs live in Blob) and they already | |
// have ENOUGH_BLOB_FEE_CAP_BLOCK. | |
// - PENDING_POOL_BITS = BASE_FEE_POOL_BITS | ENOUGH_FEE_CAP_BLOCK | | |
// ENOUGH_BLOB_FEE_CAP_BLOCK. | |
// With the lower base fee they gain ENOUGH_FEE_CAP_BLOCK, so we can set the bit and | |
// insert directly into Pending (skip generic routing). | |
self.basefee_pool.enforce_basefee_with( | |
self.all_transactions.pending_fees.base_fee, | |
|tx| { | |
// Update transaction state — guaranteed Pending by the invariants above | |
let meta = | |
self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set"); | |
meta.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK); |
which we currently dont track properly and only via the changed accounts:
let UpdateOutcome { promoted, discarded } = self.update_accounts(changed_senders); |
the problem here is that these can overlap
e.g. promoted basefee txs can also appear in either promoted or discarded
a potential fix here is
before updating the basefee internally, use the new basefee to iterate over the changed senders
reth/crates/transaction-pool/src/pool/txpool.rs
Lines 554 to 555 in 6d4a1a3
// Apply the state changes to the total set of transactions which triggers sub-pool updates. | |
let updates = self.all_transactions.update(&changed_senders); |
then we get and perform the updates for all senders correctly with the new fee
and after we append the updates from updating the fee pool
Additional context
No response
Metadata
Metadata
Assignees
Labels
Type
Projects
Status