mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-05-19 22:34:46 -07:00
global: sigops
This commit is contained in:
@@ -105,7 +105,7 @@ impl Query {
|
||||
/// metadata (sorted by tx_index).
|
||||
/// Phase 2: resolve each prevout's script_pubkey (sorted by
|
||||
/// output_type, then type_index, for sequential addr-vec reads).
|
||||
/// Phase 3: assemble `Transaction` objects, compute sigops + fees.
|
||||
/// Phase 3: assemble `Transaction` objects, compute fees.
|
||||
///
|
||||
/// The final `unwrap` is provably safe: `order` is a permutation of
|
||||
/// `0..len`, Phase 1 produces exactly one `DecodedTx` per position, and
|
||||
@@ -129,6 +129,7 @@ impl Query {
|
||||
|
||||
let mut txid_cursor = indexer.vecs.transactions.txid.cursor();
|
||||
let mut total_size_cursor = indexer.vecs.transactions.total_size.cursor();
|
||||
let mut sigops_cursor = indexer.vecs.transactions.total_sigop_cost.cursor();
|
||||
let mut first_txin_cursor = indexer.vecs.transactions.first_txin_index.cursor();
|
||||
let mut position_cursor = indexer.vecs.transactions.position.cursor();
|
||||
|
||||
@@ -137,6 +138,7 @@ impl Query {
|
||||
tx_index: TxIndex,
|
||||
txid: Txid,
|
||||
total_size: StoredU32,
|
||||
total_sigop_cost: SigOps,
|
||||
status: TxStatus,
|
||||
decoded: bitcoin::Transaction,
|
||||
first_txin_index: TxInIndex,
|
||||
@@ -154,6 +156,7 @@ impl Query {
|
||||
|
||||
let txid: Txid = txid_cursor.get(idx).data()?;
|
||||
let total_size: StoredU32 = total_size_cursor.get(idx).data()?;
|
||||
let total_sigop_cost: SigOps = sigops_cursor.get(idx).data()?;
|
||||
let first_txin_index: TxInIndex = first_txin_cursor.get(idx).data()?;
|
||||
let position: BlkPosition = position_cursor.get(idx).data()?;
|
||||
|
||||
@@ -179,6 +182,7 @@ impl Query {
|
||||
tx_index,
|
||||
txid,
|
||||
total_size,
|
||||
total_sigop_cost,
|
||||
status,
|
||||
decoded,
|
||||
first_txin_index,
|
||||
@@ -277,25 +281,6 @@ impl Query {
|
||||
|
||||
let weight = Weight::from(dtx.decoded.weight());
|
||||
|
||||
// O(n) sigop cost via FxHashMap instead of O(n²) linear scan
|
||||
let outpoint_to_idx: FxHashMap<bitcoin::OutPoint, usize> = dtx
|
||||
.decoded
|
||||
.input
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(j, txin)| (txin.previous_output, j))
|
||||
.collect();
|
||||
|
||||
let total_sigop_cost = SigOps::of_bitcoin_tx(&dtx.decoded, |outpoint| {
|
||||
outpoint_to_idx
|
||||
.get(outpoint)
|
||||
.and_then(|&j| input[j].prevout.as_ref())
|
||||
.map(|p| bitcoin::TxOut {
|
||||
value: bitcoin::Amount::from_sat(u64::from(p.value)),
|
||||
script_pubkey: p.script_pubkey.clone(),
|
||||
})
|
||||
});
|
||||
|
||||
let output: Vec<TxOut> = dtx.decoded.output.into_iter().map(TxOut::from).collect();
|
||||
|
||||
let mut transaction = Transaction {
|
||||
@@ -305,7 +290,7 @@ impl Query {
|
||||
lock_time: RawLockTime::from(dtx.decoded.lock_time),
|
||||
total_size: *dtx.total_size as usize,
|
||||
weight,
|
||||
total_sigop_cost,
|
||||
total_sigop_cost: dtx.total_sigop_cost,
|
||||
fee: Sats::ZERO,
|
||||
input,
|
||||
output,
|
||||
|
||||
@@ -9,14 +9,10 @@
|
||||
//! — the same wire converter the mempool path uses, so both produce
|
||||
//! identical `CpfpInfo` shapes.
|
||||
|
||||
use std::io::Cursor;
|
||||
|
||||
use bitcoin::consensus::Decodable;
|
||||
use brk_error::{Error, OptionData, Result};
|
||||
use brk_mempool::cluster::{Cluster, ClusterNode, LocalIdx};
|
||||
use brk_types::{
|
||||
CpfpInfo, FeeRate, Height, OutPoint, OutputType, Sats, SigOps, TxIndex, TxInIndex, TypeIndex,
|
||||
Txid, TxidPrefix, VSize, Weight,
|
||||
CpfpInfo, FeeRate, Height, TxIndex, TxInIndex, Txid, TxidPrefix, VSize, Weight,
|
||||
};
|
||||
use rustc_hash::{FxBuildHasher, FxHashMap};
|
||||
use smallvec::SmallVec;
|
||||
@@ -106,78 +102,19 @@ impl Query {
|
||||
/// the result so `effectiveFeePerVsize` matches the live path's
|
||||
/// chunk-rate semantics.
|
||||
fn confirmed_cpfp(&self, txid: &Txid) -> Result<CpfpInfo> {
|
||||
let seed = self.resolve_tx_index(txid)?;
|
||||
let height = self.confirmed_status_height(seed)?;
|
||||
let (cluster, seed_local) = self.build_confirmed_cluster(seed, height)?;
|
||||
let sigops = self.seed_sigop_cost(seed)?;
|
||||
let tx_index = self.resolve_tx_index(txid)?;
|
||||
let height = self.confirmed_status_height(tx_index)?;
|
||||
let (cluster, seed_local) = self.build_confirmed_cluster(tx_index, height)?;
|
||||
let sigops = self
|
||||
.indexer()
|
||||
.vecs
|
||||
.transactions
|
||||
.total_sigop_cost
|
||||
.collect_one(tx_index)
|
||||
.data()?;
|
||||
Ok(cluster.to_cpfp_info(seed_local, sigops))
|
||||
}
|
||||
|
||||
/// BIP-141 sigop cost for a single confirmed tx, computed on demand:
|
||||
/// re-decode the raw tx, rebuild its prevout map from `inputs.*` +
|
||||
/// addr vecs, then defer the actual count to `SigOps::of_bitcoin_tx`.
|
||||
/// Cost is one BLK read plus `n_inputs` cursor hops, so a few hundred
|
||||
/// microseconds per CPFP request.
|
||||
fn seed_sigop_cost(&self, tx_index: TxIndex) -> Result<SigOps> {
|
||||
let indexer = self.indexer();
|
||||
let total_size = indexer
|
||||
.vecs
|
||||
.transactions
|
||||
.total_size
|
||||
.collect_one(tx_index)
|
||||
.data()?;
|
||||
let position = indexer
|
||||
.vecs
|
||||
.transactions
|
||||
.position
|
||||
.collect_one(tx_index)
|
||||
.data()?;
|
||||
let buffer = self.reader().read_raw_bytes(position, *total_size as usize)?;
|
||||
let decoded = bitcoin::Transaction::consensus_decode(&mut Cursor::new(buffer))
|
||||
.map_err(|_| Error::Parse("Failed to decode transaction".into()))?;
|
||||
|
||||
let first_txin = indexer
|
||||
.vecs
|
||||
.transactions
|
||||
.first_txin_index
|
||||
.collect_one(tx_index)
|
||||
.data()?;
|
||||
let start = usize::from(first_txin);
|
||||
let count = decoded.input.len();
|
||||
|
||||
let mut outpoint_cursor = indexer.vecs.inputs.outpoint.cursor();
|
||||
let mut output_type_cursor = indexer.vecs.inputs.output_type.cursor();
|
||||
let mut type_index_cursor = indexer.vecs.inputs.type_index.cursor();
|
||||
let mut value_cursor = self.computer().inputs.spent.value.cursor();
|
||||
|
||||
let addr_readers = indexer.vecs.addrs.addr_readers();
|
||||
|
||||
let mut prevout_map: FxHashMap<bitcoin::OutPoint, bitcoin::TxOut> =
|
||||
FxHashMap::with_capacity_and_hasher(count, FxBuildHasher);
|
||||
|
||||
for (j, txin) in decoded.input.iter().enumerate() {
|
||||
let op: OutPoint = outpoint_cursor.get(start + j).data()?;
|
||||
if op.is_coinbase() {
|
||||
continue;
|
||||
}
|
||||
let ot: OutputType = output_type_cursor.get(start + j).data()?;
|
||||
let ti: TypeIndex = type_index_cursor.get(start + j).data()?;
|
||||
let val: Sats = value_cursor.get(start + j).data()?;
|
||||
let script_pubkey = addr_readers.script_pubkey(ot, ti);
|
||||
prevout_map.insert(
|
||||
txin.previous_output,
|
||||
bitcoin::TxOut {
|
||||
value: bitcoin::Amount::from_sat(u64::from(val)),
|
||||
script_pubkey,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Ok(SigOps::of_bitcoin_tx(&decoded, |outpoint| {
|
||||
prevout_map.get(outpoint).cloned()
|
||||
}))
|
||||
}
|
||||
|
||||
/// Walk the seed's same-block parent/child edges, materialize each
|
||||
/// member's `(txid, weight, fee)` from indexer/computer cursors,
|
||||
/// and build a `Cluster<TxIndex>`. The seed's `LocalIdx` comes
|
||||
|
||||
Reference in New Issue
Block a user