global: snapshot part 17

This commit is contained in:
nym21
2026-03-21 19:41:41 +01:00
parent 2991562234
commit 8859de5393
35 changed files with 567 additions and 360 deletions

View File

@@ -5,10 +5,10 @@ use brk_cohort::{
};
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, Indexes, Version};
use brk_types::{Height, Indexes, StoredU64, Version};
use derive_more::{Deref, DerefMut};
use rayon::prelude::*;
use vecdb::{AnyStoredVec, Database, Exit, Rw, StorageMode};
use vecdb::{AnyStoredVec, Database, Exit, ReadableVec, Rw, StorageMode};
use crate::{distribution::DynCohortVecs, indexes, internal::CachedWindowStarts, prices};
@@ -106,11 +106,12 @@ impl AddrCohorts {
&mut self,
prices: &prices::Vecs,
starting_indexes: &Indexes,
all_utxo_count: &impl ReadableVec<Height, StoredU64>,
exit: &Exit,
) -> Result<()> {
self.0
.par_iter_mut()
.try_for_each(|v| v.compute_rest_part2(prices, starting_indexes, exit))
.try_for_each(|v| v.compute_rest_part2(prices, starting_indexes, all_utxo_count, exit))
}
/// Returns a parallel iterator over all vecs for parallel writing.

View File

@@ -232,9 +232,10 @@ impl CohortVecs for AddrCohortVecs {
&mut self,
prices: &prices::Vecs,
starting_indexes: &Indexes,
all_utxo_count: &impl ReadableVec<Height, StoredU64>,
exit: &Exit,
) -> Result<()> {
self.metrics
.compute_rest_part2(prices, starting_indexes, exit)
.compute_rest_part2(prices, starting_indexes, all_utxo_count, exit)
}
}

View File

@@ -1,6 +1,6 @@
use brk_error::Result;
use brk_types::{Cents, Height, Indexes, Version};
use vecdb::Exit;
use brk_types::{Cents, Height, Indexes, StoredU64, Version};
use vecdb::{Exit, ReadableVec};
use crate::prices;
@@ -62,6 +62,7 @@ pub trait CohortVecs: DynCohortVecs {
&mut self,
prices: &prices::Vecs,
starting_indexes: &Indexes,
all_utxo_count: &impl ReadableVec<Height, StoredU64>,
exit: &Exit,
) -> Result<()>;
}

View File

