Skip to content

Commit 884a102

Browse files
committed
fix(mempool): tune for larger mempool size (lowered minrelaytxfee)
1 parent e60ca89 commit 884a102

File tree

2 files changed

+62
-20
lines changed

2 files changed

+62
-20
lines changed

flake.nix

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@
3333

3434
src = craneLib.cleanCargoSource ./.;
3535

36-
nativeBuildInputs = with pkgs; [ rustToolchain clang ]; # required only at build time
36+
# Build-time deps; include libclang so rocksdb-sys/bindgen can find a shared libclang.
37+
nativeBuildInputs = with pkgs; [ rustToolchain clang libclang ];
3738
buildInputs = with pkgs; [ ]; # also required at runtime
3839

3940
envVars =

src/new_index/mempool.rs

Lines changed: 60 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -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)]
641683
pub 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

Comments
 (0)