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

Commit 7389b73

Browse files
tomusdrwgavofyork
authored andcommitted
Fix a import+prune+replace case for multi-provides transactions. (#3939)
* Fix a import+prune+replace case for multi-provides transactions. * Fix tests.
1 parent 30faeef commit 7389b73

File tree

3 files changed

+65
-10
lines changed

3 files changed

+65
-10
lines changed

core/transaction-pool/graph/src/pool.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ impl<B: ChainApi> Pool<B> {
240240
tags: impl IntoIterator<Item=Tag>,
241241
known_imported_hashes: impl IntoIterator<Item=ExHash<B>> + Clone,
242242
) -> impl Future<Output=Result<(), B::Error>> {
243+
log::trace!(target: "txpool", "Pruning at {:?}", at);
243244
// Prune all transactions that provide given tags
244245
let prune_status = match self.validated_pool.prune_tags(tags) {
245246
Ok(prune_status) => prune_status,
@@ -257,6 +258,7 @@ impl<B: ChainApi> Pool<B> {
257258
let pruned_transactions = prune_status.pruned.into_iter().map(|tx| tx.data.clone());
258259
let reverify_future = self.verify(at, pruned_transactions, false);
259260

261+
log::trace!(target: "txpool", "Prunning at {:?}. Resubmitting transactions.", at);
260262
// And finally - submit reverified transactions back to the pool
261263
let at = at.clone();
262264
let validated_pool = self.validated_pool.clone();
@@ -908,3 +910,4 @@ mod tests {
908910
}
909911
}
910912
}
913+

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,20 @@ impl<Hash: hash::Hash + Member + Serialize, Ex> ReadyTransactions<Hash, Ex> {
338338
}
339339
}
340340

341-
debug!(target: "txpool", "[{:?}] Pruned.", tx.hash);
341+
// we also need to remove all other tags that this transaction provides,
342+
// but since all the hard work is done, we only clear the provided_tag -> hash
343+
// mapping.
344+
let current_tag = &tag;
345+
for tag in &tx.provides {
346+
let removed = self.provided_tags.remove(tag);
347+
assert_eq!(
348+
removed.as_ref(),
349+
if current_tag == tag { None } else { Some(&tx.hash) },
350+
"The pool contains exactly one transaction providing given tag; the removed transaction
351+
claims to provide that tag, so it has to be mapped to it's hash; qed"
352+
);
353+
}
354+
342355
removed.push(tx);
343356
}
344357
}

core/transaction-pool/src/tests.rs

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,15 @@ use sr_primitives::{
2727
transaction_validity::{TransactionValidity, ValidTransaction},
2828
};
2929

30-
struct TestApi;
30+
struct TestApi {
31+
pub modifier: Box<dyn Fn(&mut ValidTransaction) + Send + Sync>,
32+
}
3133

3234
impl TestApi {
3335
fn default() -> Self {
34-
TestApi
36+
TestApi {
37+
modifier: Box::new(|_| {}),
38+
}
3539
}
3640
}
3741

@@ -54,14 +58,18 @@ impl txpool::ChainApi for TestApi {
5458
};
5559
let provides = vec![vec![uxt.transfer().nonce as u8]];
5660

61+
let mut validity = ValidTransaction {
62+
priority: 1,
63+
requires,
64+
provides,
65+
longevity: 64,
66+
propagate: true,
67+
};
68+
69+
(self.modifier)(&mut validity);
70+
5771
futures::future::ready(Ok(
58-
Ok(ValidTransaction {
59-
priority: 1,
60-
requires,
61-
provides,
62-
longevity: 64,
63-
propagate: true,
64-
})
72+
Ok(validity)
6573
))
6674
}
6775

@@ -181,3 +189,34 @@ fn should_ban_invalid_transactions() {
181189
// then
182190
block_on(pool.submit_one(&BlockId::number(0), uxt.clone())).unwrap_err();
183191
}
192+
193+
#[test]
194+
fn should_correctly_prune_transactions_providing_more_than_one_tag() {
195+
let mut api = TestApi::default();
196+
api.modifier = Box::new(|v: &mut ValidTransaction| {
197+
v.provides.push(vec![155]);
198+
});
199+
let pool = Pool::new(Default::default(), api);
200+
let xt = uxt(Alice, 209);
201+
block_on(pool.submit_one(&BlockId::number(0), xt.clone())).expect("1. Imported");
202+
assert_eq!(pool.status().ready, 1);
203+
204+
// remove the transaction that just got imported.
205+
block_on(pool.prune_tags(&BlockId::number(1), vec![vec![209]], vec![])).expect("1. Pruned");
206+
assert_eq!(pool.status().ready, 0);
207+
// it's re-imported to future
208+
assert_eq!(pool.status().future, 1);
209+
210+
// so now let's insert another transaction that also provides the 155
211+
let xt = uxt(Alice, 211);
212+
block_on(pool.submit_one(&BlockId::number(2), xt.clone())).expect("2. Imported");
213+
assert_eq!(pool.status().ready, 1);
214+
assert_eq!(pool.status().future, 1);
215+
let pending: Vec<_> = pool.ready().map(|a| a.data.transfer().nonce).collect();
216+
assert_eq!(pending, vec![211]);
217+
218+
// prune it and make sure the pool is empty
219+
block_on(pool.prune_tags(&BlockId::number(3), vec![vec![155]], vec![])).expect("2. Pruned");
220+
assert_eq!(pool.status().ready, 0);
221+
assert_eq!(pool.status().future, 2);
222+
}

0 commit comments

Comments
 (0)