diff --git a/crates/brk_computer/src/distribution/metrics/activity/core.rs b/crates/brk_computer/src/distribution/metrics/activity/core.rs index 01c99d732..170852732 100644 --- a/crates/brk_computer/src/distribution/metrics/activity/core.rs +++ b/crates/brk_computer/src/distribution/metrics/activity/core.rs @@ -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 { - pub transfer_volume: PerBlockCumulativeWithSums, + pub transfer_volume: AmountPerBlockCumulativeWithSums, pub coindays_destroyed: PerBlockCumulativeWithSums, #[traversable(wrap = "transfer_volume", rename = "in_profit")] pub transfer_volume_in_profit: AmountPerBlockCumulativeWithSums, @@ -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, ) { - 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::>(), 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(()) diff --git a/crates/brk_computer/src/distribution/metrics/activity/full.rs b/crates/brk_computer/src/distribution/metrics/activity/full.rs index 0fe7f6d40..aa1d06c41 100644 --- a/crates/brk_computer/src/distribution/metrics/activity/full.rs +++ b/crates/brk_computer/src/distribution/metrics/activity/full.rs @@ -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 { diff --git a/crates/brk_computer/src/distribution/metrics/activity/mod.rs b/crates/brk_computer/src/distribution/metrics/activity/mod.rs index 5b0485d3a..b610f436b 100644 --- a/crates/brk_computer/src/distribution/metrics/activity/mod.rs +++ b/crates/brk_computer/src/distribution/metrics/activity/mod.rs @@ -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) } } diff --git a/crates/brk_computer/src/distribution/metrics/cohort/core.rs b/crates/brk_computer/src/distribution/metrics/cohort/core.rs index fad73ab34..4f4c3f9e0 100644 --- a/crates/brk_computer/src/distribution/metrics/cohort/core.rs +++ b/crates/brk_computer/src/distribution/metrics/cohort/core.rs @@ -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)?; diff --git a/crates/brk_computer/src/distribution/metrics/mod.rs b/crates/brk_computer/src/distribution/metrics/mod.rs index cf1395e14..e6d0aed04 100644 --- a/crates/brk_computer/src/distribution/metrics/mod.rs +++ b/crates/brk_computer/src/distribution/metrics/mod.rs @@ -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)?; diff --git a/crates/brk_computer/src/lib.rs b/crates/brk_computer/src/lib.rs index a0583db10..54afc1038 100644 --- a/crates/brk_computer/src/lib.rs +++ b/crates/brk_computer/src/lib.rs @@ -34,7 +34,6 @@ pub struct Computer { pub mining: Box>, pub transactions: Box>, pub scripts: Box>, - #[traversable(hidden)] pub positions: Box>, pub cointime: Box>, pub constants: Box, @@ -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 { + 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(label: &str, f: impl FnOnce() -> T) -> T { diff --git a/crates/brk_computer/src/outputs/count/import.rs b/crates/brk_computer/src/outputs/count/import.rs index 6c4cce900..3b2b63571 100644 --- a/crates/brk_computer/src/outputs/count/import.rs +++ b/crates/brk_computer/src/outputs/count/import.rs @@ -25,7 +25,7 @@ impl Vecs { )?, unspent: PerBlock::forced_import( db, - "exact_utxo_count", + "utxo_count_bis", version, indexes, )?, diff --git a/crates/brk_computer/src/positions.rs b/crates/brk_computer/src/positions.rs index 322270b0c..da492a269 100644 --- a/crates/brk_computer/src/positions.rs +++ b/crates/brk_computer/src/positions.rs @@ -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 { db: Database, diff --git a/crates/brk_computer/src/transactions/volume/import.rs b/crates/brk_computer/src/transactions/volume/import.rs index 11721626c..926081556 100644 --- a/crates/brk_computer/src/transactions/volume/import.rs +++ b/crates/brk_computer/src/transactions/volume/import.rs @@ -19,7 +19,7 @@ impl Vecs { Ok(Self { transfer_volume: AmountPerBlockCumulativeWithSums::forced_import( db, - "exact_transfer_volume", + "transfer_volume_bis", version, indexes, cached_starts, diff --git a/crates/brk_query/src/vecs.rs b/crates/brk_query/src/vecs.rs index c2e165c34..401868ee7 100644 --- a/crates/brk_query/src/vecs.rs +++ b/crates/brk_query/src/vecs.rs @@ -27,18 +27,18 @@ pub struct Vecs<'a> { impl<'a> Vecs<'a> { pub fn build(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(), ) } 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(), ) } diff --git a/crates/brk_traversable/src/lib.rs b/crates/brk_traversable/src/lib.rs index e46499ca4..355e78746 100644 --- a/crates/brk_traversable/src/lib.rs +++ b/crates/brk_traversable/src/lib.rs @@ -264,6 +264,10 @@ impl Traversable for Box { fn iter_any_exportable(&self) -> impl Iterator { (**self).iter_any_exportable() } + + fn iter_any_visible(&self) -> impl Iterator { + (**self).iter_any_visible() + } } impl Traversable for Option { @@ -281,6 +285,14 @@ impl Traversable for Option { None => Box::new(std::iter::empty()), } } + + fn iter_any_visible(&self) -> impl Iterator { + match self { + Some(inner) => Box::new(inner.iter_any_visible()) + as Box>, + None => Box::new(std::iter::empty()), + } + } } impl Traversable for BTreeMap { @@ -300,6 +312,15 @@ impl Traversable for BTreeMap { } iter } + + fn iter_any_visible(&self) -> impl Iterator { + let mut iter: Box> = + 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 diff --git a/crates/brk_traversable_derive/src/lib.rs b/crates/brk_traversable_derive/src/lib.rs index aaf3f42e9..31e912077 100644 --- a/crates/brk_traversable_derive/src/lib.rs +++ b/crates/brk_traversable_derive/src/lib.rs @@ -10,6 +10,7 @@ use syn::{Data, DeriveInput, Fields, Type, parse_macro_input}; struct StructAttr { merge: bool, transparent: bool, + hidden: bool, wrap: Option, } @@ -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 { self.0.iter_any_exportable() } + + fn iter_any_visible(&self) -> impl Iterator { + self.0.iter_any_visible() + } } }; } @@ -284,6 +290,10 @@ fn gen_traversable(input: &DeriveInput) -> proc_macro2::TokenStream { fn iter_any_exportable(&self) -> impl Iterator { self.#field_name.iter_any_exportable() } + + fn iter_any_visible(&self) -> impl Iterator { + 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 { - 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> = - Box::new(self.#first.iter_any_exportable()); + let mut iter: Box> = + 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> = + let mut iter: Box> = 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 { - #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 { + 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 { + #visible_body + } + } + }; + + quote! { + fn iter_any_exportable(&self) -> impl Iterator { + #exportable_body + } + #visible_impl } }