@@ -565,8 +565,9 @@ impl UTXOCohorts<Rw> {
exit,
)?;
// Clone all_supply_sats for non-all cohorts.
// Clone all_supply_sats and all_utxo_count for non-all cohorts.
let all_supply_sats = self.all.metrics.supply.total.sats.height.read_only_clone();
let all_utxo_count = self.all.metrics.outputs.unspent_count.height.read_only_clone();
// Destructure to allow parallel mutable access to independent fields.
let Self {
@@ -589,6 +590,7 @@ impl UTXOCohorts<Rw> {
let vc = &under_1h_value_created;
let vd = &under_1h_value_destroyed;
let ss = &all_supply_sats;
let au = &all_utxo_count;
let tasks: Vec<Box<dyn FnOnce() -> Result<()> + Send + '_>> = vec![
Box::new(|| {
@@ -600,6 +602,7 @@ impl UTXOCohorts<Rw> {
vc,
vd,
ss,
au,
exit,
)
}),
@@ -610,58 +613,59 @@ impl UTXOCohorts<Rw> {
starting_indexes,
height_to_market_cap,
ss,
au,
exit,
)
}),
Box::new(|| {
age_range.par_iter_mut().try_for_each(|v| {
v.metrics
.compute_rest_part2(prices, starting_indexes, ss, exit)
.compute_rest_part2(prices, starting_indexes, ss, au, exit)
})
}),
Box::new(|| {
under_age.par_iter_mut().try_for_each(|v| {
v.metrics
.compute_rest_part2(prices, starting_indexes, ss, exit)
.compute_rest_part2(prices, starting_indexes, ss, au, exit)
})
}),
Box::new(|| {
over_age.par_iter_mut().try_for_each(|v| {
v.metrics
.compute_rest_part2(prices, starting_indexes, ss, exit)
.compute_rest_part2(prices, starting_indexes, ss, au, exit)
})
}),
Box::new(|| {
over_amount
.par_iter_mut()
.try_for_each(|v| v.metrics.compute_rest_part2(prices, starting_indexes, exit))
.try_for_each(|v| v.metrics.compute_rest_part2(prices, starting_indexes, au, exit))
}),
Box::new(|| {
epoch.par_iter_mut().try_for_each(|v| {
v.metrics
.compute_rest_part2(prices, starting_indexes, ss, exit)
.compute_rest_part2(prices, starting_indexes, ss, au, exit)
})
}),
Box::new(|| {
class.par_iter_mut().try_for_each(|v| {
v.metrics
.compute_rest_part2(prices, starting_indexes, ss, exit)
.compute_rest_part2(prices, starting_indexes, ss, au, exit)
})
}),
Box::new(|| {
amount_range
.par_iter_mut()
.try_for_each(|v| v.metrics.compute_rest_part2(prices, starting_indexes, exit))
.try_for_each(|v| v.metrics.compute_rest_part2(prices, starting_indexes, au, exit))
}),
Box::new(|| {
under_amount
.par_iter_mut()
.try_for_each(|v| v.metrics.compute_rest_part2(prices, starting_indexes, exit))
.try_for_each(|v| v.metrics.compute_rest_part2(prices, starting_indexes, au, exit))
}),
Box::new(|| {
type_
.par_iter_mut()
.try_for_each(|v| v.metrics.compute_rest_part2(prices, starting_indexes, exit))
.try_for_each(|v| v.metrics.compute_rest_part2(prices, starting_indexes, au, exit))
}),
];

View File

