global: snapshot

This commit is contained in:
nym21
2026-02-27 18:48:37 +01:00
parent 6845ad409b
commit d5ec291579
62 changed files with 1960 additions and 1449 deletions

View File

@@ -36,7 +36,7 @@ impl Vecs {
self.thermo_cap.height.compute_transform(
starting_indexes.height,
&mining.rewards.subsidy.usd.cumulative.height,
&mining.rewards.subsidy.cumulative.usd.height,
|(i, v, ..)| (i, v),
exit,
)?;

View File

@@ -30,8 +30,8 @@ use super::{
vecs::Vecs,
},
BIP30_DUPLICATE_HEIGHT_1, BIP30_DUPLICATE_HEIGHT_2, BIP30_ORIGINAL_HEIGHT_1,
BIP30_ORIGINAL_HEIGHT_2, ComputeContext, FLUSH_INTERVAL, TxInReaders, TxOutReaders,
IndexToTxIndexBuf, VecsReaders,
BIP30_ORIGINAL_HEIGHT_2, ComputeContext, FLUSH_INTERVAL, IndexToTxIndexBuf, TxInReaders,
TxOutReaders, VecsReaders,
};
/// Process all blocks from starting_height to last_height.
@@ -278,7 +278,7 @@ pub(crate) fn process_blocks(
let outputs_handle = scope.spawn(|| {
// Process outputs (receive)
process_outputs(
&txoutindex_to_txindex,
txoutindex_to_txindex,
&txoutdata_vec,
&first_addressindexes,
&cache,

View File

@@ -82,6 +82,7 @@ impl ActivityMetrics {
/// Get minimum length across height-indexed vectors.
pub(crate) fn min_len(&self) -> usize {
self.sent
.base
.sats
.height
.len()
@@ -97,7 +98,7 @@ impl ActivityMetrics {
satblocks_destroyed: Sats,
satdays_destroyed: Sats,
) -> Result<()> {
self.sent.sats.height.truncate_push(height, sent)?;
self.sent.base.sats.height.truncate_push(height, sent)?;
self.satblocks_destroyed
.truncate_push(height, satblocks_destroyed)?;
self.satdays_destroyed
@@ -108,7 +109,7 @@ impl ActivityMetrics {
/// Returns a parallel iterator over all vecs for parallel writing.
pub(crate) fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
vec![
&mut self.sent.sats.height as &mut dyn AnyStoredVec,
&mut self.sent.base.sats.height as &mut dyn AnyStoredVec,
&mut self.satblocks_destroyed as &mut dyn AnyStoredVec,
&mut self.satdays_destroyed as &mut dyn AnyStoredVec,
]
@@ -128,11 +129,11 @@ impl ActivityMetrics {
others: &[&Self],
exit: &Exit,
) -> Result<()> {
self.sent.sats.height.compute_sum_of_others(
self.sent.base.sats.height.compute_sum_of_others(
starting_indexes.height,
&others
.iter()
.map(|v| &v.sent.sats.height)
.map(|v| &v.sent.base.sats.height)
.collect::<Vec<_>>(),
exit,
)?;
@@ -168,8 +169,8 @@ impl ActivityMetrics {
self.sent_14d_ema.compute_rolling_average(
starting_indexes.height,
&blocks.count.height_2w_ago,
&self.sent.sats.height,
&self.sent.usd.height,
&self.sent.base.sats.height,
&self.sent.base.usd.height,
exit,
)?;

View File

@@ -582,8 +582,8 @@ impl RealizedBase {
.min(self.loss_value_created.height.len())
.min(self.loss_value_destroyed.height.len())
.min(self.peak_regret.height.len())
.min(self.sent_in_profit.sats.height.len())
.min(self.sent_in_loss.sats.height.len())
.min(self.sent_in_profit.base.sats.height.len())
.min(self.sent_in_loss.base.sats.height.len())
}
/// Push realized state values to height-indexed vectors.
@@ -619,10 +619,12 @@ impl RealizedBase {
.height
.truncate_push(height, state.peak_regret().to_dollars())?;
self.sent_in_profit
.base
.sats
.height
.truncate_push(height, state.sent_in_profit())?;
self.sent_in_loss
.base
.sats
.height
.truncate_push(height, state.sent_in_loss())?;
@@ -644,8 +646,8 @@ impl RealizedBase {
&mut self.loss_value_created.height,
&mut self.loss_value_destroyed.height,
&mut self.peak_regret.height,
&mut self.sent_in_profit.sats.height,
&mut self.sent_in_loss.sats.height,
&mut self.sent_in_profit.base.sats.height,
&mut self.sent_in_loss.base.sats.height,
]
}
@@ -778,19 +780,19 @@ impl RealizedBase {
.collect::<Vec<_>>(),
exit,
)?;
self.sent_in_profit.sats.height.compute_sum_of_others(
self.sent_in_profit.base.sats.height.compute_sum_of_others(
starting_indexes.height,
&others
.iter()
.map(|v| &v.sent_in_profit.sats.height)
.map(|v| &v.sent_in_profit.base.sats.height)
.collect::<Vec<_>>(),
exit,
)?;
self.sent_in_loss.sats.height.compute_sum_of_others(
self.sent_in_loss.base.sats.height.compute_sum_of_others(
starting_indexes.height,
&others
.iter()
.map(|v| &v.sent_in_loss.sats.height)
.map(|v| &v.sent_in_loss.base.sats.height)
.collect::<Vec<_>>(),
exit,
)?;
@@ -1045,15 +1047,15 @@ impl RealizedBase {
self.sent_in_profit_14d_ema.compute_rolling_average(
starting_indexes.height,
&blocks.count.height_2w_ago,
&self.sent_in_profit.sats.height,
&self.sent_in_profit.usd.height,
&self.sent_in_profit.base.sats.height,
&self.sent_in_profit.base.usd.height,
exit,
)?;
self.sent_in_loss_14d_ema.compute_rolling_average(
starting_indexes.height,
&blocks.count.height_2w_ago,
&self.sent_in_loss.sats.height,
&self.sent_in_loss.usd.height,
&self.sent_in_loss.base.sats.height,
&self.sent_in_loss.base.usd.height,
exit,
)?;

View File

@@ -66,8 +66,8 @@ impl SupplyMetrics {
/// Returns a parallel iterator over all vecs for parallel writing.
pub(crate) fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
vec![
&mut self.total.sats.height as &mut dyn AnyStoredVec,
&mut self.total.usd.height as &mut dyn AnyStoredVec,
&mut self.total.base.sats.height as &mut dyn AnyStoredVec,
&mut self.total.base.usd.height as &mut dyn AnyStoredVec,
]
.into_par_iter()
}

View File

@@ -236,10 +236,10 @@ impl UnrealizedBase {
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
vec![
&mut self.supply_in_profit.sats.height as &mut dyn AnyStoredVec,
&mut self.supply_in_profit.usd.height as &mut dyn AnyStoredVec,
&mut self.supply_in_loss.sats.height as &mut dyn AnyStoredVec,
&mut self.supply_in_loss.usd.height as &mut dyn AnyStoredVec,
&mut self.supply_in_profit.base.sats.height as &mut dyn AnyStoredVec,
&mut self.supply_in_profit.base.usd.height as &mut dyn AnyStoredVec,
&mut self.supply_in_loss.base.sats.height as &mut dyn AnyStoredVec,
&mut self.supply_in_loss.base.usd.height as &mut dyn AnyStoredVec,
&mut self.unrealized_profit.height,
&mut self.unrealized_loss.height,
&mut self.invested_capital_in_profit.height,

View File

@@ -9,11 +9,11 @@ pub struct DistributionStats<A, B = A, C = A, D = A, E = A, F = A, G = A, H = A>
pub average: A,
pub min: B,
pub max: C,
pub p10: D,
pub p25: E,
pub pct10: D,
pub pct25: E,
pub median: F,
pub p75: G,
pub p90: H,
pub pct75: G,
pub pct90: H,
}
impl<A> DistributionStats<A> {
@@ -22,11 +22,11 @@ impl<A> DistributionStats<A> {
f(&mut self.average)?;
f(&mut self.min)?;
f(&mut self.max)?;
f(&mut self.p10)?;
f(&mut self.p25)?;
f(&mut self.pct10)?;
f(&mut self.pct25)?;
f(&mut self.median)?;
f(&mut self.p75)?;
f(&mut self.p90)?;
f(&mut self.pct75)?;
f(&mut self.pct90)?;
Ok(())
}
@@ -35,11 +35,11 @@ impl<A> DistributionStats<A> {
f(&self.average)
.min(f(&self.min))
.min(f(&self.max))
.min(f(&self.p10))
.min(f(&self.p25))
.min(f(&self.pct10))
.min(f(&self.pct25))
.min(f(&self.median))
.min(f(&self.p75))
.min(f(&self.p90))
.min(f(&self.pct75))
.min(f(&self.pct90))
}
}

View File

@@ -0,0 +1,46 @@
mod rolling_full;
mod rolling_sum;
mod windows;
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Bitcoin, Dollars, Sats, Version};
use vecdb::{Database, ReadableCloneableVec, Rw, StorageMode};
use crate::{
indexes,
internal::{ComputedFromHeightLast, LazyFromHeightLast, SatsToBitcoin},
};
pub use rolling_full::*;
pub use rolling_sum::*;
#[derive(Traversable)]
pub struct ByUnit<M: StorageMode = Rw> {
pub sats: ComputedFromHeightLast<Sats, M>,
pub btc: LazyFromHeightLast<Bitcoin, Sats>,
pub usd: ComputedFromHeightLast<Dollars, M>,
}
impl ByUnit {
pub(crate) fn forced_import(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
let sats = ComputedFromHeightLast::forced_import(db, name, version, indexes)?;
let btc = LazyFromHeightLast::from_computed::<SatsToBitcoin>(
&format!("{name}_btc"),
version,
sats.height.read_only_boxed_clone(),
&sats,
);
let usd =
ComputedFromHeightLast::forced_import(db, &format!("{name}_usd"), version, indexes)?;
Ok(Self { sats, btc, usd })
}
}

View File

@@ -0,0 +1,116 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Dollars, Height, Sats, Version};
use derive_more::{Deref, DerefMut};
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
use crate::{
indexes,
internal::{ByUnit, DistributionStats, WindowStarts, Windows},
traits::compute_rolling_distribution_from_starts,
};
/// One window slot: sum + 8 distribution stats, each a ByUnit.
///
/// Tree: `sum.sats.height`, `average.sats.height`, etc.
#[derive(Traversable)]
pub struct RollingFullSlot<M: StorageMode = Rw> {
pub sum: ByUnit<M>,
#[traversable(flatten)]
pub distribution: DistributionStats<ByUnit<M>>,
}
impl RollingFullSlot {
pub(crate) fn forced_import(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
Ok(Self {
sum: ByUnit::forced_import(db, &format!("{name}_sum"), version, indexes)?,
distribution: DistributionStats {
average: ByUnit::forced_import(db, &format!("{name}_average"), version, indexes)?,
min: ByUnit::forced_import(db, &format!("{name}_min"), version, indexes)?,
max: ByUnit::forced_import(db, &format!("{name}_max"), version, indexes)?,
pct10: ByUnit::forced_import(db, &format!("{name}_p10"), version, indexes)?,
pct25: ByUnit::forced_import(db, &format!("{name}_p25"), version, indexes)?,
median: ByUnit::forced_import(db, &format!("{name}_median"), version, indexes)?,
pct75: ByUnit::forced_import(db, &format!("{name}_p75"), version, indexes)?,
pct90: ByUnit::forced_import(db, &format!("{name}_p90"), version, indexes)?,
},
})
}
pub(crate) fn compute(
&mut self,
max_from: Height,
starts: &impl ReadableVec<Height, Height>,
sats_source: &impl ReadableVec<Height, Sats>,
usd_source: &impl ReadableVec<Height, Dollars>,
exit: &Exit,
) -> Result<()> {
self.sum.sats.height.compute_rolling_sum(max_from, starts, sats_source, exit)?;
self.sum.usd.height.compute_rolling_sum(max_from, starts, usd_source, exit)?;
let d = &mut self.distribution;
compute_rolling_distribution_from_starts(
max_from, starts, sats_source,
&mut d.average.sats.height, &mut d.min.sats.height,
&mut d.max.sats.height, &mut d.pct10.sats.height,
&mut d.pct25.sats.height, &mut d.median.sats.height,
&mut d.pct75.sats.height, &mut d.pct90.sats.height, exit,
)?;
compute_rolling_distribution_from_starts(
max_from, starts, usd_source,
&mut d.average.usd.height, &mut d.min.usd.height,
&mut d.max.usd.height, &mut d.pct10.usd.height,
&mut d.pct25.usd.height, &mut d.median.usd.height,
&mut d.pct75.usd.height, &mut d.pct90.usd.height, exit,
)?;
Ok(())
}
}
/// Rolling sum + distribution across 4 windows, window-first.
///
/// Tree: `_24h.sum.sats.height`, `_24h.average.sats.height`, etc.
#[derive(Deref, DerefMut, Traversable)]
#[traversable(transparent)]
pub struct RollingFullByUnit<M: StorageMode = Rw>(pub Windows<RollingFullSlot<M>>);
const VERSION: Version = Version::ZERO;
impl RollingFullByUnit {
pub(crate) fn forced_import(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
let v = version + VERSION;
Ok(Self(Windows {
_24h: RollingFullSlot::forced_import(db, &format!("{name}_24h"), v, indexes)?,
_7d: RollingFullSlot::forced_import(db, &format!("{name}_7d"), v, indexes)?,
_30d: RollingFullSlot::forced_import(db, &format!("{name}_30d"), v, indexes)?,
_1y: RollingFullSlot::forced_import(db, &format!("{name}_1y"), v, indexes)?,
}))
}
pub(crate) fn compute(
&mut self,
max_from: Height,
windows: &WindowStarts<'_>,
sats_source: &impl ReadableVec<Height, Sats>,
usd_source: &impl ReadableVec<Height, Dollars>,
exit: &Exit,
) -> Result<()> {
for (slot, starts) in self.0.as_mut_array().into_iter().zip(windows.as_array()) {
slot.compute(max_from, starts, sats_source, usd_source, exit)?;
}
Ok(())
}
}

View File

@@ -0,0 +1,46 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Dollars, Height, Sats, Version};
use derive_more::{Deref, DerefMut};
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
use crate::{
indexes,
internal::{ByUnit, WindowStarts, Windows},
};
/// Rolling sum only, window-first then unit.
///
/// Tree: `_24h.sats.height`, `_24h.btc.height`, etc.
#[derive(Deref, DerefMut, Traversable)]
#[traversable(transparent)]
pub struct RollingSumByUnit<M: StorageMode = Rw>(pub Windows<ByUnit<M>>);
const VERSION: Version = Version::ZERO;
impl RollingSumByUnit {
pub(crate) fn forced_import(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
let v = version + VERSION;
Ok(Self(Windows::<ByUnit>::forced_import(db, &format!("{name}_sum"), v, indexes)?))
}
pub(crate) fn compute_rolling_sum(
&mut self,
max_from: Height,
windows: &WindowStarts<'_>,
sats_source: &impl ReadableVec<Height, Sats>,
usd_source: &impl ReadableVec<Height, Dollars>,
exit: &Exit,
) -> Result<()> {
for (w, starts) in self.0.as_mut_array().into_iter().zip(windows.as_array()) {
w.sats.height.compute_rolling_sum(max_from, starts, sats_source, exit)?;
w.usd.height.compute_rolling_sum(max_from, starts, usd_source, exit)?;
}
Ok(())
}
}

View File

@@ -0,0 +1,24 @@
use brk_error::Result;
use brk_types::Version;
use vecdb::Database;
use crate::{
indexes,
internal::{ByUnit, Windows},
};
impl Windows<ByUnit> {
pub(crate) fn forced_import(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
Ok(Self {
_24h: ByUnit::forced_import(db, &format!("{name}_24h"), version, indexes)?,
_7d: ByUnit::forced_import(db, &format!("{name}_7d"), version, indexes)?,
_30d: ByUnit::forced_import(db, &format!("{name}_30d"), version, indexes)?,
_1y: ByUnit::forced_import(db, &format!("{name}_1y"), version, indexes)?,
})
}
}

View File

@@ -23,6 +23,7 @@ where
{
pub height: M::Stored<EagerVec<PcoVec<Height, T>>>,
pub cumulative: ComputedFromHeightLast<T, M>,
#[traversable(flatten)]
pub rolling: RollingFull<T, M>,
}

View File

@@ -1,3 +1,4 @@
mod by_unit;
mod constant;
mod cumulative;
mod cumulative_rolling_full;
@@ -21,6 +22,7 @@ mod value_lazy_computed_cumulative;
mod value_lazy_last;
mod value_sum_cumulative;
pub use by_unit::*;
pub use constant::*;
pub use cumulative::*;
pub use cumulative_rolling_full::*;

View File

@@ -1,26 +1,22 @@
//! Stored value type for Last pattern from Height.
//!
//! Both sats and USD are stored eagerly at the height level.
//! Used for rolling-window sums where USD = sum(usd_per_block),
//! NOT sats * current_price.
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Bitcoin, Dollars, Height, Sats, Version};
use vecdb::{Database, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode};
use brk_types::{Dollars, Height, Sats, Version};
use derive_more::{Deref, DerefMut};
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
use crate::{
indexes,
internal::{ComputedFromHeightLast, LazyFromHeightLast, SatsToBitcoin},
internal::ByUnit,
};
const VERSION: Version = Version::ZERO;
#[derive(Traversable)]
#[derive(Deref, DerefMut, Traversable)]
#[traversable(transparent)]
pub struct StoredValueFromHeightLast<M: StorageMode = Rw> {
pub sats: ComputedFromHeightLast<Sats, M>,
pub btc: LazyFromHeightLast<Bitcoin, Sats>,
pub usd: ComputedFromHeightLast<Dollars, M>,
#[deref]
#[deref_mut]
pub base: ByUnit<M>,
}
impl StoredValueFromHeightLast {
@@ -31,19 +27,9 @@ impl StoredValueFromHeightLast {
indexes: &indexes::Vecs,
) -> Result<Self> {
let v = version + VERSION;
let sats = ComputedFromHeightLast::forced_import(db, name, v, indexes)?;
let btc = LazyFromHeightLast::from_computed::<SatsToBitcoin>(
&format!("{name}_btc"),
v,
sats.height.read_only_boxed_clone(),
&sats,
);
let usd = ComputedFromHeightLast::forced_import(db, &format!("{name}_usd"), v, indexes)?;
Ok(Self { sats, btc, usd })
Ok(Self {
base: ByUnit::forced_import(db, name, v, indexes)?,
})
}
pub(crate) fn compute_rolling_sum(
@@ -54,10 +40,12 @@ impl StoredValueFromHeightLast {
usd_source: &impl ReadableVec<Height, Dollars>,
exit: &Exit,
) -> Result<()> {
self.sats
self.base
.sats
.height
.compute_rolling_sum(max_from, window_starts, sats_source, exit)?;
self.usd
self.base
.usd
.height
.compute_rolling_sum(max_from, window_starts, usd_source, exit)?;
Ok(())

View File

@@ -1,23 +1,22 @@
//! Rolling average values from Height - stores sats and dollars, btc is lazy.
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Bitcoin, Dollars, Height, Sats, Version};
use vecdb::{Database, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode};
use brk_types::{Dollars, Height, Sats, Version};
use derive_more::{Deref, DerefMut};
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
use crate::{
indexes,
internal::{ComputedFromHeightLast, LazyFromHeightLast, SatsToBitcoin},
internal::ByUnit,
};
const VERSION: Version = Version::ZERO;
/// Rolling average values indexed by height - sats (stored), btc (lazy), usd (stored).
#[derive(Traversable)]
#[derive(Deref, DerefMut, Traversable)]
#[traversable(transparent)]
pub struct ValueEmaFromHeight<M: StorageMode = Rw> {
pub sats: ComputedFromHeightLast<Sats, M>,
pub btc: LazyFromHeightLast<Bitcoin, Sats>,
pub usd: ComputedFromHeightLast<Dollars, M>,
#[deref]
#[deref_mut]
pub base: ByUnit<M>,
}
impl ValueEmaFromHeight {
@@ -28,27 +27,11 @@ impl ValueEmaFromHeight {
indexes: &indexes::Vecs,
) -> Result<Self> {
let v = version + VERSION;
let sats = ComputedFromHeightLast::forced_import(db, name, v, indexes)?;
let btc = LazyFromHeightLast::from_computed::<SatsToBitcoin>(
&format!("{name}_btc"),
v,
sats.height.read_only_boxed_clone(),
&sats,
);
let usd = ComputedFromHeightLast::forced_import(
db,
&format!("{name}_usd"),
v,
indexes,
)?;
Ok(Self { sats, btc, usd })
Ok(Self {
base: ByUnit::forced_import(db, name, v, indexes)?,
})
}
/// Compute rolling average for both sats and dollars in one call.
pub(crate) fn compute_rolling_average(
&mut self,
starting_height: Height,
@@ -57,10 +40,12 @@ impl ValueEmaFromHeight {
dollars_source: &(impl ReadableVec<Height, Dollars> + Sync),
exit: &Exit,
) -> Result<()> {
self.sats
self.base
.sats
.height
.compute_rolling_average(starting_height, window_starts, sats_source, exit)?;
self.usd
self.base
.usd
.height
.compute_rolling_average(starting_height, window_starts, dollars_source, exit)?;
Ok(())

View File

@@ -1,30 +1,23 @@
//! Value type for Full pattern from Height.
//!
//! Height-level USD stats are stored (eagerly computed from sats × price).
//! Uses CumFull: stored base + cumulative + rolling windows.
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Bitcoin, Dollars, Height, Sats, Version};
use vecdb::{Database, EagerVec, Exit, PcoVec, ReadableCloneableVec, Rw, StorageMode};
use brk_types::{Dollars, Height, Sats, Version};
use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
use crate::{
indexes,
internal::{
ComputedFromHeightCumulativeFull, LazyFromHeightLast, SatsToBitcoin, SatsToDollars,
WindowStarts,
},
internal::{ByUnit, RollingFullByUnit, SatsToDollars, WindowStarts},
prices,
};
#[derive(Traversable)]
pub struct ValueFromHeightFull<M: StorageMode = Rw> {
pub sats: ComputedFromHeightCumulativeFull<Sats, M>,
pub btc: LazyFromHeightLast<Bitcoin, Sats>,
pub usd: ComputedFromHeightCumulativeFull<Dollars, M>,
pub base: ByUnit<M>,
pub cumulative: ByUnit<M>,
#[traversable(flatten)]
pub rolling: RollingFullByUnit<M>,
}
const VERSION: Version = Version::TWO; // Bumped for stored height dollars
const VERSION: Version = Version::TWO;
impl ValueFromHeightFull {
pub(crate) fn forced_import(
@@ -35,19 +28,11 @@ impl ValueFromHeightFull {
) -> Result<Self> {
let v = version + VERSION;
let sats = ComputedFromHeightCumulativeFull::forced_import(db, name, v, indexes)?;
let btc = LazyFromHeightLast::from_height_source::<SatsToBitcoin>(
&format!("{name}_btc"),
v,
sats.height.read_only_boxed_clone(),
indexes,
);
let usd =
ComputedFromHeightCumulativeFull::forced_import(db, &format!("{name}_usd"), v, indexes)?;
Ok(Self { sats, btc, usd })
Ok(Self {
base: ByUnit::forced_import(db, name, v, indexes)?,
cumulative: ByUnit::forced_import(db, &format!("{name}_cumulative"), v, indexes)?,
rolling: RollingFullByUnit::forced_import(db, name, v, indexes)?,
})
}
pub(crate) fn compute(
@@ -58,15 +43,36 @@ impl ValueFromHeightFull {
exit: &Exit,
compute_sats: impl FnOnce(&mut EagerVec<PcoVec<Height, Sats>>) -> Result<()>,
) -> Result<()> {
self.sats.compute(max_from, windows, exit, compute_sats)?;
compute_sats(&mut self.base.sats.height)?;
self.usd.compute(max_from, windows, exit, |vec| {
Ok(vec.compute_binary::<Sats, Dollars, SatsToDollars>(
self.cumulative
.sats
.height
.compute_cumulative(max_from, &self.base.sats.height, exit)?;
self.base
.usd
.height
.compute_binary::<Sats, Dollars, SatsToDollars>(
max_from,
&self.sats.height,
&self.base.sats.height,
&prices.price.usd,
exit,
)?)
})
)?;
self.cumulative
.usd
.height
.compute_cumulative(max_from, &self.base.usd.height, exit)?;
self.rolling.compute(
max_from,
windows,
&self.base.sats.height,
&self.base.usd.height,
exit,
)?;
Ok(())
}
}

View File

@@ -1,26 +1,23 @@
//! Value type for Last pattern from Height.
//!
//! Height-level USD value is stored (eagerly computed from sats × price).
//! Day1 last is stored since it requires finding the last value within each date.
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Bitcoin, Dollars, Height, Sats, Version};
use vecdb::{Database, Exit, ReadableCloneableVec, Rw, StorageMode};
use brk_types::{Dollars, Height, Sats, Version};
use derive_more::{Deref, DerefMut};
use vecdb::{Database, Exit, Rw, StorageMode};
use crate::{
indexes, prices,
internal::{ComputedFromHeightLast, LazyFromHeightLast, SatsToBitcoin, SatsToDollars},
internal::{ByUnit, SatsToDollars},
};
#[derive(Traversable)]
#[derive(Deref, DerefMut, Traversable)]
#[traversable(transparent)]
pub struct ValueFromHeightLast<M: StorageMode = Rw> {
pub sats: ComputedFromHeightLast<Sats, M>,
pub btc: LazyFromHeightLast<Bitcoin, Sats>,
pub usd: ComputedFromHeightLast<Dollars, M>,
#[deref]
#[deref_mut]
pub base: ByUnit<M>,
}
const VERSION: Version = Version::TWO; // Bumped for stored height dollars
const VERSION: Version = Version::TWO;
impl ValueFromHeightLast {
pub(crate) fn forced_import(
@@ -30,35 +27,20 @@ impl ValueFromHeightLast {
indexes: &indexes::Vecs,
) -> Result<Self> {
let v = version + VERSION;
let sats = ComputedFromHeightLast::forced_import(db, name, v, indexes)?;
let btc = LazyFromHeightLast::from_computed::<SatsToBitcoin>(
&format!("{name}_btc"),
v,
sats.height.read_only_boxed_clone(),
&sats,
);
let usd = ComputedFromHeightLast::forced_import(db, &format!("{name}_usd"), v, indexes)?;
Ok(Self {
sats,
btc,
usd,
base: ByUnit::forced_import(db, name, v, indexes)?,
})
}
/// Eagerly compute USD height values: sats[h] * price[h].
pub(crate) fn compute(
&mut self,
prices: &prices::Vecs,
max_from: Height,
exit: &Exit,
) -> Result<()> {
self.usd.compute_binary::<Sats, Dollars, SatsToDollars>(
self.base.usd.compute_binary::<Sats, Dollars, SatsToDollars>(
max_from,
&self.sats.height,
&self.base.sats.height,
&prices.price.usd,
exit,
)?;

View File

@@ -21,6 +21,7 @@ pub struct ValueFromHeightLastRolling<M: StorageMode = Rw> {
#[deref_mut]
#[traversable(flatten)]
pub value: ValueFromHeight<M>,
#[traversable(flatten)]
pub rolling: StoredValueRollingWindows<M>,
}

View File

@@ -1,32 +1,21 @@
//! Value type with stored sats height + cumulative, stored usd, lazy btc.
//!
//! - Sats: stored height + cumulative (ComputedFromHeightCumulative)
//! - BTC: lazy transform from sats (LazyFromHeightLast)
//! - USD: stored (eagerly computed from price × sats)
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Bitcoin, Dollars, Height, Sats, Version};
use vecdb::{Database, Exit, ReadableCloneableVec, Rw, StorageMode};
use brk_types::{Dollars, Height, Sats, Version};
use vecdb::{Database, Exit, Rw, StorageMode};
use crate::{
indexes,
internal::{
ComputedFromHeightCumulative, ComputedFromHeightLast, LazyFromHeightLast, SatsToBitcoin,
SatsToDollars,
},
internal::{ByUnit, SatsToDollars},
prices,
};
/// Value wrapper with stored sats height + cumulative, lazy btc + stored usd.
#[derive(Traversable)]
pub struct LazyComputedValueFromHeightCumulative<M: StorageMode = Rw> {
pub sats: ComputedFromHeightCumulative<Sats, M>,
pub btc: LazyFromHeightLast<Bitcoin, Sats>,
pub usd: ComputedFromHeightLast<Dollars, M>,
pub base: ByUnit<M>,
pub cumulative: ByUnit<M>,
}
const VERSION: Version = Version::ONE; // Bumped for stored height dollars
const VERSION: Version = Version::ONE;
impl LazyComputedValueFromHeightCumulative {
pub(crate) fn forced_import(
@@ -37,35 +26,37 @@ impl LazyComputedValueFromHeightCumulative {
) -> Result<Self> {
let v = version + VERSION;
let sats = ComputedFromHeightCumulative::forced_import(db, name, v, indexes)?;
let btc = LazyFromHeightLast::from_height_source::<SatsToBitcoin>(
&format!("{name}_btc"),
v,
sats.height.read_only_boxed_clone(),
indexes,
);
let usd = ComputedFromHeightLast::forced_import(db, &format!("{name}_usd"), v, indexes)?;
Ok(Self { sats, btc, usd })
Ok(Self {
base: ByUnit::forced_import(db, name, v, indexes)?,
cumulative: ByUnit::forced_import(db, &format!("{name}_cumulative"), v, indexes)?,
})
}
/// Compute cumulative + USD from already-filled sats height vec.
pub(crate) fn compute(
&mut self,
prices: &prices::Vecs,
max_from: Height,
exit: &Exit,
) -> Result<()> {
self.sats.compute_rest(max_from, exit)?;
self.cumulative
.sats
.height
.compute_cumulative(max_from, &self.base.sats.height, exit)?;
self.base
.usd
.compute_binary::<Sats, Dollars, SatsToDollars>(
max_from,
&self.base.sats.height,
&prices.price.usd,
exit,
)?;
self.cumulative
.usd
.height
.compute_cumulative(max_from, &self.base.usd.height, exit)?;
self.usd.compute_binary::<Sats, Dollars, SatsToDollars>(
max_from,
&self.sats.height,
&prices.price.usd,
exit,
)?;
Ok(())
}
}

View File

@@ -1,29 +1,22 @@
//! Value type for SumCumulative pattern from Height.
//!
//! Height-level USD sum is stored (eagerly computed from sats × price).
//! Uses CumSum: stored base + cumulative + rolling sum windows.
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Bitcoin, Dollars, Height, Sats, Version};
use vecdb::{Database, EagerVec, Exit, PcoVec, ReadableCloneableVec, Rw, StorageMode};
use brk_types::{Dollars, Height, Sats, Version};
use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
use crate::{
indexes, prices,
internal::{
ComputedFromHeightCumulativeSum, LazyFromHeightLast, SatsToBitcoin, SatsToDollars,
WindowStarts,
},
indexes,
internal::{ByUnit, RollingSumByUnit, SatsToDollars, WindowStarts},
prices,
};
#[derive(Traversable)]
pub struct ValueFromHeightSumCumulative<M: StorageMode = Rw> {
pub sats: ComputedFromHeightCumulativeSum<Sats, M>,
pub btc: LazyFromHeightLast<Bitcoin, Sats>,
pub usd: ComputedFromHeightCumulativeSum<Dollars, M>,
pub base: ByUnit<M>,
pub cumulative: ByUnit<M>,
pub sum: RollingSumByUnit<M>,
}
const VERSION: Version = Version::TWO; // Bumped for stored height dollars
const VERSION: Version = Version::TWO;
impl ValueFromHeightSumCumulative {
pub(crate) fn forced_import(
@@ -34,19 +27,11 @@ impl ValueFromHeightSumCumulative {
) -> Result<Self> {
let v = version + VERSION;
let sats = ComputedFromHeightCumulativeSum::forced_import(db, name, v, indexes)?;
let btc = LazyFromHeightLast::from_height_source::<SatsToBitcoin>(
&format!("{name}_btc"),
v,
sats.height.read_only_boxed_clone(),
indexes,
);
let usd =
ComputedFromHeightCumulativeSum::forced_import(db, &format!("{name}_usd"), v, indexes)?;
Ok(Self { sats, btc, usd })
Ok(Self {
base: ByUnit::forced_import(db, name, v, indexes)?,
cumulative: ByUnit::forced_import(db, &format!("{name}_cumulative"), v, indexes)?,
sum: RollingSumByUnit::forced_import(db, name, v, indexes)?,
})
}
pub(crate) fn compute(
@@ -57,15 +42,36 @@ impl ValueFromHeightSumCumulative {
exit: &Exit,
compute_sats: impl FnOnce(&mut EagerVec<PcoVec<Height, Sats>>) -> Result<()>,
) -> Result<()> {
self.sats.compute(max_from, windows, exit, compute_sats)?;
compute_sats(&mut self.base.sats.height)?;
self.usd.compute(max_from, windows, exit, |vec| {
Ok(vec.compute_binary::<Sats, Dollars, SatsToDollars>(
self.cumulative
.sats
.height
.compute_cumulative(max_from, &self.base.sats.height, exit)?;
self.base
.usd
.height
.compute_binary::<Sats, Dollars, SatsToDollars>(
max_from,
&self.sats.height,
&self.base.sats.height,
&prices.price.usd,
exit,
)?)
})
)?;
self.cumulative
.usd
.height
.compute_cumulative(max_from, &self.base.usd.height, exit)?;
self.sum.compute_rolling_sum(
max_from,
windows,
&self.base.sats.height,
&self.base.usd.height,
exit,
)?;
Ok(())
}
}

View File

@@ -2,12 +2,14 @@
use brk_traversable::Traversable;
use brk_types::{
Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour4, Hour12, Minute1, Minute5,
Minute10, Minute30, Month1, Month3, Month6, Version, Week1, Year1, Year10,
Day1, Day3, DifficultyEpoch, FromCoarserIndex, HalvingEpoch, Height, Hour1, Hour4, Hour12,
Minute1, Minute5, Minute10, Minute30, Month1, Month3, Month6, Version, Week1, Year1, Year10,
};
use derive_more::{Deref, DerefMut};
use schemars::JsonSchema;
use vecdb::{LazyAggVec, ReadOnlyClone, ReadableBoxedVec, ReadableCloneableVec};
use vecdb::{
Cursor, LazyAggVec, ReadOnlyClone, ReadableBoxedVec, ReadableCloneableVec, VecIndex, VecValue,
};
use crate::{
indexes, indexes_from,
@@ -77,13 +79,45 @@ where
};
}
fn for_each_range_from_coarser<
I: VecIndex,
O: VecValue,
S1I: VecIndex + FromCoarserIndex<I>,
S2T: VecValue,
>(
from: usize,
to: usize,
source: &ReadableBoxedVec<S1I, O>,
mapping: &ReadableBoxedVec<I, S2T>,
f: &mut dyn FnMut(O),
) {
let mapping_len = mapping.len();
let source_len = source.len();
let mut cursor = Cursor::from_dyn(&**source);
for i in from..to {
if i >= mapping_len {
break;
}
let target = S1I::max_from(I::from(i), source_len);
if cursor.position() <= target {
cursor.advance(target - cursor.position());
if let Some(v) = cursor.next() {
f(v);
}
} else if let Some(v) = source.collect_one_at(target) {
f(v);
}
}
}
macro_rules! epoch {
($idx:ident) => {
LazyAggVec::from_source(
LazyAggVec::new(
name,
v,
height_source.clone(),
indexes.$idx.identity.read_only_boxed_clone(),
for_each_range_from_coarser,
)
};
}

View File

@@ -1,29 +1,25 @@
use brk_error::Result;
use brk_traversable::Traversable;
use schemars::JsonSchema;
use vecdb::{
Database, Exit, ReadableVec, Ro, Rw, StorageMode, VecIndex, VecValue, Version,
Database, Exit, ReadableVec, Ro, Rw, VecIndex, VecValue, Version,
};
use crate::internal::{
AverageVec, ComputedVecValue, MaxVec, MedianVec, MinVec, Pct10Vec, Pct25Vec, Pct75Vec,
Pct90Vec,
AverageVec, ComputedVecValue, DistributionStats, MaxVec, MedianVec, MinVec, Pct10Vec,
Pct25Vec, Pct75Vec, Pct90Vec,
};
/// Distribution stats (average + min + max + percentiles) — flat 8-field struct.
#[derive(Traversable)]
pub struct Distribution<I: VecIndex, T: ComputedVecValue + JsonSchema, M: StorageMode = Rw> {
pub average: AverageVec<I, T, M>,
#[traversable(flatten)]
pub min: MinVec<I, T, M>,
#[traversable(flatten)]
pub max: MaxVec<I, T, M>,
pub pct10: Pct10Vec<I, T, M>,
pub pct25: Pct25Vec<I, T, M>,
pub median: MedianVec<I, T, M>,
pub pct75: Pct75Vec<I, T, M>,
pub pct90: Pct90Vec<I, T, M>,
}
/// Distribution stats (average + min + max + percentiles) — concrete vec type alias.
pub type Distribution<I, T, M = Rw> = DistributionStats<
AverageVec<I, T, M>,
MinVec<I, T, M>,
MaxVec<I, T, M>,
Pct10Vec<I, T, M>,
Pct25Vec<I, T, M>,
MedianVec<I, T, M>,
Pct75Vec<I, T, M>,
Pct90Vec<I, T, M>,
>;
impl<I: VecIndex, T: ComputedVecValue + JsonSchema> Distribution<I, T> {
pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result<Self> {
@@ -111,7 +107,7 @@ impl<I: VecIndex, T: ComputedVecValue + JsonSchema> Distribution<I, T> {
}
pub fn read_only_clone(&self) -> Distribution<I, T, Ro> {
Distribution {
DistributionStats {
average: self.average.read_only_clone(),
min: self.min.read_only_clone(),
max: self.max.read_only_clone(),

View File

@@ -41,11 +41,11 @@ where
average: RollingWindows::forced_import(db, &format!("{name}_average"), v, indexes)?,
min: RollingWindows::forced_import(db, &format!("{name}_min"), v, indexes)?,
max: RollingWindows::forced_import(db, &format!("{name}_max"), v, indexes)?,
p10: RollingWindows::forced_import(db, &format!("{name}_p10"), v, indexes)?,
p25: RollingWindows::forced_import(db, &format!("{name}_p25"), v, indexes)?,
pct10: RollingWindows::forced_import(db, &format!("{name}_p10"), v, indexes)?,
pct25: RollingWindows::forced_import(db, &format!("{name}_p25"), v, indexes)?,
median: RollingWindows::forced_import(db, &format!("{name}_median"), v, indexes)?,
p75: RollingWindows::forced_import(db, &format!("{name}_p75"), v, indexes)?,
p90: RollingWindows::forced_import(db, &format!("{name}_p90"), v, indexes)?,
pct75: RollingWindows::forced_import(db, &format!("{name}_p75"), v, indexes)?,
pct90: RollingWindows::forced_import(db, &format!("{name}_p90"), v, indexes)?,
}))
}
@@ -69,30 +69,30 @@ where
compute_rolling_distribution_from_starts(
max_from, windows._24h, source,
&mut self.0.average._24h.height, &mut self.0.min._24h.height,
&mut self.0.max._24h.height, &mut self.0.p10._24h.height,
&mut self.0.p25._24h.height, &mut self.0.median._24h.height,
&mut self.0.p75._24h.height, &mut self.0.p90._24h.height, exit,
&mut self.0.max._24h.height, &mut self.0.pct10._24h.height,
&mut self.0.pct25._24h.height, &mut self.0.median._24h.height,
&mut self.0.pct75._24h.height, &mut self.0.pct90._24h.height, exit,
)?;
compute_rolling_distribution_from_starts(
max_from, windows._7d, source,
&mut self.0.average._7d.height, &mut self.0.min._7d.height,
&mut self.0.max._7d.height, &mut self.0.p10._7d.height,
&mut self.0.p25._7d.height, &mut self.0.median._7d.height,
&mut self.0.p75._7d.height, &mut self.0.p90._7d.height, exit,
&mut self.0.max._7d.height, &mut self.0.pct10._7d.height,
&mut self.0.pct25._7d.height, &mut self.0.median._7d.height,
&mut self.0.pct75._7d.height, &mut self.0.pct90._7d.height, exit,
)?;
compute_rolling_distribution_from_starts(
max_from, windows._30d, source,
&mut self.0.average._30d.height, &mut self.0.min._30d.height,
&mut self.0.max._30d.height, &mut self.0.p10._30d.height,
&mut self.0.p25._30d.height, &mut self.0.median._30d.height,
&mut self.0.p75._30d.height, &mut self.0.p90._30d.height, exit,
&mut self.0.max._30d.height, &mut self.0.pct10._30d.height,
&mut self.0.pct25._30d.height, &mut self.0.median._30d.height,
&mut self.0.pct75._30d.height, &mut self.0.pct90._30d.height, exit,
)?;
compute_rolling_distribution_from_starts(
max_from, windows._1y, source,
&mut self.0.average._1y.height, &mut self.0.min._1y.height,
&mut self.0.max._1y.height, &mut self.0.p10._1y.height,
&mut self.0.p25._1y.height, &mut self.0.median._1y.height,
&mut self.0.p75._1y.height, &mut self.0.p90._1y.height, exit,
&mut self.0.max._1y.height, &mut self.0.pct10._1y.height,
&mut self.0.pct25._1y.height, &mut self.0.median._1y.height,
&mut self.0.pct75._1y.height, &mut self.0.pct90._1y.height, exit,
)?;
Ok(())

View File

@@ -1,15 +1,14 @@
mod block_count_target;
mod cents_to_dollars;
mod cents_to_sats;
mod ohlc_cents_to_dollars;
mod ohlc_cents_to_sats;
mod dollar_halve;
mod dollar_identity;
mod dollar_plus;
mod dollar_times_tenths;
mod dollars_to_sats_fract;
mod f32_identity;
mod ohlc_cents_to_dollars;
mod ohlc_cents_to_sats;
mod percentage_diff_close_dollars;
mod percentage_dollars_f32;
mod percentage_dollars_f32_neg;
@@ -18,7 +17,7 @@ mod percentage_u32_f32;
mod price_times_ratio;
mod ratio32;
mod ratio64;
mod ratio_u64_f32;
mod return_f32_tenths;
mod return_i8;
mod return_u16;
@@ -52,13 +51,12 @@ pub use percentage_dollars_f32_neg::*;
pub use percentage_sats_f64::*;
pub use percentage_u32_f32::*;
pub use price_times_ratio::*;
pub use ratio32::*;
pub use ratio64::*;
pub use ratio_u64_f32::*;
pub use return_f32_tenths::*;
pub use return_i8::*;
pub use return_u16::*;
pub use sat_halve::*;
pub use sat_halve_to_bitcoin::*;
pub use sat_identity::*;

View File

@@ -0,0 +1,17 @@
use brk_types::{StoredF32, StoredU64};
use vecdb::BinaryTransform;
/// (StoredU64, StoredU64) -> StoredF32 ratio (a/b)
/// Used for adoption ratio calculations (script_count / total_outputs)
pub struct RatioU64F32;
impl BinaryTransform<StoredU64, StoredU64, StoredF32> for RatioU64F32 {
#[inline(always)]
fn apply(numerator: StoredU64, denominator: StoredU64) -> StoredF32 {
if *denominator > 0 {
StoredF32::from(*numerator as f64 / *denominator as f64)
} else {
StoredF32::from(0.0)
}
}
}

View File

@@ -27,7 +27,7 @@ impl Vecs {
) -> Result<()> {
self.puell_multiple.height.compute_divide(
starting_indexes.height,
&rewards.coinbase.usd.height,
&rewards.coinbase.base.usd.height,
&rewards.subsidy_usd_1y_sma.height,
exit,
)?;

View File

@@ -31,8 +31,8 @@ impl Vecs {
self.hashrate.compute(
&blocks.count,
&blocks.difficulty,
&self.rewards.coinbase.sats.rolling.sum._24h.height,
&self.rewards.coinbase.usd.rolling.sum._24h.height,
&self.rewards.coinbase.rolling._24h.sum.sats.height,
&self.rewards.coinbase.rolling._24h.sum.usd.height,
starting_indexes,
exit,
)?;

View File

@@ -84,8 +84,8 @@ impl Vecs {
|vec| {
vec.compute_transform2(
starting_indexes.height,
&self.coinbase.sats.height,
&self.fees.sats.height,
&self.coinbase.base.sats.height,
&self.fees.base.sats.height,
|(height, coinbase, fees, ..)| {
(
height,
@@ -109,7 +109,7 @@ impl Vecs {
|vec| {
vec.compute_transform(
starting_indexes.height,
&self.subsidy.sats.height,
&self.subsidy.base.sats.height,
|(height, subsidy, ..)| {
let halving = HalvingEpoch::from(height);
let expected = Sats::FIFTY_BTC / 2_usize.pow(halving.to_usize() as u32);
@@ -124,42 +124,42 @@ impl Vecs {
// All-time cumulative fee dominance
self.fee_dominance.height.compute_percentage(
starting_indexes.height,
&self.fees.sats.cumulative.height,
&self.coinbase.sats.cumulative.height,
&self.fees.cumulative.sats.height,
&self.coinbase.cumulative.sats.height,
exit,
)?;
// Rolling fee dominance = sum(fees) / sum(coinbase) * 100
self.fee_dominance_24h.height.compute_percentage(
starting_indexes.height,
&self.fees.sats.rolling.sum._24h.height,
&self.coinbase.sats.rolling.sum._24h.height,
&self.fees.rolling._24h.sum.sats.height,
&self.coinbase.rolling._24h.sum.sats.height,
exit,
)?;
self.fee_dominance_7d.height.compute_percentage(
starting_indexes.height,
&self.fees.sats.rolling.sum._7d.height,
&self.coinbase.sats.rolling.sum._7d.height,
&self.fees.rolling._7d.sum.sats.height,
&self.coinbase.rolling._7d.sum.sats.height,
exit,
)?;
self.fee_dominance_30d.height.compute_percentage(
starting_indexes.height,
&self.fees.sats.rolling.sum._30d.height,
&self.coinbase.sats.rolling.sum._30d.height,
&self.fees.rolling._30d.sum.sats.height,
&self.coinbase.rolling._30d.sum.sats.height,
exit,
)?;
self.fee_dominance_1y.height.compute_percentage(
starting_indexes.height,
&self.fees.sats.rolling.sum._1y.height,
&self.coinbase.sats.rolling.sum._1y.height,
&self.fees.rolling._1y.sum.sats.height,
&self.coinbase.rolling._1y.sum.sats.height,
exit,
)?;
// All-time cumulative subsidy dominance
self.subsidy_dominance.height.compute_percentage(
starting_indexes.height,
&self.subsidy.sats.cumulative.height,
&self.coinbase.sats.cumulative.height,
&self.subsidy.cumulative.sats.height,
&self.coinbase.cumulative.sats.height,
exit,
)?;
@@ -193,7 +193,7 @@ impl Vecs {
self.subsidy_usd_1y_sma.height.compute_rolling_average(
starting_indexes.height,
&count_vecs.height_1y_ago,
&self.coinbase.usd.height,
&self.coinbase.base.usd.height,
exit,
)?;

View File

@@ -237,7 +237,7 @@ impl Vecs {
Ok(vec.compute_transform2(
starting_indexes.height,
&self.blocks_mined.height,
&mining.rewards.subsidy.sats.height,
&mining.rewards.subsidy.base.sats.height,
|(h, mask, val, ..)| (h, MaskSats::apply(mask, val)),
exit,
)?)
@@ -253,7 +253,7 @@ impl Vecs {
Ok(vec.compute_transform2(
starting_indexes.height,
&self.blocks_mined.height,
&mining.rewards.fees.sats.height,
&mining.rewards.fees.base.sats.height,
|(h, mask, val, ..)| (h, MaskSats::apply(mask, val)),
exit,
)?)
@@ -269,7 +269,7 @@ impl Vecs {
Ok(vec.compute_transform2(
starting_indexes.height,
&self.blocks_mined.height,
&mining.rewards.coinbase.sats.height,
&mining.rewards.coinbase.base.sats.height,
|(h, mask, val, ..)| (h, MaskSats::apply(mask, val)),
exit,
)?)

View File

@@ -0,0 +1,56 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{StoredF32, Version};
use vecdb::{Database, Exit, Rw, StorageMode};
use crate::{ComputeIndexes, indexes, internal::{ComputedFromHeightLast, RatioU64F32}, outputs};
use super::count::Vecs as CountVecs;
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
pub taproot: ComputedFromHeightLast<StoredF32, M>,
pub segwit: ComputedFromHeightLast<StoredF32, M>,
}
impl Vecs {
pub(crate) fn forced_import(
db: &Database,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
Ok(Self {
taproot: ComputedFromHeightLast::forced_import(
db,
"taproot_adoption",
version,
indexes,
)?,
segwit: ComputedFromHeightLast::forced_import(db, "segwit_adoption", version, indexes)?,
})
}
pub(crate) fn compute(
&mut self,
count: &CountVecs,
outputs_count: &outputs::CountVecs,
starting_indexes: &ComputeIndexes,
exit: &Exit,
) -> Result<()> {
self.taproot.compute_binary::<_, _, RatioU64F32>(
starting_indexes.height,
&count.p2tr.height,
&outputs_count.total_count.full.sum_cumulative.sum.0,
exit,
)?;
self.segwit.compute_binary::<_, _, RatioU64F32>(
starting_indexes.height,
&count.segwit.height,
&outputs_count.total_count.full.sum_cumulative.sum.0,
exit,
)?;
Ok(())
}
}

View File

@@ -17,11 +17,14 @@ impl Vecs {
exit: &Exit,
) -> Result<()> {
self.count
.compute(indexer, &blocks.count, &outputs.count, starting_indexes, exit)?;
.compute(indexer, &blocks.count, starting_indexes, exit)?;
self.value
.compute(indexer, &blocks.count, prices, starting_indexes, exit)?;
self.adoption
.compute(&self.count, &outputs.count, starting_indexes, exit)?;
let _lock = exit.lock();
self.db.compact()?;
Ok(())

View File

@@ -1,18 +1,16 @@
use brk_error::Result;
use brk_indexer::Indexer;
use brk_types::StoredF32;
use brk_types::StoredU64;
use vecdb::Exit;
use super::Vecs;
use crate::{ComputeIndexes, blocks, outputs};
use crate::{ComputeIndexes, blocks};
impl Vecs {
pub(crate) fn compute(
&mut self,
indexer: &Indexer,
count_vecs: &blocks::CountVecs,
outputs_count: &outputs::CountVecs,
starting_indexes: &ComputeIndexes,
exit: &Exit,
) -> Result<()> {
@@ -151,37 +149,6 @@ impl Vecs {
)?)
})?;
// Adoption ratios: per-block ratio of script type / total outputs
self.taproot_adoption.height.compute_transform2(
starting_indexes.height,
&self.p2tr.height,
&outputs_count.total_count.full.sum_cumulative.sum.0,
|(h, p2tr, total, ..)| {
let ratio = if *total > 0 {
StoredF32::from(*p2tr as f64 / *total as f64)
} else {
StoredF32::from(0.0)
};
(h, ratio)
},
exit,
)?;
self.segwit_adoption.height.compute_transform2(
starting_indexes.height,
&self.segwit.height,
&outputs_count.total_count.full.sum_cumulative.sum.0,
|(h, segwit, total, ..)| {
let ratio = if *total > 0 {
StoredF32::from(*segwit as f64 / *total as f64)
} else {
StoredF32::from(0.0)
};
(h, ratio)
},
exit,
)?;
Ok(())
}
}

View File

@@ -3,10 +3,7 @@ use brk_types::Version;
use vecdb::Database;
use super::Vecs;
use crate::{
indexes,
internal::{ComputedFromHeightCumulativeSum, ComputedFromHeightLast},
};
use crate::{indexes, internal::ComputedFromHeightCumulativeSum};
impl Vecs {
pub(crate) fn forced_import(
@@ -58,18 +55,6 @@ impl Vecs {
indexes,
)?,
segwit,
taproot_adoption: ComputedFromHeightLast::forced_import(
db,
"taproot_adoption",
version,
indexes,
)?,
segwit_adoption: ComputedFromHeightLast::forced_import(
db,
"segwit_adoption",
version,
indexes,
)?,
})
}
}

View File

@@ -1,8 +1,8 @@
use brk_traversable::Traversable;
use brk_types::{StoredF32, StoredU64};
use brk_types::StoredU64;
use vecdb::{Rw, StorageMode};
use crate::internal::{ComputedFromHeightCumulativeSum, ComputedFromHeightLast};
use crate::internal::ComputedFromHeightCumulativeSum;
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
@@ -23,8 +23,4 @@ pub struct Vecs<M: StorageMode = Rw> {
// Aggregate counts
/// SegWit output count (p2wpkh + p2wsh + p2tr)
pub segwit: ComputedFromHeightCumulativeSum<StoredU64, M>,
// Adoption ratios (stored per-block, lazy period views)
pub taproot_adoption: ComputedFromHeightLast<StoredF32, M>,
pub segwit_adoption: ComputedFromHeightLast<StoredF32, M>,
}

View File

@@ -7,7 +7,7 @@ use vecdb::{Database, PAGE_SIZE};
use crate::indexes;
use super::{CountVecs, ValueVecs, Vecs};
use super::{AdoptionVecs, CountVecs, ValueVecs, Vecs};
impl Vecs {
pub(crate) fn forced_import(
@@ -22,8 +22,9 @@ impl Vecs {
let count = CountVecs::forced_import(&db, version, indexes)?;
let value = ValueVecs::forced_import(&db, version, indexes)?;
let adoption = AdoptionVecs::forced_import(&db, version, indexes)?;
let this = Self { db, count, value };
let this = Self { db, count, value, adoption };
this.db.retain_regions(
this.iter_any_exportable()

View File

@@ -1,3 +1,4 @@
pub mod adoption;
pub mod count;
pub mod value;
@@ -7,6 +8,7 @@ mod import;
use brk_traversable::Traversable;
use vecdb::{Database, Rw, StorageMode};
pub use adoption::Vecs as AdoptionVecs;
pub use count::Vecs as CountVecs;
pub use value::Vecs as ValueVecs;
@@ -19,4 +21,5 @@ pub struct Vecs<M: StorageMode = Rw> {
pub count: CountVecs<M>,
pub value: ValueVecs<M>,
pub adoption: AdoptionVecs<M>,
}

View File

@@ -22,11 +22,11 @@ impl Vecs {
.compute(starting_indexes.height, &window_starts, prices, exit, |height_vec| {
// Validate computed versions against dependencies
let opreturn_dep_version = scripts.value.opreturn.sats.height.version();
let opreturn_dep_version = scripts.value.opreturn.base.sats.height.version();
height_vec.validate_computed_version_or_reset(opreturn_dep_version)?;
// Copy per-block opreturn values from scripts
let scripts_target = scripts.value.opreturn.sats.height.len();
let scripts_target = scripts.value.opreturn.base.sats.height.len();
if scripts_target > 0 {
let target_height = Height::from(scripts_target - 1);
let current_len = height_vec.len();
@@ -36,7 +36,7 @@ impl Vecs {
if starting_height <= target_height {
let start = starting_height.to_usize();
let end = target_height.to_usize() + 1;
scripts.value.opreturn.sats.height.fold_range_at(
scripts.value.opreturn.base.sats.height.fold_range_at(
start, end, start,
|idx, value| {
height_vec.truncate_push(Height::from(idx), value).unwrap();
@@ -52,8 +52,8 @@ impl Vecs {
// 2. Compute unspendable supply = opreturn + unclaimed_rewards + genesis (at height 0)
// Get reference to opreturn height vec for computing unspendable
let opreturn_height = &self.opreturn.sats.height;
let unclaimed_height = &mining.rewards.unclaimed_rewards.sats.height;
let opreturn_height = &self.opreturn.base.sats.height;
let unclaimed_height = &mining.rewards.unclaimed_rewards.base.sats.height;
self.unspendable
.compute(starting_indexes.height, &window_starts, prices, exit, |height_vec| {