1use abtc_domain::primitives::{OutPoint, Transaction, Txid};
22use std::collections::{HashMap, HashSet};
23
24const MAX_ORPHAN_TRANSACTIONS: usize = 100;
28
29const ORPHAN_TX_EXPIRE_TIME: u64 = 20 * 60; const MAX_ORPHAN_TX_SIZE: usize = 100_000; #[derive(Debug, Clone)]
39pub struct OrphanEntry {
40 pub tx: Transaction,
42 pub time_added: u64,
44 pub from_peer: u64,
46 pub size: usize,
48}
49
50#[derive(Debug, Clone, PartialEq, Eq)]
52pub enum AddOrphanResult {
53 Added,
55 AlreadyExists,
57 TooLarge,
59 AddedAfterEviction { evicted: Txid },
61}
62
63pub struct OrphanPool {
65 orphans: HashMap<Txid, OrphanEntry>,
67 by_prev: HashMap<OutPoint, HashSet<Txid>>,
69 max_orphans: usize,
71 expire_time: u64,
73}
74
75impl OrphanPool {
76 pub fn new() -> Self {
78 OrphanPool {
79 orphans: HashMap::new(),
80 by_prev: HashMap::new(),
81 max_orphans: MAX_ORPHAN_TRANSACTIONS,
82 expire_time: ORPHAN_TX_EXPIRE_TIME,
83 }
84 }
85
86 pub fn with_config(max_orphans: usize, expire_time: u64) -> Self {
88 OrphanPool {
89 orphans: HashMap::new(),
90 by_prev: HashMap::new(),
91 max_orphans,
92 expire_time,
93 }
94 }
95
96 pub fn add_orphan(&mut self, tx: Transaction, from_peer: u64, now: u64) -> AddOrphanResult {
100 let txid = tx.txid();
101
102 if self.orphans.contains_key(&txid) {
104 return AddOrphanResult::AlreadyExists;
105 }
106
107 let size = tx.serialize().len();
109 if size > MAX_ORPHAN_TX_SIZE {
110 return AddOrphanResult::TooLarge;
111 }
112
113 let evicted = if self.orphans.len() >= self.max_orphans {
115 self.evict_random()
116 } else {
117 None
118 };
119
120 for input in &tx.inputs {
122 self.by_prev
123 .entry(input.previous_output)
124 .or_default()
125 .insert(txid);
126 }
127
128 self.orphans.insert(
129 txid,
130 OrphanEntry {
131 tx,
132 time_added: now,
133 from_peer,
134 size,
135 },
136 );
137
138 match evicted {
139 Some(evicted_txid) => AddOrphanResult::AddedAfterEviction {
140 evicted: evicted_txid,
141 },
142 None => AddOrphanResult::Added,
143 }
144 }
145
146 pub fn remove_orphan(&mut self, txid: &Txid) -> Option<OrphanEntry> {
148 if let Some(entry) = self.orphans.remove(txid) {
149 for input in &entry.tx.inputs {
151 if let Some(set) = self.by_prev.get_mut(&input.previous_output) {
152 set.remove(txid);
153 if set.is_empty() {
154 self.by_prev.remove(&input.previous_output);
155 }
156 }
157 }
158 Some(entry)
159 } else {
160 None
161 }
162 }
163
164 pub fn get_orphans_by_prev(&self, outpoint: &OutPoint) -> Vec<Txid> {
169 self.by_prev
170 .get(outpoint)
171 .map(|set| set.iter().copied().collect())
172 .unwrap_or_default()
173 }
174
175 pub fn get_children_of(&self, parent_txid: &Txid, output_count: u32) -> Vec<Txid> {
180 let mut children = HashSet::new();
181 for vout in 0..output_count {
182 let outpoint = OutPoint::new(*parent_txid, vout);
183 if let Some(set) = self.by_prev.get(&outpoint) {
184 children.extend(set.iter().copied());
185 }
186 }
187 children.into_iter().collect()
188 }
189
190 pub fn expire_old_orphans(&mut self, now: u64) -> usize {
194 let expired: Vec<Txid> = self
195 .orphans
196 .iter()
197 .filter(|(_, entry)| now.saturating_sub(entry.time_added) > self.expire_time)
198 .map(|(txid, _)| *txid)
199 .collect();
200
201 let count = expired.len();
202 for txid in expired {
203 self.remove_orphan(&txid);
204 }
205 count
206 }
207
208 pub fn remove_for_peer(&mut self, peer_id: u64) -> usize {
212 let to_remove: Vec<Txid> = self
213 .orphans
214 .iter()
215 .filter(|(_, entry)| entry.from_peer == peer_id)
216 .map(|(txid, _)| *txid)
217 .collect();
218
219 let count = to_remove.len();
220 for txid in to_remove {
221 self.remove_orphan(&txid);
222 }
223 count
224 }
225
226 pub fn get(&self, txid: &Txid) -> Option<&OrphanEntry> {
228 self.orphans.get(txid)
229 }
230
231 pub fn contains(&self, txid: &Txid) -> bool {
233 self.orphans.contains_key(txid)
234 }
235
236 pub fn len(&self) -> usize {
238 self.orphans.len()
239 }
240
241 pub fn is_empty(&self) -> bool {
243 self.orphans.is_empty()
244 }
245
246 pub fn clear(&mut self) {
248 self.orphans.clear();
249 self.by_prev.clear();
250 }
251
252 fn evict_random(&mut self) -> Option<Txid> {
257 let oldest = self
258 .orphans
259 .iter()
260 .min_by_key(|(_, entry)| entry.time_added)
261 .map(|(txid, _)| *txid);
262
263 if let Some(txid) = oldest {
264 self.remove_orphan(&txid);
265 Some(txid)
266 } else {
267 None
268 }
269 }
270}
271
272impl Default for OrphanPool {
273 fn default() -> Self {
274 Self::new()
275 }
276}
277
278#[cfg(test)]
279mod tests {
280 use super::*;
281 use abtc_domain::primitives::{Amount, Hash256, OutPoint, TxIn, TxOut};
282 use abtc_domain::script::Script;
283
284 fn make_tx(prev_txid: Txid, prev_vout: u32, value: i64) -> Transaction {
285 Transaction::v1(
286 vec![TxIn::final_input(
287 OutPoint::new(prev_txid, prev_vout),
288 Script::new(),
289 )],
290 vec![TxOut::new(Amount::from_sat(value), Script::new())],
291 0,
292 )
293 }
294
295 fn make_txid(byte: u8) -> Txid {
296 Txid::from_hash(Hash256::from_bytes([byte; 32]))
297 }
298
299 #[test]
300 fn test_add_orphan() {
301 let mut pool = OrphanPool::new();
302 let parent_txid = make_txid(0x01);
303 let tx = make_tx(parent_txid, 0, 5000);
304 let txid = tx.txid();
305
306 let result = pool.add_orphan(tx, 1, 1000);
307 assert_eq!(result, AddOrphanResult::Added);
308 assert_eq!(pool.len(), 1);
309 assert!(pool.contains(&txid));
310 }
311
312 #[test]
313 fn test_add_duplicate() {
314 let mut pool = OrphanPool::new();
315 let parent_txid = make_txid(0x01);
316 let tx = make_tx(parent_txid, 0, 5000);
317
318 pool.add_orphan(tx.clone(), 1, 1000);
319 let result = pool.add_orphan(tx, 1, 1001);
320 assert_eq!(result, AddOrphanResult::AlreadyExists);
321 assert_eq!(pool.len(), 1);
322 }
323
324 #[test]
325 fn test_remove_orphan() {
326 let mut pool = OrphanPool::new();
327 let parent_txid = make_txid(0x01);
328 let tx = make_tx(parent_txid, 0, 5000);
329 let txid = tx.txid();
330
331 pool.add_orphan(tx, 1, 1000);
332 let removed = pool.remove_orphan(&txid);
333 assert!(removed.is_some());
334 assert_eq!(pool.len(), 0);
335 assert!(!pool.contains(&txid));
336 }
337
338 #[test]
339 fn test_get_orphans_by_prev() {
340 let mut pool = OrphanPool::new();
341 let parent_txid = make_txid(0x01);
342
343 let tx1 = make_tx(parent_txid, 0, 3000);
345 let tx2 = make_tx(parent_txid, 1, 2000);
346 let txid1 = tx1.txid();
347 let txid2 = tx2.txid();
348
349 pool.add_orphan(tx1, 1, 1000);
350 pool.add_orphan(tx2, 1, 1000);
351
352 let children1 = pool.get_orphans_by_prev(&OutPoint::new(parent_txid, 0));
353 assert_eq!(children1.len(), 1);
354 assert_eq!(children1[0], txid1);
355
356 let children2 = pool.get_orphans_by_prev(&OutPoint::new(parent_txid, 1));
357 assert_eq!(children2.len(), 1);
358 assert_eq!(children2[0], txid2);
359 }
360
361 #[test]
362 fn test_get_children_of() {
363 let mut pool = OrphanPool::new();
364 let parent_txid = make_txid(0x01);
365
366 let tx1 = make_tx(parent_txid, 0, 3000);
367 let tx2 = make_tx(parent_txid, 1, 2000);
368
369 pool.add_orphan(tx1, 1, 1000);
370 pool.add_orphan(tx2, 1, 1000);
371
372 let children = pool.get_children_of(&parent_txid, 2);
373 assert_eq!(children.len(), 2);
374 }
375
376 #[test]
377 fn test_expire_old_orphans() {
378 let mut pool = OrphanPool::with_config(100, 600); let tx1 = make_tx(make_txid(0x01), 0, 3000);
380 let tx2 = make_tx(make_txid(0x02), 0, 2000);
381
382 pool.add_orphan(tx1, 1, 1000);
383 pool.add_orphan(tx2, 1, 1500);
384
385 let removed = pool.expire_old_orphans(1500);
387 assert_eq!(removed, 0);
388 assert_eq!(pool.len(), 2);
389
390 let removed = pool.expire_old_orphans(1700);
392 assert_eq!(removed, 1);
393 assert_eq!(pool.len(), 1);
394 }
395
396 #[test]
397 fn test_eviction_when_full() {
398 let mut pool = OrphanPool::with_config(3, 600);
399
400 let tx1 = make_tx(make_txid(0x01), 0, 1000);
401 let tx2 = make_tx(make_txid(0x02), 0, 2000);
402 let tx3 = make_tx(make_txid(0x03), 0, 3000);
403 let tx4 = make_tx(make_txid(0x04), 0, 4000);
404
405 pool.add_orphan(tx1, 1, 100);
406 pool.add_orphan(tx2, 1, 200);
407 pool.add_orphan(tx3, 1, 300);
408 assert_eq!(pool.len(), 3);
409
410 let result = pool.add_orphan(tx4, 1, 400);
412 match result {
413 AddOrphanResult::AddedAfterEviction { .. } => {}
414 _ => panic!("Expected AddedAfterEviction"),
415 }
416 assert_eq!(pool.len(), 3); }
418
419 #[test]
420 fn test_remove_for_peer() {
421 let mut pool = OrphanPool::new();
422 let tx1 = make_tx(make_txid(0x01), 0, 1000);
423 let tx2 = make_tx(make_txid(0x02), 0, 2000);
424 let tx3 = make_tx(make_txid(0x03), 0, 3000);
425
426 pool.add_orphan(tx1, 1, 100); pool.add_orphan(tx2, 2, 200); pool.add_orphan(tx3, 1, 300); let removed = pool.remove_for_peer(1);
431 assert_eq!(removed, 2);
432 assert_eq!(pool.len(), 1);
433 }
434
435 #[test]
436 fn test_clear() {
437 let mut pool = OrphanPool::new();
438 pool.add_orphan(make_tx(make_txid(0x01), 0, 1000), 1, 100);
439 pool.add_orphan(make_tx(make_txid(0x02), 0, 2000), 1, 200);
440 assert_eq!(pool.len(), 2);
441
442 pool.clear();
443 assert_eq!(pool.len(), 0);
444 assert!(pool.is_empty());
445 }
446
447 #[test]
448 fn test_reverse_index_cleanup() {
449 let mut pool = OrphanPool::new();
450 let parent_txid = make_txid(0x01);
451 let tx = make_tx(parent_txid, 0, 5000);
452 let txid = tx.txid();
453
454 pool.add_orphan(tx, 1, 1000);
455 assert!(!pool
456 .get_orphans_by_prev(&OutPoint::new(parent_txid, 0))
457 .is_empty());
458
459 let _ = pool.remove_orphan(&txid);
460 assert!(pool
461 .get_orphans_by_prev(&OutPoint::new(parent_txid, 0))
462 .is_empty());
463 }
464
465 #[test]
466 fn test_default_trait() {
467 let pool = OrphanPool::default();
468 assert_eq!(pool.len(), 0);
469 }
470}