use std::path::Path; use brk_error::Result; use brk_indexer::Indexer; use brk_structs::{Bitcoin, CheckedSub, Dollars, StoredF64, Version}; use vecdb::{AnyCollectableVec, Database, Exit, PAGE_SIZE, VecIterator}; use super::{ Indexes, grouped::{ ComputedRatioVecsFromDateIndex, ComputedValueVecsFromHeight, ComputedVecsFromHeight, Source, VecBuilderOptions, }, indexes, price, stateful, transactions, }; const VERSION: Version = Version::ZERO; #[derive(Clone)] pub struct Vecs { db: Database, pub indexes_to_coinblocks_created: ComputedVecsFromHeight, pub indexes_to_coinblocks_stored: ComputedVecsFromHeight, pub indexes_to_liveliness: ComputedVecsFromHeight, pub indexes_to_vaultedness: ComputedVecsFromHeight, pub indexes_to_activity_to_vaultedness_ratio: ComputedVecsFromHeight, pub indexes_to_vaulted_supply: ComputedValueVecsFromHeight, pub indexes_to_active_supply: ComputedValueVecsFromHeight, pub indexes_to_thermo_cap: ComputedVecsFromHeight, pub indexes_to_investor_cap: ComputedVecsFromHeight, pub indexes_to_vaulted_cap: ComputedVecsFromHeight, pub indexes_to_active_cap: ComputedVecsFromHeight, pub indexes_to_vaulted_price: ComputedVecsFromHeight, pub indexes_to_vaulted_price_ratio: ComputedRatioVecsFromDateIndex, pub indexes_to_active_price: ComputedVecsFromHeight, pub indexes_to_active_price_ratio: ComputedRatioVecsFromDateIndex, pub indexes_to_true_market_mean: ComputedVecsFromHeight, pub indexes_to_true_market_mean_ratio: ComputedRatioVecsFromDateIndex, pub indexes_to_cointime_value_destroyed: ComputedVecsFromHeight, pub indexes_to_cointime_value_created: ComputedVecsFromHeight, pub indexes_to_cointime_value_stored: ComputedVecsFromHeight, pub indexes_to_cointime_price: ComputedVecsFromHeight, pub indexes_to_cointime_cap: ComputedVecsFromHeight, pub indexes_to_cointime_price_ratio: ComputedRatioVecsFromDateIndex, // pub indexes_to_thermo_cap_relative_to_investor_cap: ComputedValueVecsFromHeight, } impl Vecs { pub fn forced_import( parent: &Path, version: Version, indexes: &indexes::Vecs, price: Option<&price::Vecs>, ) -> Result { let db = Database::open(&parent.join("cointime"))?; db.set_min_len(PAGE_SIZE * 1_000_000)?; let compute_dollars = price.is_some(); Ok(Self { indexes_to_coinblocks_created: ComputedVecsFromHeight::forced_import( &db, "coinblocks_created", Source::Compute, version + VERSION + Version::ZERO, indexes, VecBuilderOptions::default().add_sum().add_cumulative(), )?, indexes_to_coinblocks_stored: ComputedVecsFromHeight::forced_import( &db, "coinblocks_stored", Source::Compute, version + VERSION + Version::ZERO, indexes, VecBuilderOptions::default().add_sum().add_cumulative(), )?, indexes_to_liveliness: ComputedVecsFromHeight::forced_import( &db, "liveliness", Source::Compute, version + VERSION + Version::ZERO, indexes, VecBuilderOptions::default().add_last(), )?, indexes_to_vaultedness: ComputedVecsFromHeight::forced_import( &db, "vaultedness", Source::Compute, version + VERSION + Version::ZERO, indexes, VecBuilderOptions::default().add_last(), )?, indexes_to_activity_to_vaultedness_ratio: ComputedVecsFromHeight::forced_import( &db, "activity_to_vaultedness_ratio", Source::Compute, version + VERSION + Version::ZERO, indexes, VecBuilderOptions::default().add_last(), )?, indexes_to_vaulted_supply: ComputedValueVecsFromHeight::forced_import( &db, "vaulted_supply", Source::Compute, version + VERSION + Version::ONE, VecBuilderOptions::default().add_last(), compute_dollars, indexes, )?, indexes_to_active_supply: ComputedValueVecsFromHeight::forced_import( &db, "active_supply", Source::Compute, version + VERSION + Version::ONE, VecBuilderOptions::default().add_last(), compute_dollars, indexes, )?, indexes_to_thermo_cap: ComputedVecsFromHeight::forced_import( &db, "thermo_cap", Source::Compute, version + VERSION + Version::ONE, indexes, VecBuilderOptions::default().add_last(), )?, indexes_to_investor_cap: ComputedVecsFromHeight::forced_import( &db, "investor_cap", Source::Compute, version + VERSION + Version::ONE, indexes, VecBuilderOptions::default().add_last(), )?, indexes_to_vaulted_cap: ComputedVecsFromHeight::forced_import( &db, "vaulted_cap", Source::Compute, version + VERSION + Version::ONE, indexes, VecBuilderOptions::default().add_last(), )?, indexes_to_active_cap: ComputedVecsFromHeight::forced_import( &db, "active_cap", Source::Compute, version + VERSION + Version::ONE, indexes, VecBuilderOptions::default().add_last(), )?, indexes_to_vaulted_price: ComputedVecsFromHeight::forced_import( &db, "vaulted_price", Source::Compute, version + VERSION + Version::ZERO, indexes, VecBuilderOptions::default().add_last(), )?, indexes_to_vaulted_price_ratio: ComputedRatioVecsFromDateIndex::forced_import( &db, "vaulted_price", Source::None, version + VERSION + Version::ZERO, indexes, true, )?, indexes_to_active_price: ComputedVecsFromHeight::forced_import( &db, "active_price", Source::Compute, version + VERSION + Version::ZERO, indexes, VecBuilderOptions::default().add_last(), )?, indexes_to_active_price_ratio: ComputedRatioVecsFromDateIndex::forced_import( &db, "active_price", Source::None, version + VERSION + Version::ZERO, indexes, true, )?, indexes_to_true_market_mean: ComputedVecsFromHeight::forced_import( &db, "true_market_mean", Source::Compute, version + VERSION + Version::ZERO, indexes, VecBuilderOptions::default().add_last(), )?, indexes_to_true_market_mean_ratio: ComputedRatioVecsFromDateIndex::forced_import( &db, "true_market_mean", Source::None, version + VERSION + Version::ZERO, indexes, true, )?, indexes_to_cointime_value_destroyed: ComputedVecsFromHeight::forced_import( &db, "cointime_value_destroyed", Source::Compute, version + VERSION + Version::ZERO, indexes, VecBuilderOptions::default().add_sum().add_cumulative(), )?, indexes_to_cointime_value_created: ComputedVecsFromHeight::forced_import( &db, "cointime_value_created", Source::Compute, version + VERSION + Version::ZERO, indexes, VecBuilderOptions::default().add_sum().add_cumulative(), )?, indexes_to_cointime_value_stored: ComputedVecsFromHeight::forced_import( &db, "cointime_value_stored", Source::Compute, version + VERSION + Version::ZERO, indexes, VecBuilderOptions::default().add_sum().add_cumulative(), )?, indexes_to_cointime_price: ComputedVecsFromHeight::forced_import( &db, "cointime_price", Source::Compute, version + VERSION + Version::ZERO, indexes, VecBuilderOptions::default().add_last(), )?, indexes_to_cointime_cap: ComputedVecsFromHeight::forced_import( &db, "cointime_cap", Source::Compute, version + VERSION + Version::ZERO, indexes, VecBuilderOptions::default().add_last(), )?, indexes_to_cointime_price_ratio: ComputedRatioVecsFromDateIndex::forced_import( &db, "cointime_price", Source::None, version + VERSION + Version::ZERO, indexes, true, )?, db, }) } #[allow(clippy::too_many_arguments)] pub fn compute( &mut self, indexer: &Indexer, indexes: &indexes::Vecs, starting_indexes: &Indexes, price: Option<&price::Vecs>, transactions: &transactions::Vecs, stateful: &stateful::Vecs, exit: &Exit, ) -> Result<()> { self.compute_( indexer, indexes, starting_indexes, price, transactions, stateful, exit, )?; self.db.flush_then_punch()?; Ok(()) } #[allow(clippy::too_many_arguments)] fn compute_( &mut self, indexer: &Indexer, indexes: &indexes::Vecs, starting_indexes: &Indexes, price: Option<&price::Vecs>, transactions: &transactions::Vecs, stateful: &stateful::Vecs, exit: &Exit, ) -> Result<()> { let circulating_supply = &stateful.utxo_cohorts.all.1.height_to_supply; self.indexes_to_coinblocks_created.compute_all( indexer, indexes, starting_indexes, exit, |vec, _, _, starting_indexes, exit| { vec.compute_transform( starting_indexes.height, circulating_supply, |(i, v, ..)| (i, StoredF64::from(Bitcoin::from(v))), exit, )?; Ok(()) }, )?; let indexes_to_coinblocks_destroyed = &stateful.utxo_cohorts.all.1.indexes_to_coinblocks_destroyed; self.indexes_to_coinblocks_stored.compute_all( indexer, indexes, starting_indexes, exit, |vec, _, _, starting_indexes, exit| { let mut coinblocks_destroyed_iter = indexes_to_coinblocks_destroyed .height .as_ref() .unwrap() .into_iter(); vec.compute_transform( starting_indexes.height, self.indexes_to_coinblocks_created.height.as_ref().unwrap(), |(i, created, ..)| { let destroyed = coinblocks_destroyed_iter.unwrap_get_inner(i); (i, created.checked_sub(destroyed).unwrap()) }, exit, )?; Ok(()) }, )?; self.indexes_to_liveliness.compute_all( indexer, indexes, starting_indexes, exit, |vec, _, _, starting_indexes, exit| { vec.compute_divide( starting_indexes.height, indexes_to_coinblocks_destroyed .height_extra .unwrap_cumulative(), self.indexes_to_coinblocks_created .height_extra .unwrap_cumulative(), exit, )?; Ok(()) }, )?; let liveliness = &self.indexes_to_liveliness; self.indexes_to_vaultedness.compute_all( indexer, indexes, starting_indexes, exit, |vec, _, _, starting_indexes, exit| { vec.compute_transform( starting_indexes.height, liveliness.height.as_ref().unwrap(), |(i, v, ..)| (i, StoredF64::from(1.0).checked_sub(v).unwrap()), exit, )?; Ok(()) }, )?; let vaultedness = &self.indexes_to_vaultedness; self.indexes_to_activity_to_vaultedness_ratio.compute_all( indexer, indexes, starting_indexes, exit, |vec, _, _, starting_indexes, exit| { vec.compute_divide( starting_indexes.height, liveliness.height.as_ref().unwrap(), vaultedness.height.as_ref().unwrap(), exit, )?; Ok(()) }, )?; self.indexes_to_vaulted_supply.compute_all( indexer, indexes, price, starting_indexes, exit, |vec, _, _, starting_indexes, exit| { vec.compute_multiply( starting_indexes.height, circulating_supply, vaultedness.height.as_ref().unwrap(), exit, )?; Ok(()) }, )?; self.indexes_to_active_supply.compute_all( indexer, indexes, price, starting_indexes, exit, |vec, _, _, starting_indexes, exit| { vec.compute_multiply( starting_indexes.height, circulating_supply, liveliness.height.as_ref().unwrap(), exit, )?; Ok(()) }, )?; if let Some(price) = price { let realized_cap = stateful .utxo_cohorts .all .1 .height_to_realized_cap .as_ref() .unwrap(); let realized_price = stateful .utxo_cohorts .all .1 .indexes_to_realized_price .as_ref() .unwrap() .height .as_ref() .unwrap(); self.indexes_to_thermo_cap.compute_all( indexer, indexes, starting_indexes, exit, |vec, _, _, starting_indexes, exit| { vec.compute_transform( starting_indexes.height, transactions .indexes_to_subsidy .dollars .as_ref() .unwrap() .height_extra .unwrap_cumulative(), |(i, v, ..)| (i, v), exit, )?; Ok(()) }, )?; self.indexes_to_investor_cap.compute_all( indexer, indexes, starting_indexes, exit, |vec, _, _, starting_indexes, exit| { vec.compute_subtract( starting_indexes.height, realized_cap, self.indexes_to_thermo_cap.height.as_ref().unwrap(), exit, )?; Ok(()) }, )?; self.indexes_to_vaulted_cap.compute_all( indexer, indexes, starting_indexes, exit, |vec, _, _, starting_indexes, exit| { vec.compute_divide( starting_indexes.height, realized_cap, self.indexes_to_vaultedness.height.as_ref().unwrap(), exit, )?; Ok(()) }, )?; self.indexes_to_active_cap.compute_all( indexer, indexes, starting_indexes, exit, |vec, _, _, starting_indexes, exit| { vec.compute_multiply( starting_indexes.height, realized_cap, self.indexes_to_liveliness.height.as_ref().unwrap(), exit, )?; Ok(()) }, )?; self.indexes_to_vaulted_price.compute_all( indexer, indexes, starting_indexes, exit, |vec, _, _, starting_indexes, exit| { vec.compute_divide( starting_indexes.height, realized_price, self.indexes_to_vaultedness.height.as_ref().unwrap(), exit, )?; Ok(()) }, )?; self.indexes_to_vaulted_price_ratio.compute_rest( indexer, indexes, price, starting_indexes, exit, Some(self.indexes_to_vaulted_price.dateindex.unwrap_last()), )?; self.indexes_to_active_price.compute_all( indexer, indexes, starting_indexes, exit, |vec, _, _, starting_indexes, exit| { vec.compute_multiply( starting_indexes.height, realized_price, self.indexes_to_liveliness.height.as_ref().unwrap(), exit, )?; Ok(()) }, )?; self.indexes_to_active_price_ratio.compute_rest( indexer, indexes, price, starting_indexes, exit, Some(self.indexes_to_active_price.dateindex.unwrap_last()), )?; self.indexes_to_true_market_mean.compute_all( indexer, indexes, starting_indexes, exit, |vec, _, _, starting_indexes, exit| { vec.compute_divide( starting_indexes.height, self.indexes_to_investor_cap.height.as_ref().unwrap(), self.indexes_to_active_supply .bitcoin .height .as_ref() .unwrap(), exit, )?; Ok(()) }, )?; self.indexes_to_true_market_mean_ratio.compute_rest( indexer, indexes, price, starting_indexes, exit, Some(self.indexes_to_true_market_mean.dateindex.unwrap_last()), )?; self.indexes_to_cointime_value_destroyed.compute_all( indexer, indexes, starting_indexes, exit, |vec, _, _, starting_indexes, exit| { // TODO: Another example when the callback should be applied to each index, instead of to base then merging from more granular to less // The price taken won't be correct for time based indexes vec.compute_multiply( starting_indexes.height, &price.chainindexes_to_close.height, indexes_to_coinblocks_destroyed.height.as_ref().unwrap(), exit, )?; Ok(()) }, )?; self.indexes_to_cointime_value_created.compute_all( indexer, indexes, starting_indexes, exit, |vec, _, _, starting_indexes, exit| { vec.compute_multiply( starting_indexes.height, &price.chainindexes_to_close.height, self.indexes_to_coinblocks_created.height.as_ref().unwrap(), exit, )?; Ok(()) }, )?; self.indexes_to_cointime_value_stored.compute_all( indexer, indexes, starting_indexes, exit, |vec, _, _, starting_indexes, exit| { vec.compute_multiply( starting_indexes.height, &price.chainindexes_to_close.height, self.indexes_to_coinblocks_stored.height.as_ref().unwrap(), exit, )?; Ok(()) }, )?; self.indexes_to_cointime_price.compute_all( indexer, indexes, starting_indexes, exit, |vec, _, _, starting_indexes, exit| { vec.compute_divide( starting_indexes.height, self.indexes_to_cointime_value_destroyed .height_extra .unwrap_cumulative(), self.indexes_to_coinblocks_stored .height_extra .unwrap_cumulative(), exit, )?; Ok(()) }, )?; self.indexes_to_cointime_cap.compute_all( indexer, indexes, starting_indexes, exit, |vec, _, _, starting_indexes, exit| { vec.compute_multiply( starting_indexes.height, self.indexes_to_cointime_price.height.as_ref().unwrap(), circulating_supply, exit, )?; Ok(()) }, )?; self.indexes_to_cointime_price_ratio.compute_rest( indexer, indexes, price, starting_indexes, exit, Some(self.indexes_to_cointime_price.dateindex.unwrap_last()), )?; } Ok(()) } pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> { [ self.indexes_to_coinblocks_created.vecs(), self.indexes_to_coinblocks_stored.vecs(), self.indexes_to_liveliness.vecs(), self.indexes_to_vaultedness.vecs(), self.indexes_to_activity_to_vaultedness_ratio.vecs(), self.indexes_to_vaulted_supply.vecs(), self.indexes_to_active_supply.vecs(), self.indexes_to_thermo_cap.vecs(), self.indexes_to_investor_cap.vecs(), self.indexes_to_vaulted_cap.vecs(), self.indexes_to_active_cap.vecs(), self.indexes_to_vaulted_price.vecs(), self.indexes_to_vaulted_price_ratio.vecs(), self.indexes_to_active_price.vecs(), self.indexes_to_active_price_ratio.vecs(), self.indexes_to_true_market_mean.vecs(), self.indexes_to_true_market_mean_ratio.vecs(), self.indexes_to_cointime_price.vecs(), self.indexes_to_cointime_cap.vecs(), self.indexes_to_cointime_price_ratio.vecs(), self.indexes_to_cointime_value_destroyed.vecs(), self.indexes_to_cointime_value_created.vecs(), self.indexes_to_cointime_value_stored.vecs(), ] .into_iter() .flatten() .collect::>() } }