global: sigops

This commit is contained in:
nym21
2026-05-04 19:06:41 +02:00
parent dc32bd480f
commit abcb238022
13 changed files with 167 additions and 110 deletions

View File

@@ -25,6 +25,7 @@ serde = { workspace = true }
tracing = { workspace = true }
rayon = { workspace = true }
rustc-hash = { workspace = true }
smallvec = { workspace = true }
vecdb = { workspace = true }
[dev-dependencies]

View File

@@ -309,10 +309,13 @@ impl Indexer {
processor.check_txid_collisions(&txs)?;
let sigops = processor.compute_sigops(&txins);
processor.finalize_and_store_metadata(
txs,
txouts,
txins,
sigops,
&buffers.same_block_spent,
&mut buffers.already_added_addrs,
&mut buffers.same_block_output_info,

View File

@@ -1,4 +1,5 @@
mod metadata;
mod sigops;
mod tx;
mod txin;
mod txout;
@@ -8,7 +9,9 @@ pub use types::*;
use brk_cohort::ByAddrType;
use brk_error::Result;
use brk_types::{AddrHash, Block, Height, OutPoint, TxInIndex, TxIndex, TxOutIndex, TypeIndex};
use brk_types::{
AddrHash, Block, Height, OutPoint, SigOps, TxInIndex, TxIndex, TxOutIndex, TypeIndex,
};
use rustc_hash::{FxHashMap, FxHashSet};
use crate::{Indexes, Readers, Stores, Vecs};
@@ -39,6 +42,7 @@ impl BlockProcessor<'_> {
txs: Vec<ComputedTx>,
txouts: Vec<ProcessedOutput>,
txins: Vec<(TxInIndex, InputSource)>,
sigops: Vec<SigOps>,
same_block_spent_outpoints: &FxHashSet<OutPoint>,
already_added: &mut ByAddrType<FxHashMap<AddrHash, TypeIndex>>,
same_block_info: &mut FxHashMap<OutPoint, SameBlockOutputInfo>,
@@ -84,7 +88,7 @@ impl BlockProcessor<'_> {
same_block_info,
)
},
|| tx::store_tx_metadata(txs, txid_prefix_store, &mut tx_metadata),
|| tx::store_tx_metadata(txs, sigops, txid_prefix_store, &mut tx_metadata),
);
finalize_result?;

View File

@@ -0,0 +1,58 @@
use brk_types::{OutputType, SigOps, TxInIndex};
use rayon::prelude::*;
use smallvec::SmallVec;
use super::{BlockProcessor, InputSource};
impl BlockProcessor<'_> {
/// BIP-141 sigop cost per tx in the block. Uses each input's prevout
/// `OutputType` (already resolved by `process_inputs` for the
/// previous-block case, looked up from `block.txdata` for the
/// same-block case) to feed canonical-shaped synthetic prevouts into
/// `bitcoin::Transaction::total_sigop_cost`.
pub fn compute_sigops(&self, txins: &[(TxInIndex, InputSource)]) -> Vec<SigOps> {
let txdata = &self.block.txdata;
let base_tx_index = u32::from(self.indexes.tx_index);
let mut tx_input_offsets = Vec::with_capacity(txdata.len());
let mut offset = 0usize;
for tx in txdata {
tx_input_offsets.push(offset);
offset += tx.input.len();
}
txdata
.par_iter()
.enumerate()
.map(|(i, tx)| {
if tx.is_coinbase() {
return SigOps::ZERO;
}
let start = tx_input_offsets[i];
let tx_inputs = &txins[start..start + tx.input.len()];
let kinds: SmallVec<[(bitcoin::OutPoint, OutputType); 4]> = tx
.input
.iter()
.zip(tx_inputs.iter())
.map(|(txin, (_, source))| {
let kind = match source {
InputSource::PreviousBlock { output_type, .. } => *output_type,
InputSource::SameBlock { outpoint, .. } => {
let local =
(u32::from(outpoint.tx_index()) - base_tx_index) as usize;
let vout = u32::from(outpoint.vout()) as usize;
OutputType::from(&txdata[local].output[vout].script_pubkey)
}
};
(txin.previous_output, kind)
})
.collect();
SigOps::of_bitcoin_tx_with_kinds(tx, |op| {
kinds.iter().find(|(o, _)| o == op).map(|(_, k)| *k)
})
})
.collect()
}
}

View File