@@ -5,7 +5,7 @@ use brk_types::{
Cents, Dollars, Height, Indexes, Version,
};
use vecdb::AnyStoredVec;
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
use vecdb::{Exit, ReadOnlyClone, ReadableVec, Rw, StorageMode};
use crate::{
blocks,
@@ -135,6 +135,13 @@ impl AllCohortMetrics {
exit,
)?;
let all_utxo_count = self.outputs.unspent_count.height.read_only_clone();
self.outputs.compute_part2(
starting_indexes.height,
&all_utxo_count,
exit,
)?;
self.cost_basis.compute_prices(
starting_indexes,
&prices.spot.cents.height,

View File

@@ -1,7 +1,7 @@
use brk_cohort::Filter;
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, Indexes, Sats};
use brk_types::{Height, Indexes, Sats, StoredU64};
use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode};
use crate::{
@@ -69,6 +69,7 @@ impl BasicCohortMetrics {
prices: &prices::Vecs,
starting_indexes: &Indexes,
all_supply_sats: &impl ReadableVec<Height, Sats>,
all_utxo_count: &impl ReadableVec<Height, StoredU64>,
exit: &Exit,
) -> Result<()> {
self.realized.compute_rest_part2(
@@ -93,6 +94,8 @@ impl BasicCohortMetrics {
exit,
)?;
self.outputs.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
Ok(())
}

View File

@@ -1,7 +1,7 @@
use brk_cohort::Filter;
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, Indexes, Sats, Version};
use brk_types::{Height, Indexes, Sats, StoredU64, Version};
use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode};
use crate::{
@@ -108,6 +108,8 @@ impl CoreCohortMetrics {
self.supply
.compute(prices, starting_indexes.height, exit)?;
self.outputs.compute_rest(starting_indexes.height, exit)?;
self.activity
.compute_rest_part1(prices, starting_indexes, exit)?;
@@ -124,6 +126,7 @@ impl CoreCohortMetrics {
prices: &prices::Vecs,
starting_indexes: &Indexes,
all_supply_sats: &impl ReadableVec<Height, Sats>,
all_utxo_count: &impl ReadableVec<Height, StoredU64>,
exit: &Exit,
) -> Result<()> {
self.realized.compute_rest_part2(
@@ -148,6 +151,8 @@ impl CoreCohortMetrics {
exit,
)?;
self.outputs.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
Ok(())
}
}

View File

@@ -2,7 +2,7 @@ use brk_cohort::Filter;
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{
Dollars, Height, Indexes, Sats, Version,
Dollars, Height, Indexes, Sats, StoredU64, Version,
};
use vecdb::AnyStoredVec;
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
@@ -94,6 +94,7 @@ impl ExtendedCohortMetrics {
starting_indexes: &Indexes,
height_to_market_cap: &impl ReadableVec<Height, Dollars>,
all_supply_sats: &impl ReadableVec<Height, Sats>,
all_utxo_count: &impl ReadableVec<Height, StoredU64>,
exit: &Exit,
) -> Result<()> {
self.realized.compute_rest_part2(
@@ -141,6 +142,8 @@ impl ExtendedCohortMetrics {
exit,
)?;
self.outputs.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
Ok(())
}

View File

@@ -1,6 +1,6 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Cents, Dollars, Height, Indexes, Sats, Version};
use brk_types::{Cents, Dollars, Height, Indexes, Sats, StoredU64, Version};
use derive_more::{Deref, DerefMut};
use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode};
@@ -67,6 +67,7 @@ impl ExtendedAdjustedCohortMetrics {
under_1h_value_created: &impl ReadableVec<Height, Cents>,
under_1h_value_destroyed: &impl ReadableVec<Height, Cents>,
all_supply_sats: &impl ReadableVec<Height, Sats>,
all_utxo_count: &impl ReadableVec<Height, StoredU64>,
exit: &Exit,
) -> Result<()> {
self.inner.compute_rest_part2(
@@ -75,6 +76,7 @@ impl ExtendedAdjustedCohortMetrics {
starting_indexes,
height_to_market_cap,
all_supply_sats,
all_utxo_count,
exit,
)?;

View File

@@ -1,8 +1,8 @@
use brk_cohort::Filter;
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::Indexes;
use vecdb::{AnyStoredVec, Exit, Rw, StorageMode};
use brk_types::{Height, Indexes, StoredU64};
use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode};
use crate::{
distribution::metrics::{
@@ -101,6 +101,7 @@ impl MinimalCohortMetrics {
exit: &Exit,
) -> Result<()> {
self.supply.compute(prices, starting_indexes.height, exit)?;
self.outputs.compute_rest(starting_indexes.height, exit)?;
self.activity
.compute_rest_part1(prices, starting_indexes, exit)?;
self.realized
@@ -112,6 +113,7 @@ impl MinimalCohortMetrics {
&mut self,
prices: &prices::Vecs,
starting_indexes: &Indexes,
all_utxo_count: &impl ReadableVec<Height, StoredU64>,
exit: &Exit,
) -> Result<()> {
self.realized.compute_rest_part2(
@@ -128,6 +130,8 @@ impl MinimalCohortMetrics {
exit,
)?;
self.outputs.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
Ok(())
}
}

View File

@@ -1,8 +1,8 @@
use brk_cohort::Filter;
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::Indexes;
use vecdb::{AnyStoredVec, Exit, Rw, StorageMode};
use brk_types::{Height, Indexes, StoredU64};
use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode};
use crate::{
distribution::metrics::{
@@ -63,6 +63,7 @@ impl TypeCohortMetrics {
exit: &Exit,
) -> Result<()> {
self.supply.compute(prices, starting_indexes.height, exit)?;
self.outputs.compute_rest(starting_indexes.height, exit)?;
self.activity
.compute_rest_part1(prices, starting_indexes, exit)?;
self.realized
@@ -74,6 +75,7 @@ impl TypeCohortMetrics {
&mut self,
prices: &prices::Vecs,
starting_indexes: &Indexes,
all_utxo_count: &impl ReadableVec<Height, StoredU64>,
exit: &Exit,
) -> Result<()> {
self.realized.compute_rest_part2(
@@ -90,6 +92,8 @@ impl TypeCohortMetrics {
exit,
)?;
self.outputs.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
Ok(())
}
}

View File

@@ -269,6 +269,8 @@ pub trait CohortMetricsBase:
) -> Result<()> {
self.supply_mut()
.compute(prices, starting_indexes.height, exit)?;
self.outputs_mut()
.compute_rest(starting_indexes.height, exit)?;
self.activity_mut()
.compute_rest_part1(prices, starting_indexes, exit)?;

View File

@@ -1,38 +1,44 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{BasisPointsSigned32, Indexes, StoredI64, StoredU64, Version};
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
use brk_types::{BasisPointsSigned32, Height, Indexes, StoredF32, StoredI64, StoredU32, StoredU64, Version};
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, Rw, StorageMode, WritableVec};
use crate::{
distribution::{
metrics::ImportConfig,
state::{CohortState, CostBasisOps, RealizedOps},
},
internal::PerBlockWithDeltas,
internal::{PerBlock, PerBlockCumulativeRolling, PerBlockWithDeltas, RatioU32U64F32},
};
/// Base output metrics: utxo_count + delta.
#[derive(Traversable)]
pub struct OutputsBase<M: StorageMode = Rw> {
pub unspent_count: PerBlockWithDeltas<StoredU64, StoredI64, BasisPointsSigned32, M>,
pub spent_count: PerBlockCumulativeRolling<StoredU32, StoredU64, M>,
pub spending_rate: PerBlock<StoredF32, M>,
}
impl OutputsBase {
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
let v1 = Version::ONE;
Ok(Self {
unspent_count: PerBlockWithDeltas::forced_import(
cfg.db,
&cfg.name("utxo_count"),
cfg.version,
Version::ONE,
v1,
cfg.indexes,
cfg.cached_starts,
)?,
spent_count: cfg.import("spent_utxo_count", v1)?,
spending_rate: cfg.import("spending_rate", v1)?,
})
}
pub(crate) fn min_len(&self) -> usize {
self.unspent_count.height.len()
.min(self.spent_count.block.len())
}
#[inline(always)]
@@ -40,10 +46,35 @@ impl OutputsBase {
self.unspent_count
.height
.push(StoredU64::from(state.supply.utxo_count));
self.spent_count
.block
.push(StoredU32::from(state.spent_utxo_count));
}
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
vec![&mut self.unspent_count.height as &mut dyn AnyStoredVec]
vec![
&mut self.unspent_count.height as &mut dyn AnyStoredVec,
&mut self.spent_count.block,
]
}
pub(crate) fn compute_rest(&mut self, max_from: Height, exit: &Exit) -> Result<()> {
self.spent_count.compute_rest(max_from, exit)
}
pub(crate) fn compute_part2(
&mut self,
max_from: Height,
all_utxo_count: &impl ReadableVec<Height, StoredU64>,
exit: &Exit,
) -> Result<()> {
self.spending_rate
.compute_binary::<StoredU32, StoredU64, RatioU32U64F32>(
max_from,
&self.spent_count.block,
all_utxo_count,
exit,
)
}
pub(crate) fn compute_from_stateful(
@@ -60,6 +91,7 @@ impl OutputsBase {
.collect::<Vec<_>>(),
exit,
)?;
sum_others!(self, starting_indexes, others, exit; spent_count.block);
Ok(())
}
}

View File

@@ -28,6 +28,7 @@ impl<R: RealizedOps> AddrCohortState<R> {
self.addr_count = 0;
self.inner.supply = SupplyState::default();
self.inner.sent = Sats::ZERO;
self.inner.spent_utxo_count = 0;
self.inner.satdays_destroyed = Sats::ZERO;
self.inner.realized = R::default();
}

View File

@@ -53,6 +53,7 @@ pub struct CohortState<R: RealizedOps, C: CostBasisOps> {
pub supply: SupplyState,
pub realized: R,
pub sent: Sats,
pub spent_utxo_count: u64,
pub satdays_destroyed: Sats,
cost_basis: C,
}
@@ -63,6 +64,7 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
supply: SupplyState::default(),
realized: R::default(),
sent: Sats::ZERO,
spent_utxo_count: 0,
satdays_destroyed: Sats::ZERO,
cost_basis: C::create(path, name),
}
@@ -97,6 +99,7 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
pub(crate) fn reset_single_iteration_values(&mut self) {
self.sent = Sats::ZERO;
self.spent_utxo_count = 0;
if R::TRACK_ACTIVITY {
self.satdays_destroyed = Sats::ZERO;
}
@@ -197,6 +200,7 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
) {
self.supply -= supply;
self.sent += pre.sats;
self.spent_utxo_count += supply.utxo_count;
if R::TRACK_ACTIVITY {
self.satdays_destroyed += pre.age.satdays_destroyed(pre.sats);
}
@@ -220,6 +224,7 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
self.send_utxo_precomputed(supply, &pre);
} else if supply.utxo_count > 0 {
self.supply -= supply;
self.spent_utxo_count += supply.utxo_count;
}
}
@@ -239,6 +244,7 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
}
self.supply -= supply;
self.spent_utxo_count += supply.utxo_count;
if supply.value > Sats::ZERO {
self.sent += supply.value;

View File

@@ -23,6 +23,7 @@ impl<R: RealizedOps, C: CostBasisOps> UTXOCohortState<R, C> {
pub(crate) fn reset(&mut self) {
self.0.supply = SupplyState::default();
self.0.sent = Sats::ZERO;
self.0.spent_utxo_count = 0;
self.0.satdays_destroyed = Sats::ZERO;
self.0.realized = R::default();
}

View File

@@ -467,8 +467,10 @@ impl Vecs {
&height_to_market_cap,
exit,
)?;
let all_utxo_count = self.utxo_cohorts.all.metrics.outputs.unspent_count.height.read_only_clone();
self.addr_cohorts
.compute_rest_part2(prices, starting_indexes, exit)?;
.compute_rest_part2(prices, starting_indexes, &all_utxo_count, exit)?;
let _lock = exit.lock();
self.db.compact()?;

View File

@@ -26,7 +26,7 @@ pub use derived::{
pub use ratio::{
RatioCentsBp32, RatioCentsSignedCentsBps32, RatioCentsSignedDollarsBps32, RatioDiffCentsBps32,
RatioDiffDollarsBps32, RatioDiffF32Bps32, RatioDollarsBp16, RatioDollarsBp32,
RatioDollarsBps32, RatioSatsBp16, RatioU64Bp16,
RatioDollarsBps32, RatioSatsBp16, RatioU32U64F32, RatioU64Bp16,
};
pub use specialized::{
BlockCountTarget1m, BlockCountTarget1w, BlockCountTarget1y, BlockCountTarget24h,

View File

@@ -1,6 +1,6 @@
use brk_types::{
BasisPoints16, BasisPoints32, BasisPointsSigned32, Cents, CentsSigned, Dollars, Sats, StoredF32,
StoredU64,
StoredU32, StoredU64,
};
use vecdb::BinaryTransform;
@@ -112,6 +112,19 @@ impl BinaryTransform<Dollars, Dollars, BasisPoints32> for RatioDollarsBp32 {
}
}
pub struct RatioU32U64F32;
impl BinaryTransform<StoredU32, StoredU64, StoredF32> for RatioU32U64F32 {
#[inline(always)]
fn apply(numerator: StoredU32, denominator: StoredU64) -> StoredF32 {
if *denominator > 0 {
StoredF32::from(*numerator as f64 / *denominator as f64)
} else {
StoredF32::default()
}
}
}
pub struct RatioDiffF32Bps32;
impl BinaryTransform<StoredF32, StoredF32, BasisPointsSigned32> for RatioDiffF32Bps32 {