global: snapshot

This commit is contained in:
nym21
2026-03-16 19:33:24 +01:00
parent 5848d25612
commit 5609e6c010
12 changed files with 132 additions and 58 deletions

View File

@@ -1,6 +1,6 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Bitcoin, Indexes, Sats, StoredF64, Version};
use brk_types::{Bitcoin, Indexes, StoredF64, Version};
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
use crate::{
@@ -11,7 +11,7 @@ use crate::{
#[derive(Traversable)]
pub struct ActivityCore<M: StorageMode = Rw> {
pub transfer_volume: PerBlockCumulativeWithSums<Sats, Sats, M>,
pub transfer_volume: AmountPerBlockCumulativeWithSums<M>,
pub coindays_destroyed: PerBlockCumulativeWithSums<StoredF64, StoredF64, M>,
#[traversable(wrap = "transfer_volume", rename = "in_profit")]
pub transfer_volume_in_profit: AmountPerBlockCumulativeWithSums<M>,
@@ -33,6 +33,7 @@ impl ActivityCore {
pub(crate) fn min_len(&self) -> usize {
self.transfer_volume
.base
.sats
.height
.len()
.min(self.coindays_destroyed.base.height.len())
@@ -45,7 +46,7 @@ impl ActivityCore {
&mut self,
state: &CohortState<impl RealizedOps, impl CostBasisOps>,
) {
self.transfer_volume.base.height.push(state.sent);
self.transfer_volume.base.sats.height.push(state.sent);
self.coindays_destroyed.base.height.push(
StoredF64::from(Bitcoin::from(state.satdays_destroyed)),
);
@@ -63,7 +64,8 @@ impl ActivityCore {
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
vec![
&mut self.transfer_volume.base.height as &mut dyn AnyStoredVec,
&mut self.transfer_volume.base.sats.height as &mut dyn AnyStoredVec,
&mut self.transfer_volume.base.cents.height,
&mut self.coindays_destroyed.base.height,
&mut self.transfer_volume_in_profit.base.sats.height,
&mut self.transfer_volume_in_profit.base.cents.height,
@@ -82,11 +84,11 @@ impl ActivityCore {
others: &[&Self],
exit: &Exit,
) -> Result<()> {
self.transfer_volume.base.height.compute_sum_of_others(
self.transfer_volume.base.sats.height.compute_sum_of_others(
starting_indexes.height,
&others
.iter()
.map(|v| &v.transfer_volume.base.height)
.map(|v| &v.transfer_volume.base.sats.height)
.collect::<Vec<_>>(),
exit,
)?;
@@ -100,11 +102,12 @@ impl ActivityCore {
pub(crate) fn compute_rest_part1(
&mut self,
prices: &prices::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.transfer_volume
.compute_rest(starting_indexes.height, exit)?;
.compute_rest(starting_indexes.height, prices, exit)?;
self.coindays_destroyed
.compute_rest(starting_indexes.height, exit)?;
Ok(())

View File

@@ -6,7 +6,10 @@ use vecdb::{AnyStoredVec, Exit, ReadableCloneableVec, Rw, StorageMode};
use crate::internal::{Identity, LazyPerBlock, PerBlock};
use crate::distribution::{metrics::ImportConfig, state::{CohortState, CostBasisOps, RealizedOps}};
use crate::{
distribution::{metrics::ImportConfig, state::{CohortState, CostBasisOps, RealizedOps}},
prices,
};
use super::ActivityCore;
@@ -71,10 +74,11 @@ impl ActivityFull {
pub(crate) fn compute_rest_part1(
&mut self,
prices: &prices::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.inner.compute_rest_part1(starting_indexes, exit)
self.inner.compute_rest_part1(prices, starting_indexes, exit)
}
pub(crate) fn compute_rest_part2(
@@ -85,7 +89,7 @@ impl ActivityFull {
self.dormancy.height.compute_transform2(
starting_indexes.height,
&self.inner.coindays_destroyed.base.height,
&self.inner.transfer_volume.base.height,
&self.inner.transfer_volume.base.sats.height,
|(i, cdd, sent_sats, ..)| {
let sent_btc = f64::from(Bitcoin::from(sent_sats));
if sent_btc == 0.0 {

View File

@@ -8,7 +8,10 @@ use brk_error::Result;
use brk_types::{Indexes, Version};
use vecdb::Exit;
use crate::distribution::state::{CohortState, CostBasisOps, RealizedOps};
use crate::{
distribution::state::{CohortState, CostBasisOps, RealizedOps},
prices,
};
pub trait ActivityLike: Send + Sync {
fn as_core(&self) -> &ActivityCore;
@@ -27,6 +30,7 @@ pub trait ActivityLike: Send + Sync {
) -> Result<()>;
fn compute_rest_part1(
&mut self,
prices: &prices::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()>;
@@ -45,8 +49,8 @@ impl ActivityLike for ActivityCore {
fn compute_from_stateful(&mut self, starting_indexes: &Indexes, others: &[&ActivityCore], exit: &Exit) -> Result<()> {
self.compute_from_stateful(starting_indexes, others, exit)
}
fn compute_rest_part1(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
self.compute_rest_part1(starting_indexes, exit)
fn compute_rest_part1(&mut self, prices: &prices::Vecs, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
self.compute_rest_part1(prices, starting_indexes, exit)
}
}
@@ -63,7 +67,7 @@ impl ActivityLike for ActivityFull {
fn compute_from_stateful(&mut self, starting_indexes: &Indexes, others: &[&ActivityCore], exit: &Exit) -> Result<()> {
self.compute_from_stateful(starting_indexes, others, exit)
}
fn compute_rest_part1(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
self.compute_rest_part1(starting_indexes, exit)
fn compute_rest_part1(&mut self, prices: &prices::Vecs, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
self.compute_rest_part1(prices, starting_indexes, exit)
}
}

View File

@@ -109,7 +109,7 @@ impl CoreCohortMetrics {
.compute(prices, starting_indexes.height, exit)?;
self.activity
.compute_rest_part1(starting_indexes, exit)?;
.compute_rest_part1(prices, starting_indexes, exit)?;
self.activity
.compute_sent_profitability(prices, starting_indexes, exit)?;

View File

@@ -223,7 +223,7 @@ pub trait CohortMetricsBase:
self.supply_mut()
.compute(prices, starting_indexes.height, exit)?;
self.activity_mut()
.compute_rest_part1(starting_indexes, exit)?;
.compute_rest_part1(prices, starting_indexes, exit)?;
self.activity_core_mut()
.compute_sent_profitability(prices, starting_indexes, exit)?;

View File

@@ -34,7 +34,6 @@ pub struct Computer<M: StorageMode = Rw> {
pub mining: Box<mining::Vecs<M>>,
pub transactions: Box<transactions::Vecs<M>>,
pub scripts: Box<scripts::Vecs<M>>,
#[traversable(hidden)]
pub positions: Box<positions::Vecs<M>>,
pub cointime: Box<cointime::Vecs<M>>,
pub constants: Box<constants::Vecs>,
@@ -493,10 +492,10 @@ impl Computer {
}
}
macro_rules! impl_iter_named_exportable {
macro_rules! impl_iter_named {
($($field:ident),+ $(,)?) => {
impl_iter_named_exportable!(@mode Ro, $($field),+);
impl_iter_named_exportable!(@mode Rw, $($field),+);
impl_iter_named!(@mode Ro, $($field),+);
impl_iter_named!(@mode Rw, $($field),+);
};
(@mode $mode:ty, $($field:ident),+) => {
impl Computer<$mode> {
@@ -507,11 +506,19 @@ macro_rules! impl_iter_named_exportable {
std::iter::empty()
$(.chain(self.$field.iter_any_exportable().map(|v| ($field::DB_NAME, v))))+
}
pub fn iter_named_visible(
&self,
) -> impl Iterator<Item = (&'static str, &dyn AnyExportableVec)> {
use brk_traversable::Traversable;
std::iter::empty()
$(.chain(self.$field.iter_any_visible().map(|v| ($field::DB_NAME, v))))+
}
}
};
}
impl_iter_named_exportable!(blocks, mining, transactions, scripts, positions, cointime,
impl_iter_named!(blocks, mining, transactions, scripts, positions, cointime,
constants, indicators, indexes, market, pools, prices, distribution, supply, inputs, outputs);
fn timed<T>(label: &str, f: impl FnOnce() -> T) -> T {

View File

@@ -25,7 +25,7 @@ impl Vecs {
)?,
unspent: PerBlock::forced_import(
db,
"exact_utxo_count",
"utxo_count_bis",
version,
indexes,
)?,

View File

@@ -16,6 +16,7 @@ use crate::internal::db_utils::{finalize_db, open_db};
pub const DB_NAME: &str = "positions";
#[derive(Traversable)]
#[traversable(hidden)]
pub struct Vecs<M: StorageMode = Rw> {
db: Database,

View File

@@ -19,7 +19,7 @@ impl Vecs {
Ok(Self {
transfer_volume: AmountPerBlockCumulativeWithSums::forced_import(
db,
"exact_transfer_volume",
"transfer_volume_bis",
version,
indexes,
cached_starts,

View File

@@ -27,18 +27,18 @@ pub struct Vecs<'a> {
impl<'a> Vecs<'a> {
pub fn build(indexer: &'a Indexer<vecdb::Ro>, computer: &'a Computer<vecdb::Ro>) -> Self {
Self::build_from(
indexer.vecs.iter_any_exportable(),
indexer.vecs.iter_any_visible(),
indexer.vecs.to_tree_node(),
computer.iter_named_exportable(),
computer.iter_named_visible(),
computer.to_tree_node(),
)
}
pub fn build_rw(indexer: &'a Indexer, computer: &'a Computer) -> Self {
Self::build_from(
indexer.vecs.iter_any_exportable(),
indexer.vecs.iter_any_visible(),
indexer.vecs.to_tree_node(),
computer.iter_named_exportable(),
computer.iter_named_visible(),
computer.to_tree_node(),
)
}

View File

@@ -264,6 +264,10 @@ impl<T: Traversable + ?Sized> Traversable for Box<T> {
fn iter_any_exportable(&self) -> impl Iterator<Item = &dyn AnyExportableVec> {
(**self).iter_any_exportable()
}
fn iter_any_visible(&self) -> impl Iterator<Item = &dyn AnyExportableVec> {
(**self).iter_any_visible()
}
}
impl<T: Traversable> Traversable for Option<T> {
@@ -281,6 +285,14 @@ impl<T: Traversable> Traversable for Option<T> {
None => Box::new(std::iter::empty()),
}
}
fn iter_any_visible(&self) -> impl Iterator<Item = &dyn AnyExportableVec> {
match self {
Some(inner) => Box::new(inner.iter_any_visible())
as Box<dyn Iterator<Item = &dyn AnyExportableVec>>,
None => Box::new(std::iter::empty()),
}
}
}
impl<K: Display, V: Traversable> Traversable for BTreeMap<K, V> {
@@ -300,6 +312,15 @@ impl<K: Display, V: Traversable> Traversable for BTreeMap<K, V> {
}
iter
}
fn iter_any_visible(&self) -> impl Iterator<Item = &dyn AnyExportableVec> {
let mut iter: Box<dyn Iterator<Item = &dyn AnyExportableVec>> =
Box::new(std::iter::empty());
for v in self.values() {
iter = Box::new(iter.chain(v.iter_any_visible()));
}
iter
}
}
/// Unit type implementation - used as ZST placeholder for disabled features

View File

@@ -10,6 +10,7 @@ use syn::{Data, DeriveInput, Fields, Type, parse_macro_input};
struct StructAttr {
merge: bool,
transparent: bool,
hidden: bool,
wrap: Option<String>,
}
@@ -24,6 +25,7 @@ fn get_struct_attr(attrs: &[syn::Attribute]) -> StructAttr {
match ident.to_string().as_str() {
"merge" => result.merge = true,
"transparent" => result.transparent = true,
"hidden" => result.hidden = true,
_ => {}
}
continue;
@@ -244,6 +246,10 @@ fn gen_traversable(input: &DeriveInput) -> proc_macro2::TokenStream {
fn iter_any_exportable(&self) -> impl Iterator<Item = &dyn vecdb::AnyExportableVec> {
self.0.iter_any_exportable()
}
fn iter_any_visible(&self) -> impl Iterator<Item = &dyn vecdb::AnyExportableVec> {
self.0.iter_any_visible()
}
}
};
}
@@ -284,6 +290,10 @@ fn gen_traversable(input: &DeriveInput) -> proc_macro2::TokenStream {
fn iter_any_exportable(&self) -> impl Iterator<Item = &dyn vecdb::AnyExportableVec> {
self.#field_name.iter_any_exportable()
}
fn iter_any_visible(&self) -> impl Iterator<Item = &dyn vecdb::AnyExportableVec> {
self.#field_name.iter_any_visible()
}
}
};
}
@@ -294,7 +304,7 @@ fn gen_traversable(input: &DeriveInput) -> proc_macro2::TokenStream {
analyze_fields(named_fields, &generic_params);
let field_traversals = generate_field_traversals(&field_infos, struct_attr.merge);
let iterator_impl = generate_iterator_impl(&field_infos);
let iterator_impl = generate_iterator_impl(&field_infos, struct_attr.hidden);
let where_clause = build_where_clause(
generics,
&generics_needing_traversable,
@@ -505,41 +515,31 @@ fn generate_field_traversals(infos: &[FieldInfo], merge: bool) -> proc_macro2::T
}
}
fn generate_iterator_impl(infos: &[FieldInfo]) -> proc_macro2::TokenStream {
let regular_fields: Vec<_> = infos
.iter()
.filter(|i| !i.is_option)
.map(|i| i.name)
.collect();
fn generate_iter_body(
fields: &[&syn::Ident],
option_fields: &[&syn::Ident],
method: &str,
) -> proc_macro2::TokenStream {
let method_ident = syn::Ident::new(method, proc_macro2::Span::call_site());
let option_fields: Vec<_> = infos
.iter()
.filter(|i| i.is_option)
.map(|i| i.name)
.collect();
if regular_fields.is_empty() && option_fields.is_empty() {
return quote! {
fn iter_any_exportable(&self) -> impl Iterator<Item = &dyn vecdb::AnyExportableVec> {
std::iter::empty()
}
};
if fields.is_empty() && option_fields.is_empty() {
return quote! { std::iter::empty() };
}
let (init_part, chain_part) = if let Some((&first, rest)) = regular_fields.split_first() {
let (init_part, chain_part) = if let Some((&first, rest)) = fields.split_first() {
(
quote! {
let mut regular_iter: Box<dyn Iterator<Item = &dyn vecdb::AnyExportableVec>> =
Box::new(self.#first.iter_any_exportable());
let mut iter: Box<dyn Iterator<Item = &dyn vecdb::AnyExportableVec>> =
Box::new(self.#first.#method_ident());
},
quote! {
#(regular_iter = Box::new(regular_iter.chain(self.#rest.iter_any_exportable()));)*
#(iter = Box::new(iter.chain(self.#rest.#method_ident()));)*
},
)
} else {
(
quote! {
let mut regular_iter: Box<dyn Iterator<Item = &dyn vecdb::AnyExportableVec>> =
let mut iter: Box<dyn Iterator<Item = &dyn vecdb::AnyExportableVec>> =
Box::new(std::iter::empty());
},
quote! {},
@@ -550,7 +550,7 @@ fn generate_iterator_impl(infos: &[FieldInfo]) -> proc_macro2::TokenStream {
let chains = option_fields.iter().map(|f| {
quote! {
if let Some(ref x) = self.#f {
regular_iter = Box::new(regular_iter.chain(x.iter_any_exportable()));
iter = Box::new(iter.chain(x.#method_ident()));
}
}
});
@@ -560,12 +560,46 @@ fn generate_iterator_impl(infos: &[FieldInfo]) -> proc_macro2::TokenStream {
};
quote! {
fn iter_any_exportable(&self) -> impl Iterator<Item = &dyn vecdb::AnyExportableVec> {
#init_part
#chain_part
#option_part
regular_iter
#init_part
#chain_part
#option_part
iter
}
}
fn generate_iterator_impl(infos: &[FieldInfo], struct_hidden: bool) -> proc_macro2::TokenStream {
let all_regular: Vec<_> = infos.iter().filter(|i| !i.is_option).map(|i| i.name).collect();
let all_option: Vec<_> = infos.iter().filter(|i| i.is_option).map(|i| i.name).collect();
let exportable_body = generate_iter_body(&all_regular, &all_option, "iter_any_exportable");
let has_hidden_fields = infos.iter().any(|i| i.hidden);
let visible_impl = if struct_hidden {
// Entire struct is hidden — iter_any_visible returns nothing
quote! {
fn iter_any_visible(&self) -> impl Iterator<Item = &dyn vecdb::AnyExportableVec> {
std::iter::empty()
}
}
} else {
// Always generate iter_any_visible that calls iter_any_visible on children
// (skipping hidden fields if any), so hidden propagates through the tree
let visible_regular: Vec<_> = infos.iter().filter(|i| !i.is_option && !i.hidden).map(|i| i.name).collect();
let visible_option: Vec<_> = infos.iter().filter(|i| i.is_option && !i.hidden).map(|i| i.name).collect();
let visible_body = generate_iter_body(&visible_regular, &visible_option, "iter_any_visible");
quote! {
fn iter_any_visible(&self) -> impl Iterator<Item = &dyn vecdb::AnyExportableVec> {
#visible_body
}
}
};
quote! {
fn iter_any_exportable(&self) -> impl Iterator<Item = &dyn vecdb::AnyExportableVec> {
#exportable_body
}
#visible_impl
}
}