@@ -1,6 +1,6 @@
use brk_error::{Error, Result};
use brk_store::Store;
use brk_types::{StoredBool, TxIndex, Txid, TxidPrefix};
use brk_types::{SigOps, StoredBool, TxIndex, Txid, TxidPrefix};
use rayon::prelude::*;
use tracing::error;
use vecdb::{AnyVec, WritableVec, likely};
@@ -90,10 +90,12 @@ impl<'a> BlockProcessor<'a> {
pub(super) fn store_tx_metadata(
txs: Vec<ComputedTx>,
sigops: Vec<SigOps>,
store: &mut Store<TxidPrefix, TxIndex>,
md: &mut TxMetadataVecs<'_>,
) -> Result<()> {
for ct in txs {
debug_assert_eq!(txs.len(), sigops.len());
for (ct, sigops) in txs.into_iter().zip(sigops) {
if ct.prev_tx_index_opt.is_none() {
store.insert(ct.txid_prefix, ct.tx_index);
}
@@ -106,6 +108,7 @@ pub(super) fn store_tx_metadata(
.checked_push(ct.tx_index, ct.base_size.into())?;
md.total_size
.checked_push(ct.tx_index, ct.total_size.into())?;
md.total_sigop_cost.checked_push(ct.tx_index, sigops)?;
md.is_explicitly_rbf
.checked_push(ct.tx_index, StoredBool::from(ct.tx.is_explicitly_rbf()))?;
}

View File

@@ -1,8 +1,8 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{
BlkPosition, Height, RawLockTime, StoredBool, StoredU32, TxInIndex, TxIndex, TxOutIndex,
TxVersion, Txid, Version,
BlkPosition, Height, RawLockTime, SigOps, StoredBool, StoredU32, TxInIndex, TxIndex,
TxOutIndex, TxVersion, Txid, Version,
};
use rayon::prelude::*;
use vecdb::{
@@ -19,6 +19,7 @@ pub struct TransactionsVecs<M: StorageMode = Rw> {
pub raw_locktime: M::Stored<PcoVec<TxIndex, RawLockTime>>,
pub base_size: M::Stored<PcoVec<TxIndex, StoredU32>>,
pub total_size: M::Stored<PcoVec<TxIndex, StoredU32>>,
pub total_sigop_cost: M::Stored<PcoVec<TxIndex, SigOps>>,
pub is_explicitly_rbf: M::Stored<PcoVec<TxIndex, StoredBool>>,
pub first_txin_index: M::Stored<PcoVec<TxIndex, TxInIndex>>,
pub first_txout_index: M::Stored<BytesVec<TxIndex, TxOutIndex>>,
@@ -32,6 +33,7 @@ pub struct TxMetadataVecs<'a> {
pub raw_locktime: &'a mut PcoVec<TxIndex, RawLockTime>,
pub base_size: &'a mut PcoVec<TxIndex, StoredU32>,
pub total_size: &'a mut PcoVec<TxIndex, StoredU32>,
pub total_sigop_cost: &'a mut PcoVec<TxIndex, SigOps>,
pub is_explicitly_rbf: &'a mut PcoVec<TxIndex, StoredBool>,
}
@@ -52,6 +54,7 @@ impl TransactionsVecs {
raw_locktime: &mut self.raw_locktime,
base_size: &mut self.base_size,
total_size: &mut self.total_size,
total_sigop_cost: &mut self.total_sigop_cost,
is_explicitly_rbf: &mut self.is_explicitly_rbf,
},
)
@@ -65,6 +68,7 @@ impl TransactionsVecs {
raw_locktime,
base_size,
total_size,
total_sigop_cost,
is_explicitly_rbf,
first_txin_index,
first_txout_index,
@@ -76,6 +80,7 @@ impl TransactionsVecs {
raw_locktime = PcoVec::forced_import(db, "raw_locktime", version),
base_size = PcoVec::forced_import(db, "base_size", version),
total_size = PcoVec::forced_import(db, "total_size", version),
total_sigop_cost = PcoVec::forced_import(db, "total_sigop_cost", version),
is_explicitly_rbf = PcoVec::forced_import(db, "is_explicitly_rbf", version),
first_txin_index = PcoVec::forced_import(db, "first_txin_index", version),
first_txout_index = BytesVec::forced_import(db, "first_txout_index", version),
@@ -88,6 +93,7 @@ impl TransactionsVecs {
raw_locktime,
base_size,
total_size,
total_sigop_cost,
is_explicitly_rbf,
first_txin_index,
first_txout_index,
@@ -107,6 +113,8 @@ impl TransactionsVecs {
.truncate_if_needed_with_stamp(tx_index, stamp)?;
self.total_size
.truncate_if_needed_with_stamp(tx_index, stamp)?;
self.total_sigop_cost
.truncate_if_needed_with_stamp(tx_index, stamp)?;
self.is_explicitly_rbf
.truncate_if_needed_with_stamp(tx_index, stamp)?;
self.first_txin_index
@@ -126,6 +134,7 @@ impl TransactionsVecs {
&mut self.raw_locktime,
&mut self.base_size,
&mut self.total_size,
&mut self.total_sigop_cost,
&mut self.is_explicitly_rbf,
&mut self.first_txin_index,
&mut self.first_txout_index,