@@ -35,6 +35,8 @@ pub struct Mempool {
3535 config : Arc < Config > ,
3636 txstore : HashMap < Txid , Transaction > ,
3737 feeinfo : HashMap < Txid , TxFeeInfo > ,
38+ // Map txid -> scripthashes touched, to prune efficiently on eviction.
39+ tx_scripthashes : HashMap < Txid , Vec < FullHash > > ,
3840 history : HashMap < FullHash , Vec < TxHistoryInfo > > , // ScriptHash -> {history_entries}
3941 edges : HashMap < OutPoint , ( Txid , u32 ) > , // OutPoint -> (spending_txid, spending_vin)
4042 recent : ArrayDeque < TxOverview , RECENT_TXS_SIZE , Wrapping > , // The N most recent txs to enter the mempool
@@ -71,6 +73,7 @@ impl Mempool {
7173 config,
7274 txstore : HashMap :: new ( ) ,
7375 feeinfo : HashMap :: new ( ) ,
76+ tx_scripthashes : HashMap :: new ( ) ,
7477 history : HashMap :: new ( ) ,
7578 edges : HashMap :: new ( ) ,
7679 recent : ArrayDeque :: new ( ) ,
@@ -300,7 +303,8 @@ impl Mempool {
300303 . latency
301304 . with_label_values ( & [ "update_backlog_stats" ] )
302305 . start_timer ( ) ;
303- self . backlog_stats = ( BacklogStats :: new ( & self . feeinfo ) , Instant :: now ( ) ) ;
306+ let feeinfo: Vec < & TxFeeInfo > = self . feeinfo . values ( ) . collect ( ) ;
307+ self . backlog_stats = ( BacklogStats :: from_feeinfo_slice ( & feeinfo) , Instant :: now ( ) ) ;
304308 }
305309
306310 #[ trace]
@@ -354,6 +358,7 @@ impl Mempool {
354358
355359 let prevouts = extract_tx_prevouts ( & tx, & txos, false ) ;
356360 let txid_bytes = full_hash ( & txid[ ..] ) ;
361+ let mut tx_scripthashes = Vec :: with_capacity ( tx. input . len ( ) + tx. output . len ( ) ) ; // best-effort capacity hint
357362
358363 // Get feeinfo for caching and recent tx overview
359364 let feeinfo = TxFeeInfo :: new ( & tx, & prevouts, self . config . network_type ) ;
@@ -410,11 +415,15 @@ impl Mempool {
410415
411416 // Index funding/spending history entries and spend edges
412417 for ( scripthash, entry) in funding. chain ( spending) {
418+ tx_scripthashes. push ( scripthash) ;
413419 self . history
414420 . entry ( scripthash)
415421 . or_insert_with ( Vec :: new)
416422 . push ( entry) ;
417423 }
424+ tx_scripthashes. sort_unstable ( ) ;
425+ tx_scripthashes. dedup ( ) ;
426+ self . tx_scripthashes . insert ( txid, tx_scripthashes) ;
418427 for ( i, txi) in tx. input . iter ( ) . enumerate ( ) {
419428 self . edges . insert ( txi. previous_output , ( txid, i as u32 ) ) ;
420429 }
@@ -469,31 +478,38 @@ impl Mempool {
469478 let _timer = self . latency . with_label_values ( & [ "remove" ] ) . start_timer ( ) ;
470479
471480 for txid in & to_remove {
472- self . txstore
481+ let tx = self
482+ . txstore
473483 . remove ( * txid)
474484 . unwrap_or_else ( || panic ! ( "missing mempool tx {}" , txid) ) ;
475485
476- self . feeinfo . remove ( * txid) . or_else ( || {
477- warn ! ( "missing mempool tx feeinfo {}" , txid) ;
478- None
486+ self . feeinfo . remove ( * txid) . unwrap_or_else ( || {
487+ panic ! ( "missing mempool tx feeinfo {}" , txid) ;
479488 } ) ;
480- }
481489
482- // TODO: make it more efficient (currently it takes O(|mempool|) time)
483- self . history . retain ( |_scripthash, entries| {
484- entries. retain ( |entry| !to_remove. contains ( & entry. get_txid ( ) ) ) ;
485- !entries. is_empty ( )
486- } ) ;
490+ let scripthashes = self
491+ . tx_scripthashes
492+ . remove ( * txid)
493+ . unwrap_or_else ( || panic ! ( "missing tx_scripthashes for {}" , txid) ) ;
494+ prune_history_entries ( & mut self . history , & scripthashes, txid) ;
495+
496+ for txin in tx. input {
497+ assert ! (
498+ self . edges. remove( & txin. previous_output) . is_some( ) ,
499+ "missing mempool edge for outpoint {}:{} (tx {})" ,
500+ txin. previous_output. txid,
501+ txin. previous_output. vout,
502+ txid
503+ ) ;
504+ }
505+ }
487506
488507 #[ cfg( feature = "liquid" ) ]
489508 asset:: remove_mempool_tx_assets (
490509 & to_remove,
491510 & mut self . asset_history ,
492511 & mut self . asset_issuance ,
493512 ) ;
494-
495- self . edges
496- . retain ( |_outpoint, ( txid, _vin) | !to_remove. contains ( txid) ) ;
497513 }
498514
499515 #[ cfg( feature = "liquid" ) ]
@@ -637,6 +653,32 @@ impl Mempool {
637653 }
638654}
639655
656+ fn prune_history_entries (
657+ history : & mut HashMap < FullHash , Vec < TxHistoryInfo > > ,
658+ scripthashes : & [ FullHash ] ,
659+ txid : & Txid ,
660+ ) {
661+ for scripthash in scripthashes {
662+ let entries = history
663+ . get_mut ( scripthash)
664+ . unwrap_or_else ( || panic ! ( "missing history bucket for {:?}" , scripthash) ) ;
665+
666+ let before = entries. len ( ) ;
667+ entries. retain ( |entry| entry. get_txid ( ) != * txid) ;
668+ let removed = before - entries. len ( ) ;
669+ assert ! (
670+ removed > 0 ,
671+ "tx {} not found in history bucket {:?}" ,
672+ txid,
673+ scripthash
674+ ) ;
675+
676+ if entries. is_empty ( ) {
677+ history. remove ( scripthash) ;
678+ }
679+ }
680+ }
681+
640682#[ derive( Serialize ) ]
641683pub struct BacklogStats {
642684 pub count : u32 ,
@@ -656,18 +698,17 @@ impl BacklogStats {
656698 }
657699
658700 #[ trace]
659- fn new ( feeinfo : & HashMap < Txid , TxFeeInfo > ) -> Self {
660- let ( count, vsize, total_fee) = feeinfo
661- . values ( )
662- . fold ( ( 0 , 0 , 0 ) , |( count, vsize, fee) , feeinfo| {
701+ fn from_feeinfo_slice ( fees : & [ & TxFeeInfo ] ) -> Self {
702+ let ( count, vsize, total_fee) =
703+ fees. iter ( ) . fold ( ( 0 , 0 , 0 ) , |( count, vsize, fee) , feeinfo| {
663704 ( count + 1 , vsize + feeinfo. vsize , fee + feeinfo. fee )
664705 } ) ;
665706
666707 BacklogStats {
667708 count,
668709 vsize,
669710 total_fee,
670- fee_histogram : make_fee_histogram ( feeinfo . values ( ) . collect ( ) ) ,
711+ fee_histogram : make_fee_histogram ( fees . iter ( ) . copied ( ) . collect ( ) ) ,
671712 }
672713 }
673714}
0 commit comments