mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-06-17 18:29:44 -07:00
255 lines
9.2 KiB
Rust
255 lines
9.2 KiB
Rust
use brk_error::Result;
|
|
use brk_types::{
|
|
EmptyOutputIndex, Height, OpReturnIndex, OutputType, P2AAddrIndex, P2MSOutputIndex,
|
|
P2PK33AddrIndex, P2PK65AddrIndex, P2PKHAddrIndex, P2SHAddrIndex, P2TRAddrIndex,
|
|
P2WPKHAddrIndex, P2WSHAddrIndex, TxInIndex, TxIndex, TxOutIndex, TypeIndex, UnknownOutputIndex,
|
|
};
|
|
use tracing::info;
|
|
use vecdb::{AnyStoredVec, PcoVec, PcoVecValue, ReadableVec, VecIndex, VecValue, WritableVec};
|
|
|
|
use crate::{Stores, Vecs};
|
|
|
|
/// Pipeline-wide length/count snapshot. Lengths semantics:
|
|
/// `bound.f = N` means positions `0..N` are fully written; readers
|
|
/// reject `pos >= bound.f`.
|
|
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
|
pub struct Lengths {
|
|
pub empty_output_index: EmptyOutputIndex,
|
|
pub height: Height,
|
|
pub op_return_index: OpReturnIndex,
|
|
pub p2ms_output_index: P2MSOutputIndex,
|
|
pub p2pk33_addr_index: P2PK33AddrIndex,
|
|
pub p2pk65_addr_index: P2PK65AddrIndex,
|
|
pub p2pkh_addr_index: P2PKHAddrIndex,
|
|
pub p2sh_addr_index: P2SHAddrIndex,
|
|
pub p2tr_addr_index: P2TRAddrIndex,
|
|
pub p2wpkh_addr_index: P2WPKHAddrIndex,
|
|
pub p2wsh_addr_index: P2WSHAddrIndex,
|
|
pub p2a_addr_index: P2AAddrIndex,
|
|
pub tx_index: TxIndex,
|
|
pub txin_index: TxInIndex,
|
|
pub txout_index: TxOutIndex,
|
|
pub unknown_output_index: UnknownOutputIndex,
|
|
}
|
|
|
|
impl Lengths {
|
|
pub fn to_type_index(&self, output_type: OutputType) -> TypeIndex {
|
|
match output_type {
|
|
OutputType::Empty => *self.empty_output_index,
|
|
OutputType::OpReturn => *self.op_return_index,
|
|
OutputType::P2A => *self.p2a_addr_index,
|
|
OutputType::P2MS => *self.p2ms_output_index,
|
|
OutputType::P2PK33 => *self.p2pk33_addr_index,
|
|
OutputType::P2PK65 => *self.p2pk65_addr_index,
|
|
OutputType::P2PKH => *self.p2pkh_addr_index,
|
|
OutputType::P2SH => *self.p2sh_addr_index,
|
|
OutputType::P2TR => *self.p2tr_addr_index,
|
|
OutputType::P2WPKH => *self.p2wpkh_addr_index,
|
|
OutputType::P2WSH => *self.p2wsh_addr_index,
|
|
OutputType::Unknown => *self.unknown_output_index,
|
|
}
|
|
}
|
|
|
|
/// Bump per-block totals after processing a block.
|
|
pub fn add_block(&mut self, tx_count: usize, input_count: usize, output_count: usize) {
|
|
self.tx_index += TxIndex::from(tx_count);
|
|
self.txin_index += TxInIndex::from(input_count);
|
|
self.txout_index += TxOutIndex::from(output_count);
|
|
}
|
|
|
|
/// Increments the address index for the given address type and returns the previous value.
|
|
/// Only call this for address types (P2PK65, P2PK33, P2PKH, P2SH, P2WPKH, P2WSH, P2TR, P2A).
|
|
#[inline]
|
|
pub fn increment_addr_index(&mut self, addr_type: OutputType) -> TypeIndex {
|
|
match addr_type {
|
|
OutputType::P2PK65 => self.p2pk65_addr_index.copy_then_increment(),
|
|
OutputType::P2PK33 => self.p2pk33_addr_index.copy_then_increment(),
|
|
OutputType::P2PKH => self.p2pkh_addr_index.copy_then_increment(),
|
|
OutputType::P2SH => self.p2sh_addr_index.copy_then_increment(),
|
|
OutputType::P2WPKH => self.p2wpkh_addr_index.copy_then_increment(),
|
|
OutputType::P2WSH => self.p2wsh_addr_index.copy_then_increment(),
|
|
OutputType::P2TR => self.p2tr_addr_index.copy_then_increment(),
|
|
OutputType::P2A => self.p2a_addr_index.copy_then_increment(),
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
pub fn checked_push(&self, vecs: &mut Vecs) -> Result<()> {
|
|
let height = self.height;
|
|
vecs.transactions
|
|
.first_tx_index
|
|
.checked_push(height, self.tx_index)?;
|
|
vecs.inputs
|
|
.first_txin_index
|
|
.checked_push(height, self.txin_index)?;
|
|
vecs.outputs
|
|
.first_txout_index
|
|
.checked_push(height, self.txout_index)?;
|
|
vecs.scripts
|
|
.empty
|
|
.first_index
|
|
.checked_push(height, self.empty_output_index)?;
|
|
vecs.scripts
|
|
.p2ms
|
|
.first_index
|
|
.checked_push(height, self.p2ms_output_index)?;
|
|
vecs.scripts
|
|
.op_return
|
|
.first_index
|
|
.checked_push(height, self.op_return_index)?;
|
|
vecs.addrs
|
|
.p2a
|
|
.first_index
|
|
.checked_push(height, self.p2a_addr_index)?;
|
|
vecs.scripts
|
|
.unknown
|
|
.first_index
|
|
.checked_push(height, self.unknown_output_index)?;
|
|
vecs.addrs
|
|
.p2pk33
|
|
.first_index
|
|
.checked_push(height, self.p2pk33_addr_index)?;
|
|
vecs.addrs
|
|
.p2pk65
|
|
.first_index
|
|
.checked_push(height, self.p2pk65_addr_index)?;
|
|
vecs.addrs
|
|
.p2pkh
|
|
.first_index
|
|
.checked_push(height, self.p2pkh_addr_index)?;
|
|
vecs.addrs
|
|
.p2sh
|
|
.first_index
|
|
.checked_push(height, self.p2sh_addr_index)?;
|
|
vecs.addrs
|
|
.p2tr
|
|
.first_index
|
|
.checked_push(height, self.p2tr_addr_index)?;
|
|
vecs.addrs
|
|
.p2wpkh
|
|
.first_index
|
|
.checked_push(height, self.p2wpkh_addr_index)?;
|
|
vecs.addrs
|
|
.p2wsh
|
|
.first_index
|
|
.checked_push(height, self.p2wsh_addr_index)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Read current local lengths. `None` pre-genesis.
|
|
pub fn from_local(vecs: &Vecs, stores: &Stores) -> Option<Self> {
|
|
let height = vecs.next_height().min(stores.next_height());
|
|
Self::collect_at(height, vecs)
|
|
}
|
|
|
|
/// Read lengths to resume at `required_height`. Reorg-aware:
|
|
/// - if local is ahead, clamp down to `required_height`;
|
|
/// - if local is behind, return `None` (caller must full-reset).
|
|
pub fn resume_at(required_height: Height, vecs: &Vecs, stores: &Stores) -> Option<Self> {
|
|
let local = vecs.next_height().min(stores.next_height());
|
|
if local < required_height {
|
|
return None;
|
|
}
|
|
let height = if local > required_height {
|
|
info!(
|
|
"Reorg detected: rolling back from {} to {}",
|
|
local, required_height
|
|
);
|
|
required_height
|
|
} else {
|
|
local
|
|
};
|
|
Self::collect_at(height, vecs)
|
|
}
|
|
|
|
fn collect_at(height: Height, vecs: &Vecs) -> Option<Self> {
|
|
Some(Self {
|
|
empty_output_index: next_index(
|
|
&vecs.scripts.empty.first_index,
|
|
&vecs.scripts.empty.to_tx_index,
|
|
height,
|
|
)?,
|
|
height,
|
|
p2ms_output_index: next_index(
|
|
&vecs.scripts.p2ms.first_index,
|
|
&vecs.scripts.p2ms.to_tx_index,
|
|
height,
|
|
)?,
|
|
op_return_index: next_index(
|
|
&vecs.scripts.op_return.first_index,
|
|
&vecs.scripts.op_return.to_tx_index,
|
|
height,
|
|
)?,
|
|
p2pk33_addr_index: next_index(
|
|
&vecs.addrs.p2pk33.first_index,
|
|
&vecs.addrs.p2pk33.bytes,
|
|
height,
|
|
)?,
|
|
p2pk65_addr_index: next_index(
|
|
&vecs.addrs.p2pk65.first_index,
|
|
&vecs.addrs.p2pk65.bytes,
|
|
height,
|
|
)?,
|
|
p2pkh_addr_index: next_index(
|
|
&vecs.addrs.p2pkh.first_index,
|
|
&vecs.addrs.p2pkh.bytes,
|
|
height,
|
|
)?,
|
|
p2sh_addr_index: next_index(
|
|
&vecs.addrs.p2sh.first_index,
|
|
&vecs.addrs.p2sh.bytes,
|
|
height,
|
|
)?,
|
|
p2tr_addr_index: next_index(
|
|
&vecs.addrs.p2tr.first_index,
|
|
&vecs.addrs.p2tr.bytes,
|
|
height,
|
|
)?,
|
|
p2wpkh_addr_index: next_index(
|
|
&vecs.addrs.p2wpkh.first_index,
|
|
&vecs.addrs.p2wpkh.bytes,
|
|
height,
|
|
)?,
|
|
p2wsh_addr_index: next_index(
|
|
&vecs.addrs.p2wsh.first_index,
|
|
&vecs.addrs.p2wsh.bytes,
|
|
height,
|
|
)?,
|
|
p2a_addr_index: next_index(&vecs.addrs.p2a.first_index, &vecs.addrs.p2a.bytes, height)?,
|
|
tx_index: next_index(
|
|
&vecs.transactions.first_tx_index,
|
|
&vecs.transactions.txid,
|
|
height,
|
|
)?,
|
|
txin_index: next_index(&vecs.inputs.first_txin_index, &vecs.inputs.outpoint, height)?,
|
|
txout_index: next_index(&vecs.outputs.first_txout_index, &vecs.outputs.value, height)?,
|
|
unknown_output_index: next_index(
|
|
&vecs.scripts.unknown.first_index,
|
|
&vecs.scripts.unknown.to_tx_index,
|
|
height,
|
|
)?,
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Per-type next-to-write counter at `next_height`. `None` pre-genesis.
|
|
fn next_index<I, T>(
|
|
height_to_index: &PcoVec<Height, I>,
|
|
index_to_else: &impl ReadableVec<I, T>,
|
|
next_height: Height,
|
|
) -> Option<I>
|
|
where
|
|
I: VecIndex + PcoVecValue + From<usize>,
|
|
T: VecValue,
|
|
{
|
|
let h = Height::from(height_to_index.stamp());
|
|
if h.is_zero() {
|
|
None
|
|
} else if h + 1_u32 == next_height {
|
|
Some(I::from(index_to_else.len()))
|
|
} else {
|
|
height_to_index.collect_one(next_height)
|
|
}
|
|
}
|