crates: snapshot

This commit is contained in:
nym21
2026-05-12 22:33:09 +02:00
parent 8fc2e71492
commit 5cc3fbfa6e
25 changed files with 450 additions and 362 deletions
+16 -14
View File
@@ -32,11 +32,12 @@ use std::{
};
use brk_error::Result;
use brk_oracle::Histogram;
use brk_rpc::Client;
use brk_types::{
AddrBytes, AddrMempoolStats, BlockTemplate, BlockTemplateDiff, FeeRate, MempoolBlock,
MempoolInfo, MempoolRecentTx, NextBlockHash, OutpointPrefix, OutputType, Sats, Timestamp,
Transaction, TxOut, Txid, TxidPrefix, Vin, Vout,
MempoolInfo, MempoolRecentTx, NextBlockHash, OutpointPrefix, Timestamp, Transaction, TxOut,
Txid, TxidPrefix, Vin, Vout,
};
use parking_lot::{RwLock, RwLockReadGuard};
use rustc_hash::{FxHashMap, FxHashSet};
@@ -236,19 +237,20 @@ impl Mempool {
.collect()
}
/// Apply `f` to an iterator over `(value, output_type)` for every output
/// of every live mempool tx. The lock is held for the duration of the call.
pub fn process_live_outputs<R>(
&self,
f: impl FnOnce(&mut dyn Iterator<Item = (Sats, OutputType)>) -> R,
) -> R {
/// Histogram of pre-bucketed oracle bins across all live mempool tx
/// outputs. Bins are computed once on insert (see `OutputBins`), so this
/// hot path is `O(eligible outputs)` of integer increments. Used by
/// `live_price` to blend the mempool into the committed oracle without
/// re-parsing scripts per request.
pub fn live_histogram(&self) -> Histogram {
let mut hist = Histogram::zeros();
let state = self.read();
let mut iter = state
.txs
.values()
.flat_map(|tx| &tx.output)
.map(|txout| (txout.value, txout.type_()));
f(&mut iter)
for (_, record) in state.txs.records() {
for bin in record.output_bins.iter() {
hist.increment(bin as usize);
}
}
hist
}
/// Effective fee rate for a live tx: snapshot's linearized chunk
+2
View File
@@ -5,10 +5,12 @@
pub(crate) mod addr_tracker;
pub(crate) mod outpoint_spends;
pub(crate) mod output_bins;
pub(crate) mod tx_graveyard;
pub(crate) mod tx_store;
pub(crate) use addr_tracker::AddrTracker;
pub(crate) use outpoint_spends::OutpointSpends;
pub(crate) use output_bins::OutputBins;
pub(crate) use tx_graveyard::{TxGraveyard, TxTombstone};
pub(crate) use tx_store::TxStore;
@@ -0,0 +1,23 @@
use brk_oracle::default_eligible_bin;
use brk_types::Transaction;
use smallvec::SmallVec;
/// Pre-bucketed oracle bins for a tx's eligible outputs. Computed once on
/// insert so `Mempool::live_histogram` can bin all live outputs without
/// re-parsing scripts or recomputing eligibility per request.
pub struct OutputBins(SmallVec<[u16; 4]>);
impl OutputBins {
pub fn from_tx(tx: &Transaction) -> Self {
Self(
tx.output
.iter()
.filter_map(|o| default_eligible_bin(o.value, o.type_()))
.collect(),
)
}
pub fn iter(&self) -> impl Iterator<Item = u16> + '_ {
self.0.iter().copied()
}
}
+17 -8
View File
@@ -1,15 +1,28 @@
use brk_types::{MempoolRecentTx, Transaction, TxOut, Txid, TxidPrefix, Vin};
use rustc_hash::{FxHashMap, FxHashSet};
use crate::TxEntry;
use crate::{TxEntry, stores::OutputBins};
const RECENT_CAP: usize = 10;
/// Per-tx record: live tx body and its mempool entry, kept under one
/// key so a single map probe returns both.
/// Per-tx record: live tx body, its mempool entry, and the pre-bucketed
/// oracle bins for its outputs. Kept under one key so a single map probe
/// returns everything readers need.
pub struct TxRecord {
pub tx: Transaction,
pub entry: TxEntry,
pub output_bins: OutputBins,
}
impl TxRecord {
pub fn new(tx: Transaction, entry: TxEntry) -> Self {
let output_bins = OutputBins::from_tx(&tx);
Self {
tx,
entry,
output_bins,
}
}
}
/// Live-pool index keyed by `TxidPrefix`. The full `Txid` lives in
@@ -63,10 +76,6 @@ impl TxStore {
self.records.values().map(|r| &r.entry.txid)
}
pub fn values(&self) -> impl Iterator<Item = &Transaction> {
self.records.values().map(|r| &r.tx)
}
pub fn insert(&mut self, tx: Transaction, entry: TxEntry) {
let prefix = entry.txid_prefix();
debug_assert!(
@@ -77,7 +86,7 @@ impl TxStore {
if tx.input.iter().any(|i| i.prevout.is_none()) {
self.unresolved.insert(prefix);
}
self.records.insert(prefix, TxRecord { tx, entry });
self.records.insert(prefix, TxRecord::new(tx, entry));
}
fn sample_recent(&mut self, txid: &Txid, tx: &Transaction) {