mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-20 07:28:11 -07:00
global: big snapshot part 2
This commit is contained in:
@@ -4,10 +4,10 @@ use derive_more::{Deref, DerefMut};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{LazyRollingDeltasFromHeight, WindowStartVec, Windows},
|
||||
internal::{LazyRollingDeltasFromHeight, WindowStartVec, Windows, WithAddrTypes},
|
||||
};
|
||||
|
||||
use super::{AddrCountsVecs, WithAddrTypes};
|
||||
use super::AddrCountsVecs;
|
||||
|
||||
type AddrDelta = LazyRollingDeltasFromHeight<StoredU64, StoredI64, BasisPointsSigned32>;
|
||||
|
||||
|
||||
@@ -5,9 +5,8 @@ use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Database, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
distribution::addr::WithAddrTypes,
|
||||
indexes,
|
||||
internal::PerBlock,
|
||||
internal::{PerBlock, WithAddrTypes},
|
||||
};
|
||||
|
||||
/// Exposed address count (`all` + per-type) for a single variant (funded or total).
|
||||
|
||||
@@ -45,7 +45,7 @@ use brk_types::{Indexes, Version};
|
||||
use rayon::prelude::*;
|
||||
use vecdb::{AnyStoredVec, Database, Exit, Rw, StorageMode};
|
||||
|
||||
use crate::indexes;
|
||||
use crate::{indexes, prices};
|
||||
|
||||
/// Top-level container for all exposed address tracking: counts (funded +
|
||||
/// total) plus the funded supply.
|
||||
@@ -87,9 +87,15 @@ impl ExposedAddrVecs {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rest(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
|
||||
pub(crate) fn compute_rest(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
prices: &prices::Vecs,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.count.compute_rest(starting_indexes, exit)?;
|
||||
self.supply.compute_rest(starting_indexes, exit)?;
|
||||
self.supply
|
||||
.compute_rest(starting_indexes.height, prices, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use brk_cohort::ByAddrType;
|
||||
use brk_types::{Height, Sats};
|
||||
use brk_types::Height;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::ReadableVec;
|
||||
|
||||
use crate::internal::PerBlock;
|
||||
use crate::internal::AmountPerBlock;
|
||||
|
||||
use super::vecs::ExposedAddrSupplyVecs;
|
||||
|
||||
@@ -23,8 +23,8 @@ impl From<(&ExposedAddrSupplyVecs, Height)> for AddrTypeToExposedAddrSupply {
|
||||
#[inline]
|
||||
fn from((vecs, starting_height): (&ExposedAddrSupplyVecs, Height)) -> Self {
|
||||
if let Some(prev_height) = starting_height.decremented() {
|
||||
let read = |v: &PerBlock<Sats>| -> u64 {
|
||||
u64::from(v.height.collect_one(prev_height).unwrap())
|
||||
let read = |v: &AmountPerBlock| -> u64 {
|
||||
u64::from(v.sats.height.collect_one(prev_height).unwrap())
|
||||
};
|
||||
Self(ByAddrType {
|
||||
p2pk65: read(&vecs.by_addr_type.p2pk65),
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Sats, Version};
|
||||
use brk_types::Version;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Database, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
distribution::addr::WithAddrTypes,
|
||||
indexes,
|
||||
internal::PerBlock,
|
||||
internal::{AmountPerBlock, WithAddrTypes},
|
||||
};
|
||||
|
||||
/// Exposed address supply (sats) — `all` + per-address-type. Tracks the total
|
||||
/// balance held by addresses currently in the funded exposed set.
|
||||
/// Exposed address supply (sats/btc/cents/usd) — `all` + per-address-type.
|
||||
/// Tracks the total balance held by addresses currently in the funded
|
||||
/// exposed set. Sats are pushed stateful per block; cents/usd are derived
|
||||
/// post-hoc from sats × spot price.
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct ExposedAddrSupplyVecs<M: StorageMode = Rw>(
|
||||
#[traversable(flatten)] pub WithAddrTypes<PerBlock<Sats, M>>,
|
||||
#[traversable(flatten)] pub WithAddrTypes<AmountPerBlock<M>>,
|
||||
);
|
||||
|
||||
impl ExposedAddrSupplyVecs {
|
||||
@@ -23,7 +24,7 @@ impl ExposedAddrSupplyVecs {
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self(WithAddrTypes::<PerBlock<Sats>>::forced_import(
|
||||
Ok(Self(WithAddrTypes::<AmountPerBlock>::forced_import(
|
||||
db,
|
||||
"exposed_addr_supply",
|
||||
version,
|
||||
|
||||
@@ -8,7 +8,6 @@ mod new_addr_count;
|
||||
mod reused;
|
||||
mod total_addr_count;
|
||||
mod type_map;
|
||||
mod with_addr_types;
|
||||
|
||||
pub use activity::{AddrActivityVecs, AddrTypeToActivityCounts};
|
||||
pub use addr_count::{AddrCountsVecs, AddrTypeToAddrCount};
|
||||
@@ -24,4 +23,3 @@ pub use reused::{
|
||||
};
|
||||
pub use total_addr_count::TotalAddrCountVecs;
|
||||
pub use type_map::{AddrTypeToTypeIndexMap, AddrTypeToVec, HeightToAddrTypeToVec};
|
||||
pub use with_addr_types::WithAddrTypes;
|
||||
|
||||
@@ -6,10 +6,10 @@ use vecdb::{Database, Exit, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{PerBlockCumulativeRolling, WindowStartVec, Windows},
|
||||
internal::{PerBlockCumulativeRolling, WindowStartVec, Windows, WithAddrTypes},
|
||||
};
|
||||
|
||||
use super::{TotalAddrCountVecs, WithAddrTypes};
|
||||
use super::TotalAddrCountVecs;
|
||||
|
||||
/// New address count per block (global + per-type).
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
|
||||
@@ -5,9 +5,8 @@ use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Database, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
distribution::addr::WithAddrTypes,
|
||||
indexes,
|
||||
internal::PerBlock,
|
||||
internal::{PerBlock, WithAddrTypes},
|
||||
};
|
||||
|
||||
/// Reused address count (`all` + per-type) for a single variant (funded or total).
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
//! an aggregated `all`.
|
||||
//! - [`uses`] — per-block count of outputs going to addresses that were
|
||||
//! already reused, plus the derived percent over total address-output
|
||||
//! count (denominator from `scripts::count`).
|
||||
//! count (denominator from `outputs::by_type`).
|
||||
|
||||
mod count;
|
||||
mod uses;
|
||||
@@ -27,7 +27,7 @@ use vecdb::{AnyStoredVec, Database, Exit, Rw, StorageMode};
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{WindowStartVec, Windows},
|
||||
scripts,
|
||||
outputs,
|
||||
};
|
||||
|
||||
/// Top-level container for all reused address tracking: counts (funded +
|
||||
@@ -74,12 +74,12 @@ impl ReusedAddrVecs {
|
||||
pub(crate) fn compute_rest(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
scripts_count: &scripts::CountVecs,
|
||||
outputs_by_type: &outputs::ByTypeVecs,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.count.compute_rest(starting_indexes, exit)?;
|
||||
self.uses
|
||||
.compute_rest(starting_indexes, scripts_count, exit)?;
|
||||
.compute_rest(starting_indexes, outputs_by_type, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
use brk_cohort::ByAddrType;
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{BasisPoints16, Height, Indexes, OutputType, StoredU64, Version};
|
||||
use brk_types::{BasisPoints16, Indexes, OutputType, StoredU64, Version};
|
||||
use rayon::prelude::*;
|
||||
use vecdb::{AnyStoredVec, Database, Exit, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
distribution::addr::WithAddrTypes,
|
||||
indexes,
|
||||
internal::{
|
||||
PerBlockCumulativeRolling, PercentCumulativeRolling, RatioU64Bp16, WindowStartVec, Windows,
|
||||
PerBlockCumulativeRolling, PercentCumulativeRolling, WindowStartVec, Windows,
|
||||
WithAddrTypes,
|
||||
},
|
||||
scripts,
|
||||
outputs,
|
||||
};
|
||||
|
||||
use super::state::AddrTypeToReusedAddrUseCount;
|
||||
@@ -22,8 +22,9 @@ use super::state::AddrTypeToReusedAddrUseCount;
|
||||
/// that were *already* reused at the moment of the use, so the use that
|
||||
/// makes an address reused is not itself counted.
|
||||
///
|
||||
/// The denominator for the percent (total address-output count) lives in
|
||||
/// `scripts::count` and is reused here rather than duplicated.
|
||||
/// The denominator for the percent (per-type and aggregate address-output
|
||||
/// counts) is read from `outputs::ByTypeVecs::output_count` rather than
|
||||
/// duplicated here.
|
||||
#[derive(Traversable)]
|
||||
pub struct ReusedAddrUsesVecs<M: StorageMode = Rw> {
|
||||
pub reused_addr_use_count:
|
||||
@@ -87,66 +88,29 @@ impl ReusedAddrUsesVecs {
|
||||
pub(crate) fn compute_rest(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
scripts_count: &scripts::CountVecs,
|
||||
outputs_by_type: &outputs::ByTypeVecs,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.reused_addr_use_count
|
||||
.compute_rest(starting_indexes.height, exit)?;
|
||||
|
||||
compute_one_percent(
|
||||
&mut self.reused_addr_use_percent.all,
|
||||
self.reused_addr_use_percent.all.compute_count_ratio(
|
||||
&self.reused_addr_use_count.all,
|
||||
&scripts_count.addr_output_count,
|
||||
&outputs_by_type.output_count.all,
|
||||
starting_indexes.height,
|
||||
exit,
|
||||
)?;
|
||||
for otype in OutputType::ADDR_TYPES {
|
||||
compute_one_percent(
|
||||
self.reused_addr_use_percent
|
||||
.by_addr_type
|
||||
.get_mut_unwrap(otype),
|
||||
self.reused_addr_use_count.by_addr_type.get_unwrap(otype),
|
||||
denom_for_type(scripts_count, otype),
|
||||
starting_indexes.height,
|
||||
exit,
|
||||
)?;
|
||||
self.reused_addr_use_percent
|
||||
.by_addr_type
|
||||
.get_mut_unwrap(otype)
|
||||
.compute_count_ratio(
|
||||
self.reused_addr_use_count.by_addr_type.get_unwrap(otype),
|
||||
outputs_by_type.output_count.by_type.get(otype),
|
||||
starting_indexes.height,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn compute_one_percent(
|
||||
percent: &mut PercentCumulativeRolling<BasisPoints16>,
|
||||
reused: &PerBlockCumulativeRolling<StoredU64, StoredU64>,
|
||||
denom: &PerBlockCumulativeRolling<StoredU64, StoredU64>,
|
||||
starting_height: Height,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
percent.compute_binary::<StoredU64, StoredU64, RatioU64Bp16, _, _, _, _>(
|
||||
starting_height,
|
||||
&reused.cumulative.height,
|
||||
&denom.cumulative.height,
|
||||
reused.sum.as_array().map(|w| &w.height),
|
||||
denom.sum.as_array().map(|w| &w.height),
|
||||
exit,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn denom_for_type(
|
||||
scripts_count: &scripts::CountVecs,
|
||||
otype: OutputType,
|
||||
) -> &PerBlockCumulativeRolling<StoredU64, StoredU64> {
|
||||
match otype {
|
||||
OutputType::P2PK33 => &scripts_count.p2pk33,
|
||||
OutputType::P2PK65 => &scripts_count.p2pk65,
|
||||
OutputType::P2PKH => &scripts_count.p2pkh,
|
||||
OutputType::P2SH => &scripts_count.p2sh,
|
||||
OutputType::P2WPKH => &scripts_count.p2wpkh,
|
||||
OutputType::P2WSH => &scripts_count.p2wsh,
|
||||
OutputType::P2TR => &scripts_count.p2tr,
|
||||
OutputType::P2A => &scripts_count.p2a,
|
||||
_ => unreachable!("OutputType::ADDR_TYPES contains only address types"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,12 @@ use brk_types::{Height, StoredU64, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Database, Exit, Rw, StorageMode};
|
||||
|
||||
use crate::{indexes, internal::PerBlock};
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{PerBlock, WithAddrTypes},
|
||||
};
|
||||
|
||||
use super::{AddrCountsVecs, WithAddrTypes};
|
||||
use super::AddrCountsVecs;
|
||||
|
||||
/// Total address count (global + per-type) with all derived indexes.
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
//! Generic `all` + per-`AddrType` container, mirrors the `WithSth` pattern
|
||||
//! along the address-type axis. Used by every metric that tracks one
|
||||
//! aggregate value alongside a per-address-type breakdown.
|
||||
|
||||
use brk_cohort::ByAddrType;
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Indexes, Version};
|
||||
use rayon::prelude::*;
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{AnyStoredVec, AnyVec, Database, EagerVec, Exit, PcoVec, WritableVec};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{NumericValue, PerBlock, PerBlockCumulativeRolling, WindowStartVec, Windows},
|
||||
};
|
||||
|
||||
/// `all` aggregate plus per-`AddrType` breakdown.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct WithAddrTypes<T> {
|
||||
pub all: T,
|
||||
#[traversable(flatten)]
|
||||
pub by_addr_type: ByAddrType<T>,
|
||||
}
|
||||
|
||||
impl<T> WithAddrTypes<PerBlock<T>>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let all = PerBlock::forced_import(db, name, version, indexes)?;
|
||||
let by_addr_type = ByAddrType::new_with_name(|type_name| {
|
||||
PerBlock::forced_import(db, &format!("{type_name}_{name}"), version, indexes)
|
||||
})?;
|
||||
Ok(Self { all, by_addr_type })
|
||||
}
|
||||
|
||||
pub(crate) fn min_stateful_len(&self) -> usize {
|
||||
self.by_addr_type
|
||||
.values()
|
||||
.map(|v| v.height.len())
|
||||
.min()
|
||||
.unwrap()
|
||||
.min(self.all.height.len())
|
||||
}
|
||||
|
||||
pub(crate) fn par_iter_height_mut(
|
||||
&mut self,
|
||||
) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
rayon::iter::once(&mut self.all.height as &mut dyn AnyStoredVec).chain(
|
||||
self.by_addr_type
|
||||
.par_values_mut()
|
||||
.map(|v| &mut v.height as &mut dyn AnyStoredVec),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn reset_height(&mut self) -> Result<()> {
|
||||
self.all.height.reset()?;
|
||||
for v in self.by_addr_type.values_mut() {
|
||||
v.height.reset()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn push_height<U>(&mut self, total: U, per_type: impl IntoIterator<Item = U>)
|
||||
where
|
||||
U: Into<T>,
|
||||
{
|
||||
self.all.height.push(total.into());
|
||||
for (v, value) in self.by_addr_type.values_mut().zip(per_type) {
|
||||
v.height.push(value.into());
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute `all.height` as the per-block sum of the per-type vecs.
|
||||
pub(crate) fn compute_rest(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let sources: Vec<&EagerVec<PcoVec<Height, T>>> =
|
||||
self.by_addr_type.values().map(|v| &v.height).collect();
|
||||
self.all
|
||||
.height
|
||||
.compute_sum_of_others(starting_indexes.height, &sources, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, C> WithAddrTypes<PerBlockCumulativeRolling<T, C>>
|
||||
where
|
||||
T: NumericValue + JsonSchema + Into<C>,
|
||||
C: NumericValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
cached_starts: &Windows<&WindowStartVec>,
|
||||
) -> Result<Self> {
|
||||
let all = PerBlockCumulativeRolling::forced_import(
|
||||
db,
|
||||
name,
|
||||
version,
|
||||
indexes,
|
||||
cached_starts,
|
||||
)?;
|
||||
let by_addr_type = ByAddrType::new_with_name(|type_name| {
|
||||
PerBlockCumulativeRolling::forced_import(
|
||||
db,
|
||||
&format!("{type_name}_{name}"),
|
||||
version,
|
||||
indexes,
|
||||
cached_starts,
|
||||
)
|
||||
})?;
|
||||
Ok(Self { all, by_addr_type })
|
||||
}
|
||||
|
||||
pub(crate) fn min_stateful_len(&self) -> usize {
|
||||
self.by_addr_type
|
||||
.values()
|
||||
.map(|v| v.block.len())
|
||||
.min()
|
||||
.unwrap()
|
||||
.min(self.all.block.len())
|
||||
}
|
||||
|
||||
pub(crate) fn par_iter_height_mut(
|
||||
&mut self,
|
||||
) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
rayon::iter::once(&mut self.all.block as &mut dyn AnyStoredVec).chain(
|
||||
self.by_addr_type
|
||||
.par_values_mut()
|
||||
.map(|v| &mut v.block as &mut dyn AnyStoredVec),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn reset_height(&mut self) -> Result<()> {
|
||||
self.all.block.reset()?;
|
||||
for v in self.by_addr_type.values_mut() {
|
||||
v.block.reset()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn push_height<U>(&mut self, total: U, per_type: impl IntoIterator<Item = U>)
|
||||
where
|
||||
U: Into<T>,
|
||||
{
|
||||
self.all.block.push(total.into());
|
||||
for (v, value) in self.by_addr_type.values_mut().zip(per_type) {
|
||||
v.block.push(value.into());
|
||||
}
|
||||
}
|
||||
|
||||
/// Finalize `cumulative` / `sum` / `average` for `all` and every per-type vec.
|
||||
pub(crate) fn compute_rest(&mut self, max_from: Height, exit: &Exit) -> Result<()> {
|
||||
self.all.compute_rest(max_from, exit)?;
|
||||
for v in self.by_addr_type.values_mut() {
|
||||
v.compute_rest(max_from, exit)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ use crate::{
|
||||
PerBlockCumulativeRolling, WindowStartVec, Windows,
|
||||
db_utils::{finalize_db, open_db},
|
||||
},
|
||||
outputs, prices, scripts, transactions,
|
||||
outputs, prices, transactions,
|
||||
};
|
||||
|
||||
use super::{
|
||||
@@ -235,7 +235,6 @@ impl Vecs {
|
||||
indexes: &indexes::Vecs,
|
||||
inputs: &inputs::Vecs,
|
||||
outputs: &outputs::Vecs,
|
||||
scripts: &scripts::Vecs,
|
||||
transactions: &transactions::Vecs,
|
||||
blocks: &blocks::Vecs,
|
||||
prices: &prices::Vecs,
|
||||
@@ -473,8 +472,10 @@ impl Vecs {
|
||||
self.addrs.empty.compute_rest(starting_indexes, exit)?;
|
||||
self.addrs
|
||||
.reused
|
||||
.compute_rest(starting_indexes, &scripts.count, exit)?;
|
||||
self.addrs.exposed.compute_rest(starting_indexes, exit)?;
|
||||
.compute_rest(starting_indexes, &outputs.by_type, exit)?;
|
||||
self.addrs
|
||||
.exposed
|
||||
.compute_rest(starting_indexes, prices, exit)?;
|
||||
|
||||
// 6c. Compute total_addr_count = addr_count + empty_addr_count
|
||||
self.addrs.total.compute(
|
||||
|
||||
Reference in New Issue
Block a user