global: snapshot

This commit is contained in:
nym21
2026-03-10 11:22:17 +01:00
parent 64ef63a056
commit 5ede3dc416
40 changed files with 408 additions and 259 deletions

View File

@@ -47,8 +47,7 @@ pub(crate) fn process_sent(
for (receive_height, by_type) in sent_data.into_iter() {
let prev_price = height_to_price[receive_height.to_usize()];
let prev_timestamp = height_to_timestamp[receive_height.to_usize()];
let blocks_old = current_height.to_usize() - receive_height.to_usize();
let age = Age::new(current_timestamp, prev_timestamp, blocks_old);
let age = Age::new(current_timestamp, prev_timestamp);
// Compute peak price during holding period for peak regret
// This is the max HIGH price between receive and send heights

View File

@@ -43,8 +43,7 @@ impl UTXOCohorts<Rw> {
let block_state = &chain_state[receive_height.to_usize()];
let prev_price = block_state.price;
let blocks_old = chain_len - 1 - receive_height.to_usize();
let age = Age::new(last_timestamp, block_state.timestamp, blocks_old);
let age = Age::new(last_timestamp, block_state.timestamp);
// Compute peak price during holding period for peak regret
// This is the max price between receive and send heights

View File

@@ -2,12 +2,12 @@ use brk_cohort::ByAddressType;
use brk_error::Result;
use brk_indexer::Indexer;
use brk_types::{
Cents, Date, Height, ONE_DAY_IN_SEC, OutputType, Sats, Timestamp, TxIndex, TypeIndex,
Cents, Date, Height, ONE_DAY_IN_SEC, OutputType, Sats, StoredF64, Timestamp, TxIndex, TypeIndex,
};
use rayon::prelude::*;
use rustc_hash::FxHashSet;
use tracing::{debug, info};
use vecdb::{AnyVec, Exit, ReadableVec, VecIndex};
use vecdb::{AnyVec, Exit, ReadableVec, VecIndex, WritableVec};
use crate::{
distribution::{
@@ -66,7 +66,7 @@ pub(crate) fn process_blocks(
let height_to_first_txindex = &indexer.vecs.transactions.first_txindex;
let height_to_first_txoutindex = &indexer.vecs.outputs.first_txoutindex;
let height_to_first_txinindex = &indexer.vecs.inputs.first_txinindex;
let height_to_tx_count = &transactions.count.tx_count.height;
let height_to_tx_count = &transactions.count.tx_count.raw.height;
let height_to_output_count = &outputs.count.total_count.full.sum;
let height_to_input_count = &inputs.count.full.sum;
let txindex_to_output_count = &indexes.txindex.output_count;
@@ -353,6 +353,23 @@ pub(crate) fn process_blocks(
timestamp,
});
// Compute total coinblocks destroyed (once globally, before send() consumes height_to_sent)
{
let h = height.to_usize();
let total_satblocks: u128 = height_to_sent
.iter()
.filter(|(rh, _)| rh.to_usize() < h)
.map(|(rh, sent)| {
let blocks_old = h - rh.to_usize();
blocks_old as u128 * u64::from(sent.spendable_supply.value) as u128
})
.sum();
vecs.coinblocks_destroyed.raw.height.truncate_push(
height,
StoredF64::from(total_satblocks as f64 / Sats::ONE_BTC_U128 as f64),
)?;
}
// Record maturation (sats crossing age boundaries)
vecs.utxo_cohorts.push_maturation(height, &matured)?;

View File

@@ -79,9 +79,13 @@ pub(crate) fn write(
.chain(vecs.addr_count.par_iter_height_mut())
.chain(vecs.empty_addr_count.par_iter_height_mut())
.chain(vecs.address_activity.par_iter_height_mut())
.chain(rayon::iter::once(
&mut vecs.supply_state as &mut dyn AnyStoredVec,
))
.chain(
[
&mut vecs.supply_state as &mut dyn AnyStoredVec,
&mut vecs.coinblocks_destroyed.raw.height,
]
.into_par_iter(),
)
.chain(vecs.utxo_cohorts.par_iter_vecs_mut())
.chain(vecs.address_cohorts.par_iter_vecs_mut())
.try_for_each(|v| v.any_stamped_write_maybe_with_changes(stamp, with_changes))?;

View File

@@ -2,7 +2,7 @@ use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Bitcoin, Height, Indexes, Sats, StoredF32, StoredF64, Version};
use derive_more::{Deref, DerefMut};
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, Rw, StorageMode, WritableVec};
use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode};
use crate::internal::{ComputedPerBlock, RollingWindowsFrom1w};
@@ -10,12 +10,6 @@ use crate::{blocks, distribution::{metrics::ImportConfig, state::{CohortState, R
use super::ActivityCore;
#[derive(Traversable)]
pub struct ActivityCoinblocks<M: StorageMode = Rw> {
pub raw: ComputedPerBlock<StoredF64, M>,
pub cumulative: ComputedPerBlock<StoredF64, M>,
}
#[derive(Deref, DerefMut, Traversable)]
pub struct ActivityFull<M: StorageMode = Rw> {
#[deref]
@@ -23,8 +17,6 @@ pub struct ActivityFull<M: StorageMode = Rw> {
#[traversable(flatten)]
pub inner: ActivityCore<M>,
pub coinblocks_destroyed: ActivityCoinblocks<M>,
#[traversable(wrap = "coindays_destroyed", rename = "cumulative")]
pub coindays_destroyed_cumulative: ComputedPerBlock<StoredF64, M>,
#[traversable(wrap = "coindays_destroyed", rename = "sum")]
@@ -42,10 +34,6 @@ impl ActivityFull {
let v1 = Version::ONE;
Ok(Self {
inner: ActivityCore::forced_import(cfg)?,
coinblocks_destroyed: ActivityCoinblocks {
raw: cfg.import("coinblocks_destroyed", v1)?,
cumulative: cfg.import("coinblocks_destroyed_cumulative", v1)?,
},
coindays_destroyed_cumulative: cfg.import("coindays_destroyed_cumulative", v1)?,
coindays_destroyed_sum: cfg.import("coindays_destroyed", v1)?,
sent_sum_extended: cfg.import("sent", v1)?,
@@ -55,9 +43,7 @@ impl ActivityFull {
}
pub(crate) fn full_min_len(&self) -> usize {
self.inner
.min_len()
.min(self.coinblocks_destroyed.raw.height.len())
self.inner.min_len()
}
pub(crate) fn full_truncate_push(
@@ -65,17 +51,11 @@ impl ActivityFull {
height: Height,
state: &CohortState<impl RealizedOps>,
) -> Result<()> {
self.inner.truncate_push(height, state)?;
self.coinblocks_destroyed.raw.height.truncate_push(
height,
StoredF64::from(Bitcoin::from(state.satblocks_destroyed)),
)?;
Ok(())
self.inner.truncate_push(height, state)
}
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
let mut vecs = self.inner.collect_vecs_mut();
vecs.push(&mut self.coinblocks_destroyed.raw.height as &mut dyn AnyStoredVec);
vecs.push(&mut self.dormancy.height);
vecs.push(&mut self.velocity.height);
vecs
@@ -100,15 +80,6 @@ impl ActivityFull {
self.inner
.compute_rest_part1(blocks, starting_indexes, exit)?;
self.coinblocks_destroyed
.cumulative
.height
.compute_cumulative(
starting_indexes.height,
&self.coinblocks_destroyed.raw.height,
exit,
)?;
self.coindays_destroyed_cumulative
.height
.compute_cumulative(

View File

@@ -289,7 +289,7 @@ impl RealizedFull {
.min(self.investor.price.cents.height.len())
.min(self.cap_raw.len())
.min(self.investor.cap_raw.len())
.min(self.peak_regret.value.height.len())
.min(self.peak_regret.value.raw.height.len())
}
pub(crate) fn truncate_push(
@@ -326,6 +326,7 @@ impl RealizedFull {
.truncate_push(height, state.realized.investor_cap_raw())?;
self.peak_regret
.value
.raw
.height
.truncate_push(height, state.realized.peak_regret())?;
@@ -341,7 +342,7 @@ impl RealizedFull {
vecs.push(&mut self.investor.price.cents.height);
vecs.push(&mut self.cap_raw as &mut dyn AnyStoredVec);
vecs.push(&mut self.investor.cap_raw as &mut dyn AnyStoredVec);
vecs.push(&mut self.peak_regret.value.height);
vecs.push(&mut self.peak_regret.value.raw.height);
vecs
}
@@ -400,6 +401,7 @@ impl RealizedFull {
self.peak_regret
.value
.raw
.height
.truncate_push(height, accum.peak_regret)?;
@@ -609,7 +611,7 @@ impl RealizedFull {
.rel_to_rcap
.compute_binary::<Cents, Cents, RatioCentsBp32>(
starting_indexes.height,
&self.peak_regret.value.height,
&self.peak_regret.value.raw.height,
&self.core.minimal.cap.cents.height,
exit,
)?;

View File

@@ -0,0 +1,96 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, Indexes, Version};
use derive_more::{Deref, DerefMut};
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
use crate::{distribution::state::UnrealizedState, prices};
use crate::internal::AmountPerBlock;
use crate::distribution::metrics::ImportConfig;
use super::SupplyBase;
/// Core supply metrics: total + halved + in_profit/in_loss (4 stored vecs).
#[derive(Deref, DerefMut, Traversable)]
pub struct SupplyCore<M: StorageMode = Rw> {
#[deref]
#[deref_mut]
#[traversable(flatten)]
pub base: SupplyBase<M>,
pub in_profit: AmountPerBlock<M>,
pub in_loss: AmountPerBlock<M>,
}
impl SupplyCore {
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
let v0 = Version::ZERO;
let base = SupplyBase::forced_import(cfg)?;
Ok(Self {
base,
in_profit: cfg.import("supply_in_profit", v0)?,
in_loss: cfg.import("supply_in_loss", v0)?,
})
}
pub(crate) fn min_len(&self) -> usize {
self.base
.min_len()
.min(self.in_profit.sats.height.len())
.min(self.in_loss.sats.height.len())
}
pub(crate) fn truncate_push_profitability(
&mut self,
height: Height,
state: &UnrealizedState,
) -> Result<()> {
self.in_profit
.sats
.height
.truncate_push(height, state.supply_in_profit)?;
self.in_loss
.sats
.height
.truncate_push(height, state.supply_in_loss)?;
Ok(())
}
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
let mut vecs = self.base.collect_vecs_mut();
vecs.push(&mut self.in_profit.sats.height as &mut dyn AnyStoredVec);
vecs.push(&mut self.in_profit.cents.height);
vecs.push(&mut self.in_loss.sats.height);
vecs.push(&mut self.in_loss.cents.height);
vecs
}
pub(crate) fn compute(
&mut self,
prices: &prices::Vecs,
max_from: Height,
exit: &Exit,
) -> Result<()> {
self.base.compute(prices, max_from, exit)?;
self.in_profit.compute(prices, max_from, exit)?;
self.in_loss.compute(prices, max_from, exit)?;
Ok(())
}
pub(crate) fn compute_from_stateful(
&mut self,
starting_indexes: &Indexes,
others: &[&Self],
exit: &Exit,
) -> Result<()> {
let base_refs: Vec<&SupplyBase> = others.iter().map(|o| &o.base).collect();
self.base
.compute_from_stateful(starting_indexes, &base_refs, exit)?;
sum_others!(self, starting_indexes, others, exit; in_profit.sats.height);
sum_others!(self, starting_indexes, others, exit; in_loss.sats.height);
Ok(())
}
}

View File

@@ -1,5 +1,7 @@
mod base;
mod core;
mod full;
pub use base::SupplyBase;
pub use self::core::SupplyCore;
pub use full::SupplyFull;

View File

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

View File

@@ -53,7 +53,6 @@ pub struct CohortState<R: RealizedOps> {
pub supply: SupplyState,
pub realized: R,
pub sent: Sats,
pub satblocks_destroyed: Sats,
pub satdays_destroyed: Sats,
cost_basis_data: CostBasisData,
}
@@ -64,7 +63,6 @@ impl<R: RealizedOps> CohortState<R> {
supply: SupplyState::default(),
realized: R::default(),
sent: Sats::ZERO,
satblocks_destroyed: Sats::ZERO,
satdays_destroyed: Sats::ZERO,
cost_basis_data: CostBasisData::create(path, name),
}
@@ -104,7 +102,6 @@ impl<R: RealizedOps> CohortState<R> {
pub(crate) fn reset_single_iteration_values(&mut self) {
self.sent = Sats::ZERO;
self.satdays_destroyed = Sats::ZERO;
self.satblocks_destroyed = Sats::ZERO;
self.realized.reset_single_iteration_values();
}
@@ -202,7 +199,6 @@ impl<R: RealizedOps> CohortState<R> {
) {
self.supply -= supply;
self.sent += pre.sats;
self.satblocks_destroyed += pre.age.satblocks_destroyed(pre.sats);
self.satdays_destroyed += pre.age.satdays_destroyed(pre.sats);
self.realized
@@ -246,7 +242,6 @@ impl<R: RealizedOps> CohortState<R> {
if supply.value > Sats::ZERO {
self.sent += supply.value;
self.satblocks_destroyed += age.satblocks_destroyed(supply.value);
self.satdays_destroyed += age.satdays_destroyed(supply.value);
let sats = supply.value;

View File

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

View File

@@ -5,7 +5,7 @@ use brk_indexer::Indexer;
use brk_traversable::Traversable;
use brk_types::{
Cents, EmptyAddressData, EmptyAddressIndex, FundedAddressData, FundedAddressIndex, Height,
Indexes, SupplyState, Timestamp, TxIndex, Version,
Indexes, StoredF64, SupplyState, Timestamp, TxIndex, Version,
};
use tracing::{debug, info};
use vecdb::{
@@ -23,7 +23,7 @@ use crate::{
state::BlockState,
},
indexes, inputs,
internal::{finalize_db, open_db},
internal::{finalize_db, open_db, ComputedPerBlockCumulative},
outputs, prices, transactions,
};
@@ -49,6 +49,8 @@ pub struct Vecs<M: StorageMode = Rw> {
pub utxo_cohorts: UTXOCohorts<M>,
pub address_cohorts: AddressCohorts<M>,
pub coinblocks_destroyed: ComputedPerBlockCumulative<StoredF64, M>,
pub addr_count: AddrCountsVecs<M>,
pub empty_addr_count: AddrCountsVecs<M>,
pub address_activity: AddressActivityVecs<M>,
@@ -159,6 +161,13 @@ impl Vecs {
utxo_cohorts,
address_cohorts,
coinblocks_destroyed: ComputedPerBlockCumulative::forced_import(
&db,
"coinblocks_destroyed",
version + Version::TWO,
indexes,
)?,
any_address_indexes: AnyAddressIndexesVecs::forced_import(&db, version)?,
addresses_data: AddressesDataVecs {
funded: fundedaddressindex_to_fundedaddressdata,
@@ -390,6 +399,10 @@ impl Vecs {
exit,
)?;
// 5b. Compute coinblocks_destroyed cumulative from raw
self.coinblocks_destroyed
.compute_rest(starting_indexes.height, exit)?;
// 6. Compute rest part1 (day1 mappings)
aggregates::compute_rest_part1(
&mut self.utxo_cohorts,
@@ -474,5 +487,6 @@ impl Vecs {
.min(Height::from(self.addr_count.min_stateful_height()))
.min(Height::from(self.empty_addr_count.min_stateful_height()))
.min(Height::from(self.address_activity.min_stateful_height()))
.min(Height::from(self.coinblocks_destroyed.raw.height.len()))
}
}