computer: indexes + rolling

This commit is contained in:
nym21
2026-02-24 17:07:35 +01:00
parent cefc8cfd42
commit f74115c6e2
160 changed files with 2604 additions and 4739 deletions

View File

@@ -2,7 +2,7 @@ use brk_error::Result;
use brk_indexer::Indexer;
use vecdb::Exit;
use crate::{blocks, indexes, inputs, outputs, ComputeIndexes};
use crate::{blocks, indexes, inputs, outputs, prices, ComputeIndexes};
use super::Vecs;
@@ -15,12 +15,13 @@ impl Vecs {
blocks: &blocks::Vecs,
inputs: &inputs::Vecs,
outputs: &outputs::Vecs,
prices: &prices::Vecs,
starting_indexes: &ComputeIndexes,
exit: &Exit,
) -> Result<()> {
// Count computes first
self.count
.compute(indexer, starting_indexes, exit)?;
.compute(indexer, &blocks.count, starting_indexes, exit)?;
// Versions depends on count
self.versions
@@ -30,12 +31,14 @@ impl Vecs {
self.size
.compute(indexer, indexes, starting_indexes, exit)?;
// Fees depends on size
// Fees depends on size, blocks (window starts), prices (USD conversion)
self.fees.compute(
indexer,
indexes,
inputs,
&self.size,
blocks,
prices,
starting_indexes,
exit,
)?;

View File

@@ -3,25 +3,30 @@ use brk_indexer::Indexer;
use vecdb::Exit;
use super::Vecs;
use crate::ComputeIndexes;
use crate::{blocks, ComputeIndexes};
impl Vecs {
pub(crate) fn compute(
&mut self,
indexer: &Indexer,
count_vecs: &blocks::CountVecs,
starting_indexes: &ComputeIndexes,
exit: &Exit,
) -> Result<()> {
self.tx_count
.compute(starting_indexes, exit, |v| {
v.compute_count_from_indexes(
let window_starts = count_vecs.window_starts();
self.tx_count.compute(
starting_indexes.height,
&window_starts,
exit,
|height| {
Ok(height.compute_count_from_indexes(
starting_indexes.height,
&indexer.vecs.transactions.first_txindex,
&indexer.vecs.transactions.txid,
exit,
)?;
Ok(())
})?;
)?)
},
)?;
Ok(())
}

View File

@@ -1,10 +1,10 @@
use brk_error::Result;
use brk_indexer::Indexer;
use brk_types::{StoredBool, TxIndex, Version};
use vecdb::{Database, ReadableCloneableVec, LazyVecFrom2};
use vecdb::{Database, LazyVecFrom2, ReadableCloneableVec};
use super::Vecs;
use crate::{indexes, internal::ComputedFromHeightFull};
use crate::{indexes, internal::ComputedFromHeightCumFull};
impl Vecs {
pub(crate) fn forced_import(
@@ -24,7 +24,9 @@ impl Vecs {
);
Ok(Self {
tx_count: ComputedFromHeightFull::forced_import(db, "tx_count", version, indexes)?,
tx_count: ComputedFromHeightCumFull::forced_import(
db, "tx_count", version, indexes,
)?,
is_coinbase: txindex_to_is_coinbase,
})
}

View File

@@ -2,10 +2,10 @@ use brk_traversable::Traversable;
use brk_types::{Height, StoredBool, StoredU64, TxIndex};
use vecdb::{LazyVecFrom2, Rw, StorageMode};
use crate::internal::ComputedFromHeightFull;
use crate::internal::ComputedFromHeightCumFull;
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
pub tx_count: ComputedFromHeightFull<StoredU64, M>,
pub tx_count: ComputedFromHeightCumFull<StoredU64, M>,
pub is_coinbase: LazyVecFrom2<TxIndex, StoredBool, TxIndex, Height, Height, TxIndex>,
}

View File

@@ -1,11 +1,11 @@
use brk_error::Result;
use brk_indexer::Indexer;
use brk_types::{FeeRate, Sats};
use brk_types::{Bitcoin, FeeRate, Sats};
use vecdb::{Exit, unlikely};
use super::super::size;
use super::Vecs;
use crate::{ComputeIndexes, indexes, inputs};
use crate::{blocks, indexes, inputs, prices, ComputeIndexes};
impl Vecs {
#[allow(clippy::too_many_arguments)]
@@ -15,6 +15,8 @@ impl Vecs {
indexes: &indexes::Vecs,
txins: &inputs::Vecs,
size_vecs: &size::Vecs,
blocks: &blocks::Vecs,
prices: &prices::Vecs,
starting_indexes: &ComputeIndexes,
exit: &Exit,
) -> Result<()> {
@@ -34,7 +36,7 @@ impl Vecs {
exit,
)?;
self.fee.base.compute_transform2(
self.fee_txindex.compute_transform2(
starting_indexes.txindex,
&self.input_value,
&self.output_value,
@@ -49,21 +51,59 @@ impl Vecs {
exit,
)?;
self.fee_rate.txindex.compute_transform2(
self.fee_rate_txindex.compute_transform2(
starting_indexes.txindex,
&self.fee.base,
&self.fee_txindex,
&size_vecs.vsize.txindex,
|(txindex, fee, vsize, ..)| (txindex, FeeRate::from((fee, vsize))),
exit,
)?;
// Skip coinbase (first tx per block) since it has no fee
self.fee
.derive_from_with_skip(indexer, indexes, starting_indexes, exit, 1)?;
self.fee.compute_with_skip(
starting_indexes.height,
&self.fee_txindex,
&indexer.vecs.transactions.first_txindex,
&indexes.height.txindex_count,
exit,
1,
)?;
// Skip coinbase (first tx per block) since it has no feerate
self.fee_rate
.derive_from_with_skip(indexer, indexes, starting_indexes, exit, 1)?;
self.fee_rate.compute_with_skip(
starting_indexes.height,
&self.fee_rate_txindex,
&indexer.vecs.transactions.first_txindex,
&indexes.height.txindex_count,
exit,
1,
)?;
// Compute fee USD sum per block: price * Bitcoin::from(sats)
self.fee_usd_sum.compute_transform2(
starting_indexes.height,
self.fee.sum_cum.sum.inner(),
&prices.usd.price,
|(h, sats, price, ..)| (h, price * Bitcoin::from(sats)),
exit,
)?;
// Rolling fee stats (from per-block sum)
let window_starts = blocks.count.window_starts();
self.fee_rolling.compute(
starting_indexes.height,
&window_starts,
self.fee.sum_cum.sum.inner(),
exit,
)?;
// Rolling fee rate distribution (from per-block average)
self.fee_rate_rolling.compute_distribution(
starting_indexes.height,
&window_starts,
&self.fee_rate.min_max_average.average.0,
exit,
)?;
Ok(())
}

View File

@@ -1,32 +1,33 @@
use brk_error::Result;
use brk_indexer::Indexer;
use brk_types::Version;
use vecdb::{Database, EagerVec, ImportableVec};
use super::Vecs;
use crate::{
indexes,
internal::{ComputedFromTxDistribution, ValueFromTxFull},
prices,
internal::{Distribution, Full, RollingDistribution, RollingFull},
};
/// Bump this when fee/feerate aggregation logic changes (e.g., skip coinbase).
const VERSION: Version = Version::ONE;
const VERSION: Version = Version::new(2);
impl Vecs {
pub(crate) fn forced_import(
db: &Database,
version: Version,
indexer: &Indexer,
indexes: &indexes::Vecs,
prices: &prices::Vecs,
) -> Result<Self> {
let v = version + VERSION;
Ok(Self {
input_value: EagerVec::forced_import(db, "input_value", version)?,
output_value: EagerVec::forced_import(db, "output_value", version)?,
fee: ValueFromTxFull::forced_import(db, "fee", v, indexes, indexer, prices)?,
fee_rate: ComputedFromTxDistribution::forced_import(db, "fee_rate", v, indexes)?,
fee_txindex: EagerVec::forced_import(db, "fee", v)?,
fee: Full::forced_import(db, "fee", v)?,
fee_usd_sum: EagerVec::forced_import(db, "fee_usd_sum", v)?,
fee_rolling: RollingFull::forced_import(db, "fee", v, indexes)?,
fee_rate_txindex: EagerVec::forced_import(db, "fee_rate", v)?,
fee_rate: Distribution::forced_import(db, "fee_rate", v)?,
fee_rate_rolling: RollingDistribution::forced_import(db, "fee_rate", v, indexes)?,
})
}
}

View File

@@ -1,13 +1,18 @@
use brk_traversable::Traversable;
use brk_types::{FeeRate, Sats, TxIndex};
use brk_types::{Dollars, FeeRate, Height, Sats, TxIndex};
use vecdb::{EagerVec, PcoVec, Rw, StorageMode};
use crate::internal::{ComputedFromTxDistribution, ValueFromTxFull};
use crate::internal::{Distribution, Full, RollingDistribution, RollingFull};
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
pub input_value: M::Stored<EagerVec<PcoVec<TxIndex, Sats>>>,
pub output_value: M::Stored<EagerVec<PcoVec<TxIndex, Sats>>>,
pub fee: ValueFromTxFull<M>,
pub fee_rate: ComputedFromTxDistribution<FeeRate, M>,
pub fee_txindex: M::Stored<EagerVec<PcoVec<TxIndex, Sats>>>,
pub fee: Full<Height, Sats, M>,
pub fee_usd_sum: M::Stored<EagerVec<PcoVec<Height, Dollars>>>,
pub fee_rolling: RollingFull<Sats, M>,
pub fee_rate_txindex: M::Stored<EagerVec<PcoVec<TxIndex, FeeRate>>>,
pub fee_rate: Distribution<Height, FeeRate, M>,
pub fee_rate_rolling: RollingDistribution<FeeRate, M>,
}

View File

@@ -25,7 +25,7 @@ impl Vecs {
let count = CountVecs::forced_import(&db, version, indexer, indexes)?;
let size = SizeVecs::forced_import(&db, version, indexer, indexes)?;
let fees = FeesVecs::forced_import(&db, version, indexer, indexes, prices)?;
let fees = FeesVecs::forced_import(&db, version, indexes)?;
let versions = VersionsVecs::forced_import(&db, version, indexes)?;
let volume = VolumeVecs::forced_import(&db, version, indexes, prices)?;

View File

@@ -46,6 +46,23 @@ impl Vecs {
exit,
)?;
// Rolling sums for sent and received
let window_starts = blocks.count.window_starts();
self.sent_sum_rolling.compute_rolling_sum(
starting_indexes.height,
&window_starts,
&self.sent_sum.sats.height,
&self.sent_sum.usd.height,
exit,
)?;
self.received_sum_rolling.compute_rolling_sum(
starting_indexes.height,
&window_starts,
&self.received_sum.sats.height,
&self.received_sum.usd.height,
exit,
)?;
// tx_per_sec: per-block tx count / block interval
self.tx_per_sec.height.compute_transform2(
starting_indexes.height,
@@ -83,7 +100,7 @@ impl Vecs {
// outputs_per_sec: per-block output count / block interval
self.outputs_per_sec.height.compute_transform2(
starting_indexes.height,
&outputs_count.total_count.height.sum_cum.sum.0,
&outputs_count.total_count.sum_cum.sum.0,
&blocks.interval.interval.height,
|(h, output_count, interval, ..)| {
let interval_f64 = f64::from(*interval);

View File

@@ -5,7 +5,7 @@ use vecdb::Database;
use super::Vecs;
use crate::{
indexes,
internal::{ComputedFromHeightLast, ValueFromHeightLast, ValueFromHeightSum},
internal::{ComputedFromHeightLast, StoredValueRollingWindows, ValueFromHeightLast},
prices,
};
@@ -18,13 +18,17 @@ impl Vecs {
) -> Result<Self> {
let v2 = Version::TWO;
Ok(Self {
sent_sum: ValueFromHeightSum::forced_import(db, "sent_sum", version, indexes, prices)?,
received_sum: ValueFromHeightSum::forced_import(
db,
"received_sum",
version,
indexes,
prices,
sent_sum: ValueFromHeightLast::forced_import(
db, "sent_sum", version, indexes, prices,
)?,
sent_sum_rolling: StoredValueRollingWindows::forced_import(
db, "sent_sum", version, indexes,
)?,
received_sum: ValueFromHeightLast::forced_import(
db, "received_sum", version, indexes, prices,
)?,
received_sum_rolling: StoredValueRollingWindows::forced_import(
db, "received_sum", version, indexes,
)?,
annualized_volume: ValueFromHeightLast::forced_import(
db,

View File

@@ -2,13 +2,20 @@ use brk_traversable::Traversable;
use brk_types::StoredF32;
use vecdb::{Rw, StorageMode};
use crate::internal::{ComputedFromHeightLast, ValueFromHeightLast, ValueFromHeightSum};
use crate::internal::{
ComputedFromHeightLast, StoredValueRollingWindows, ValueFromHeightLast,
};
/// Volume metrics
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
pub sent_sum: ValueFromHeightSum<M>,
pub received_sum: ValueFromHeightSum<M>,
#[traversable(flatten)]
pub sent_sum: ValueFromHeightLast<M>,
pub sent_sum_rolling: StoredValueRollingWindows<M>,
#[traversable(flatten)]
pub received_sum: ValueFromHeightLast<M>,
pub received_sum_rolling: StoredValueRollingWindows<M>,
#[traversable(flatten)]
pub annualized_volume: ValueFromHeightLast<M>,
pub tx_per_sec: ComputedFromHeightLast<StoredF32, M>,
pub outputs_per_sec: ComputedFromHeightLast<StoredF32, M>,