From 664898a2a513c4085092f406abf7f194f1b3ef12 Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 25 Oct 2023 13:43:28 +0800 Subject: [PATCH] fix orphan for rbf and adjust evict interval --- tx-pool/src/component/orphan.rs | 27 ++++++------- tx-pool/src/component/tests/mod.rs | 1 + tx-pool/src/component/tests/orphan.rs | 58 +++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 15 deletions(-) create mode 100644 tx-pool/src/component/tests/orphan.rs diff --git a/tx-pool/src/component/orphan.rs b/tx-pool/src/component/orphan.rs index ae045beab9c..02479f1726a 100644 --- a/tx-pool/src/component/orphan.rs +++ b/tx-pool/src/component/orphan.rs @@ -7,10 +7,10 @@ use ckb_types::{ packed::{OutPoint, ProposalShortId}, }; use ckb_util::shrink_to_fit; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; const SHRINK_THRESHOLD: usize = 100; -pub(crate) const ORPHAN_TX_EXPIRE_TIME: u64 = 2 * MAX_BLOCK_INTERVAL; // double block interval +pub(crate) const ORPHAN_TX_EXPIRE_TIME: u64 = 100 * MAX_BLOCK_INTERVAL; // double block interval pub(crate) const DEFAULT_MAX_ORPHAN_TRANSACTIONS: usize = 100; #[derive(Debug, Clone)] @@ -39,7 +39,7 @@ impl Entry { #[derive(Default, Debug, Clone)] pub(crate) struct OrphanPool { pub(crate) entries: HashMap, - pub(crate) by_out_point: HashMap, + pub(crate) by_out_point: HashMap>, } impl OrphanPool { @@ -60,7 +60,7 @@ impl OrphanPool { self.entries.contains_key(id) } - pub fn shrink_to_fit(&mut self) { + fn shrink_to_fit(&mut self) { shrink_to_fit!(self.entries, SHRINK_THRESHOLD); shrink_to_fit!(self.by_out_point, SHRINK_THRESHOLD); } @@ -73,7 +73,9 @@ impl OrphanPool { self.entries.remove(id).map(|entry| { debug!("remove orphan tx {}", entry.tx.hash()); for out_point in entry.tx.input_pts_iter() { - self.by_out_point.remove(&out_point); + self.by_out_point + .get_mut(&out_point) + .map(|set| set.remove(id)); } entry }) @@ -134,23 +136,17 @@ impl OrphanPool { return vec![]; } - // double spend checking - if tx - .input_pts_iter() - .any(|out_point| self.by_out_point.contains_key(&out_point)) - { - return vec![]; - } - debug!("add_orphan_tx {}", tx.hash()); - self.entries.insert( tx.proposal_short_id(), Entry::new(tx.clone(), peer, declared_cycle), ); for out_point in tx.input_pts_iter() { - self.by_out_point.insert(out_point, tx.proposal_short_id()); + self.by_out_point + .entry(out_point) + .or_insert_with(HashSet::default) + .insert(tx.proposal_short_id()); } self.limit_size() @@ -160,6 +156,7 @@ impl OrphanPool { tx.output_pts() .iter() .filter_map(|out_point| self.by_out_point.get(out_point).cloned()) + .flatten() .collect::>() } } diff --git a/tx-pool/src/component/tests/mod.rs b/tx-pool/src/component/tests/mod.rs index fb851e48553..13ba389e820 100644 --- a/tx-pool/src/component/tests/mod.rs +++ b/tx-pool/src/component/tests/mod.rs @@ -1,5 +1,6 @@ mod chunk; mod entry; +mod orphan; mod pending; mod proposed; mod recent_reject; diff --git a/tx-pool/src/component/tests/orphan.rs b/tx-pool/src/component/tests/orphan.rs new file mode 100644 index 00000000000..9c59c5c1e47 --- /dev/null +++ b/tx-pool/src/component/tests/orphan.rs @@ -0,0 +1,58 @@ +use crate::component::orphan::OrphanPool; +use crate::component::tests::util::build_tx; +use ckb_types::packed::Byte32; + +#[test] +fn test_orphan() { + let tx1 = build_tx(vec![(&Byte32::zero(), 1), (&Byte32::zero(), 2)], 1); + let mut orphan = OrphanPool::new(); + assert_eq!(orphan.len(), 0); + assert_eq!(orphan.contains_key(&tx1.proposal_short_id()), false); + + orphan.add_orphan_tx(tx1.clone(), 0.into(), 0); + assert_eq!(orphan.len(), 1); + + orphan.add_orphan_tx(tx1.clone(), 0.into(), 0); + assert_eq!(orphan.len(), 1); + + let tx2 = build_tx(vec![(&tx1.hash(), 0)], 1); + orphan.add_orphan_tx(tx2.clone(), 0.into(), 0); + assert_eq!(orphan.len(), 2); + + orphan.remove_orphan_tx(&tx1.proposal_short_id()); + assert_eq!(orphan.len(), 1); + orphan.remove_orphan_tx(&tx2.proposal_short_id()); + assert_eq!(orphan.len(), 0); +} + +#[test] +fn test_orphan_duplicated() { + let tx1 = build_tx(vec![(&Byte32::zero(), 1), (&Byte32::zero(), 2)], 3); + let mut orphan = OrphanPool::new(); + + let tx2 = build_tx(vec![(&tx1.hash(), 0)], 1); + let tx3 = build_tx(vec![(&tx2.hash(), 0)], 1); + let tx4 = build_tx(vec![(&tx3.hash(), 0), (&tx1.hash(), 1)], 1); + let tx5 = build_tx(vec![(&tx1.hash(), 2)], 1); + orphan.add_orphan_tx(tx1.clone(), 0.into(), 0); + orphan.add_orphan_tx(tx2.clone(), 0.into(), 0); + orphan.add_orphan_tx(tx3.clone(), 0.into(), 0); + orphan.add_orphan_tx(tx4.clone(), 0.into(), 0); + orphan.add_orphan_tx(tx5.clone(), 0.into(), 0); + assert_eq!(orphan.len(), 5); + + let txs = orphan.find_by_previous(&tx2); + assert_eq!(txs.len(), 1); + + let txs = orphan.find_by_previous(&tx1); + assert_eq!(txs.len(), 3); + assert!(txs.contains(&tx2.proposal_short_id())); + assert!(txs.contains(&tx4.proposal_short_id())); + assert!(txs.contains(&tx5.proposal_short_id())); + + orphan.remove_orphan_tx(&tx4.proposal_short_id()); + let txs = orphan.find_by_previous(&tx1); + assert_eq!(txs.len(), 2); + assert!(txs.contains(&tx2.proposal_short_id())); + assert!(txs.contains(&tx5.proposal_short_id())); +}