Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit 4eaea34

Browse files
bkchrNikVolf
andauthored
Fix tx-pool returning the same transaction multiple times (#6535)
* Fix tx-pool returning the same transaction multiple times This fixes a bug that lead to returning the same transaction multiple times when iterating the `ready` iterator. Internally the transaction was kept in the `best` list and could be duplicated in that list be re-inserting it again. This `best` list is using a `TransactionRef` which internally uses a `insertion_id`. This `insertion_id` could lead to the same transaction being inserted multiple times into the `best` list. * Update client/transaction-pool/src/testing/pool.rs Co-authored-by: Nikolay Volf <[email protected]> Co-authored-by: Nikolay Volf <[email protected]>
1 parent 0d0a84d commit 4eaea34

File tree

5 files changed

+63
-24
lines changed

5 files changed

+63
-24
lines changed

client/transaction-pool/graph/src/ready.rs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -275,12 +275,7 @@ impl<Hash: hash::Hash + Member + Serialize, Ex> ReadyTransactions<Hash, Ex> {
275275
) -> Vec<Arc<Transaction<Hash, Ex>>> {
276276
let mut removed = vec![];
277277
let mut ready = self.ready.write();
278-
loop {
279-
let hash = match to_remove.pop() {
280-
Some(hash) => hash,
281-
None => return removed,
282-
};
283-
278+
while let Some(hash) = to_remove.pop() {
284279
if let Some(mut tx) = ready.remove(&hash) {
285280
let invalidated = tx.transaction.transaction.provides
286281
.iter()
@@ -319,6 +314,8 @@ impl<Hash: hash::Hash + Member + Serialize, Ex> ReadyTransactions<Hash, Ex> {
319314
removed.push(tx.transaction.transaction);
320315
}
321316
}
317+
318+
removed
322319
}
323320

324321
/// Removes transactions that provide given tag.
@@ -330,17 +327,16 @@ impl<Hash: hash::Hash + Member + Serialize, Ex> ReadyTransactions<Hash, Ex> {
330327
let mut removed = vec![];
331328
let mut to_remove = vec![tag];
332329

333-
loop {
334-
let tag = match to_remove.pop() {
335-
Some(tag) => tag,
336-
None => return removed,
337-
};
338-
330+
while let Some(tag) = to_remove.pop() {
339331
let res = self.provided_tags.remove(&tag)
340-
.and_then(|hash| self.ready.write().remove(&hash));
332+
.and_then(|hash| self.ready.write().remove(&hash));
341333

342334
if let Some(tx) = res {
343335
let unlocks = tx.unlocks;
336+
337+
// Make sure we remove it from best txs
338+
self.best.remove(&tx.transaction);
339+
344340
let tx = tx.transaction.transaction;
345341

346342
// prune previous transactions as well
@@ -403,6 +399,8 @@ impl<Hash: hash::Hash + Member + Serialize, Ex> ReadyTransactions<Hash, Ex> {
403399
removed.push(tx);
404400
}
405401
}
402+
403+
removed
406404
}
407405

408406
/// Checks if the transaction is providing the same tags as other transactions.

client/transaction-pool/src/revalidation.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,14 +141,14 @@ impl<Api: ChainApi> RevalidationWorker<Api> {
141141
// which they got into the pool
142142
while left > 0 {
143143
let first_block = match self.block_ordered.keys().next().cloned() {
144-
Some(bn) => bn,
145-
None => break,
144+
Some(bn) => bn,
145+
None => break,
146146
};
147147
let mut block_drained = false;
148148
if let Some(extrinsics) = self.block_ordered.get_mut(&first_block) {
149149
let to_queue = extrinsics.iter().take(left).cloned().collect::<Vec<_>>();
150150
if to_queue.len() == extrinsics.len() {
151-
block_drained = true;
151+
block_drained = true;
152152
} else {
153153
for xt in &to_queue {
154154
extrinsics.remove(xt);
@@ -159,7 +159,7 @@ impl<Api: ChainApi> RevalidationWorker<Api> {
159159
}
160160

161161
if block_drained {
162-
self.block_ordered.remove(&first_block);
162+
self.block_ordered.remove(&first_block);
163163
}
164164
}
165165

client/transaction-pool/src/testing/pool.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,3 +1066,28 @@ fn import_notification_to_pool_maintain_works() {
10661066
block_on(pool.maintain(evt.into()));
10671067
assert_eq!(pool.status().ready, 0);
10681068
}
1069+
1070+
// When we prune transactions, we need to make sure that we remove
1071+
#[test]
1072+
fn pruning_a_transaction_should_remove_it_from_best_transaction() {
1073+
let (pool, _guard, _notifier) = maintained_pool();
1074+
1075+
let xt1 = Extrinsic::IncludeData(Vec::new());
1076+
1077+
block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt1.clone())).expect("1. Imported");
1078+
let header = pool.api.push_block(1, vec![xt1.clone()]);
1079+
1080+
// This will prune `xt1`.
1081+
block_on(pool.maintain(block_event(header)));
1082+
1083+
// Submit the tx again.
1084+
block_on(pool.submit_one(&BlockId::number(1), SOURCE, xt1.clone())).expect("2. Imported");
1085+
1086+
let mut iterator = block_on(pool.ready_at(1));
1087+
1088+
assert_eq!(iterator.next().unwrap().data, xt1.clone());
1089+
1090+
// If the tx was not removed from the best txs, the tx would be
1091+
// returned a second time by the iterator.
1092+
assert!(iterator.next().is_none());
1093+
}

test-utils/runtime/src/lib.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,10 +194,20 @@ impl sp_runtime::traits::Dispatchable for Extrinsic {
194194
}
195195

196196
impl Extrinsic {
197+
/// Convert `&self` into `&Transfer`.
198+
///
199+
/// Panics if this is no `Transfer` extrinsic.
197200
pub fn transfer(&self) -> &Transfer {
201+
self.try_transfer().expect("cannot convert to transfer ref")
202+
}
203+
204+
/// Try to convert `&self` into `&Transfer`.
205+
///
206+
/// Returns `None` if this is no `Transfer` extrinsic.
207+
pub fn try_transfer(&self) -> Option<&Transfer> {
198208
match self {
199-
Extrinsic::Transfer { ref transfer, .. } => transfer,
200-
_ => panic!("cannot convert to transfer ref"),
209+
Extrinsic::Transfer { ref transfer, .. } => Some(transfer),
210+
_ => None,
201211
}
202212
}
203213
}

test-utils/runtime/transaction-pool/src/lib.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -209,13 +209,19 @@ impl sc_transaction_graph::ChainApi for TestApi {
209209
) -> Self::ValidationFuture {
210210
self.validation_requests.write().push(uxt.clone());
211211

212-
let chain_nonce = self.chain.read().nonces.get(&uxt.transfer().from).cloned().unwrap_or(0);
213-
let requires = if chain_nonce == uxt.transfer().nonce {
214-
vec![]
212+
let (requires, provides) = if let Some(transfer) = uxt.try_transfer() {
213+
let chain_nonce = self.chain.read().nonces.get(&transfer.from).cloned().unwrap_or(0);
214+
let requires = if chain_nonce == transfer.nonce {
215+
vec![]
216+
} else {
217+
vec![vec![chain_nonce as u8]]
218+
};
219+
let provides = vec![vec![transfer.nonce as u8]];
220+
221+
(requires, provides)
215222
} else {
216-
vec![vec![chain_nonce as u8]]
223+
(Vec::new(), vec![uxt.encode()])
217224
};
218-
let provides = vec![vec![uxt.transfer().nonce as u8]];
219225

220226
if self.chain.read().invalid_hashes.contains(&self.hash_and_length(&uxt).0) {
221227
return futures::future::ready(Ok(

0 commit comments

Comments
 (0)