diff --git a/Cargo.lock b/Cargo.lock index 0d4eddf4f954d..7dc6dc0617b42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6552,9 +6552,9 @@ dependencies = [ [[package]] name = "frame-decode" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c554ce2394e2c04426a070b4cb133c72f6f14c86b665f4e13094addd8e8958" +checksum = "a7cb8796f93fa038f979a014234d632e9688a120e745f936e2635123c77537f7" dependencies = [ "frame-metadata 20.0.0", "parity-scale-codec", @@ -20601,9 +20601,9 @@ dependencies = [ [[package]] name = "scale-typegen" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aebea322734465f39e4ad8100e1f9708c6c6c325d92b8780015d30c44fae791" +checksum = "05c61b6b706a3eaad63b506ab50a1d2319f817ae01cf753adcc3f055f9f0fcd6" dependencies = [ "proc-macro2 1.0.95", "quote 1.0.40", @@ -24871,7 +24871,7 @@ dependencies = [ "proc-macro2 1.0.95", "quote 1.0.40", "scale-info", - "scale-typegen 0.11.0", + "scale-typegen 0.11.1", "subxt-metadata 0.41.0", "syn 2.0.98", "thiserror 2.0.12", @@ -24915,7 +24915,7 @@ dependencies = [ "base58", "blake2 0.10.6", "derive-where", - "frame-decode 0.7.0", + "frame-decode 0.7.1", "frame-metadata 20.0.0", "hashbrown 0.14.5", "hex", @@ -24996,7 +24996,7 @@ dependencies = [ "parity-scale-codec", "proc-macro-error2", "quote 1.0.40", - "scale-typegen 0.11.0", + "scale-typegen 0.11.1", "subxt-codegen 0.41.0", "subxt-utils-fetchmetadata 0.41.0", "syn 2.0.98", @@ -25022,7 +25022,7 @@ version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fff4591673600c4388e21305788282414d26c791b4dee21b7cb0b19c10076f98" dependencies = [ - "frame-decode 0.7.0", + "frame-decode 0.7.1", "frame-metadata 20.0.0", "hashbrown 0.14.5", "parity-scale-codec", diff --git a/prdoc/pr_8500.prdoc b/prdoc/pr_8500.prdoc new file mode 100644 index 0000000000000..b08333ef2e08a --- /dev/null +++ b/prdoc/pr_8500.prdoc @@ -0,0 +1,9 @@ +title: 'txpool: fix tx removal from unlocks set' +doc: +- audience: Node Dev + description: |- + Now removing a tx subtree will correctly remove it from the transactions that would unlock it. + +crates: +- name: sc-transaction-pool + bump: major diff --git a/substrate/client/transaction-pool/src/graph/ready.rs b/substrate/client/transaction-pool/src/graph/ready.rs index 3e52d3e4d9c0f..88986baa88ed1 100644 --- a/substrate/client/transaction-pool/src/graph/ready.rs +++ b/substrate/client/transaction-pool/src/graph/ready.rs @@ -298,8 +298,8 @@ impl ReadyTransactions { // remove from unlocks for tag in &tx.transaction.transaction.requires { if let Some(hash) = self.provided_tags.get(tag) { - if let Some(tx) = ready.get_mut(hash) { - remove_item(&mut tx.unlocks, hash); + if let Some(tx_unlocking) = ready.get_mut(hash) { + remove_item(&mut tx_unlocking.unlocks, &tx_hash); } } } @@ -788,4 +788,40 @@ mod tests { assert_eq!(it.next().as_ref().map(data), Some(7)); assert_eq!(it.next().as_ref().map(data), None); } + + #[test] + fn should_remove_tx_from_unlocks_set_of_its_parent() { + // given + let mut ready = ReadyTransactions::default(); + populate_pool(&mut ready); + + // when + let mut it = ready.get(); + let tx1 = it.next().unwrap(); + let tx2 = it.next().unwrap(); + let tx3 = it.next().unwrap(); + let tx4 = it.next().unwrap(); + let lock = ready.ready.read(); + let tx1_unlocks = &lock.get(&tx1.hash).unwrap().unlocks; + + // There are two tags provided by tx1 and required by tx2. + assert_eq!(tx1_unlocks[0], tx2.hash); + assert_eq!(tx1_unlocks[1], tx2.hash); + assert_eq!(tx1_unlocks[2], tx3.hash); + assert_eq!(tx1_unlocks[4], tx4.hash); + drop(lock); + + // then consider tx2 invalid, and hence, remove it. + let removed = ready.remove_subtree(&[tx2.hash]); + assert_eq!(removed.len(), 2); + assert_eq!(removed[0].hash, tx2.hash); + // tx3 is removed too, since it requires tx2 provides tags. + assert_eq!(removed[1].hash, tx3.hash); + + let lock = ready.ready.read(); + let tx1_unlocks = &lock.get(&tx1.hash).unwrap().unlocks; + assert!(!tx1_unlocks.contains(&tx2.hash)); + assert!(!tx1_unlocks.contains(&tx3.hash)); + assert!(tx1_unlocks.contains(&tx4.hash)); + } }