mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-08-08 16:43:06 -07:00
brk: metric trimming part X + 1
This commit is contained in:
@@ -394,7 +394,7 @@ impl UTXOCohorts<Rw> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn push_maturation(&mut self, matured: &AgeRange<Sats>) {
|
||||
for (v, &sats) in self.matured.iter_mut().zip(matured.iter()) {
|
||||
v.block.sats.push(sats);
|
||||
v.push_block_sats(sats);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -605,7 +605,7 @@ impl UTXOCohorts<Rw> {
|
||||
.transfer_volume
|
||||
.block
|
||||
.cents
|
||||
.read_only_clone();
|
||||
.clone();
|
||||
let under_1h_value_destroyed = self
|
||||
.age_range
|
||||
.under_1h
|
||||
@@ -801,8 +801,6 @@ impl UTXOCohorts<Rw> {
|
||||
vecs.extend(self.profitability.collect_all_vecs_mut());
|
||||
for v in self.matured.iter_mut() {
|
||||
let inner = &mut v.inner;
|
||||
vecs.push(&mut inner.block.sats);
|
||||
vecs.push(&mut inner.block.cents);
|
||||
vecs.push(&mut inner.cumulative.sats.height);
|
||||
vecs.push(&mut inner.cumulative.cents.height);
|
||||
}
|
||||
@@ -821,7 +819,7 @@ impl UTXOCohorts<Rw> {
|
||||
.chain(
|
||||
self.matured
|
||||
.iter()
|
||||
.map(|v| Height::from(v.block.sats.len())),
|
||||
.map(|v| Height::from(v.cumulative.sats.height.len())),
|
||||
)
|
||||
.min()
|
||||
.unwrap_or_default()
|
||||
|
||||
@@ -3,7 +3,7 @@ use brk_indexer::Lengths;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, StoredF64, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
distribution::{
|
||||
@@ -45,8 +45,8 @@ impl ActivityCore {
|
||||
self.minimal
|
||||
.min_len()
|
||||
.min(self.coindays_destroyed.block.len())
|
||||
.min(self.transfer_volume_in_profit.block.sats.len())
|
||||
.min(self.transfer_volume_in_loss.block.sats.len())
|
||||
.min(self.transfer_volume_in_profit.cumulative.sats.height.len())
|
||||
.min(self.transfer_volume_in_loss.cumulative.sats.height.len())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -55,22 +55,18 @@ impl ActivityCore {
|
||||
self.coindays_destroyed
|
||||
.push_block(StoredF64::from(Bitcoin::from(state.satdays_destroyed)));
|
||||
self.transfer_volume_in_profit
|
||||
.block
|
||||
.sats
|
||||
.push(state.realized.sent_in_profit());
|
||||
.push_block_sats(state.realized.sent_in_profit());
|
||||
self.transfer_volume_in_loss
|
||||
.block
|
||||
.sats
|
||||
.push(state.realized.sent_in_loss());
|
||||
.push_block_sats(state.realized.sent_in_loss());
|
||||
}
|
||||
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
let mut vecs = self.minimal.collect_vecs_mut();
|
||||
vecs.push(self.coindays_destroyed.stored_mut());
|
||||
vecs.push(&mut self.transfer_volume_in_profit.inner.block.sats);
|
||||
vecs.push(&mut self.transfer_volume_in_profit.inner.block.cents);
|
||||
vecs.push(&mut self.transfer_volume_in_loss.inner.block.sats);
|
||||
vecs.push(&mut self.transfer_volume_in_loss.inner.block.cents);
|
||||
vecs.push(&mut self.transfer_volume_in_profit.inner.cumulative.sats.height);
|
||||
vecs.push(&mut self.transfer_volume_in_profit.inner.cumulative.cents.height);
|
||||
vecs.push(&mut self.transfer_volume_in_loss.inner.cumulative.sats.height);
|
||||
vecs.push(&mut self.transfer_volume_in_loss.inner.cumulative.cents.height);
|
||||
vecs
|
||||
}
|
||||
|
||||
@@ -89,8 +85,8 @@ impl ActivityCore {
|
||||
.compute_from_stateful(starting_lengths, &minimal_refs, exit)?;
|
||||
|
||||
sum_others!(self, starting_lengths, others, exit; coindays_destroyed.cumulative.height);
|
||||
sum_others!(self, starting_lengths, others, exit; transfer_volume_in_profit.block.sats);
|
||||
sum_others!(self, starting_lengths, others, exit; transfer_volume_in_loss.block.sats);
|
||||
sum_others!(self, starting_lengths, others, exit; transfer_volume_in_profit.cumulative.sats.height);
|
||||
sum_others!(self, starting_lengths, others, exit; transfer_volume_in_loss.cumulative.sats.height);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use brk_error::Result;
|
||||
use brk_indexer::Lengths;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::Version;
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
distribution::{
|
||||
@@ -27,19 +27,19 @@ impl ActivityMinimal {
|
||||
}
|
||||
|
||||
pub(crate) fn min_len(&self) -> usize {
|
||||
self.transfer_volume.block.sats.len()
|
||||
self.transfer_volume.cumulative.sats.height.len()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn push_state(&mut self, state: &CohortState<impl RealizedOps, impl CostBasisOps>) {
|
||||
self.transfer_volume.block.sats.push(state.sent);
|
||||
self.transfer_volume.push_block_sats(state.sent);
|
||||
}
|
||||
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
let inner = &mut self.transfer_volume.inner;
|
||||
vec![
|
||||
&mut inner.block.sats as &mut dyn AnyStoredVec,
|
||||
&mut inner.block.cents,
|
||||
&mut inner.cumulative.sats.height as &mut dyn AnyStoredVec,
|
||||
&mut inner.cumulative.cents.height,
|
||||
]
|
||||
}
|
||||
|
||||
@@ -49,14 +49,18 @@ impl ActivityMinimal {
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.transfer_volume.block.sats.compute_sum_of_others(
|
||||
starting_lengths.height,
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| &v.transfer_volume.block.sats)
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.transfer_volume
|
||||
.cumulative
|
||||
.sats
|
||||
.height
|
||||
.compute_sum_of_others(
|
||||
starting_lengths.height,
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| &v.transfer_volume.cumulative.sats.height)
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -105,7 +105,6 @@ impl MinimalCohortMetrics {
|
||||
self.outputs.compute_rest(starting_lengths.height, exit)?;
|
||||
self.activity
|
||||
.compute_rest_part1(prices, starting_lengths, exit)?;
|
||||
self.realized.compute_rest_part1(starting_lengths, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,6 @@ impl TypeCohortMetrics {
|
||||
self.outputs.compute_rest(starting_lengths.height, exit)?;
|
||||
self.activity
|
||||
.compute_rest_part1(prices, starting_lengths, exit)?;
|
||||
self.realized.compute_rest_part1(starting_lengths, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -142,22 +142,13 @@ impl RealizedCore {
|
||||
starting_lengths: &Lengths,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.minimal.compute_rest_part1(starting_lengths, exit)?;
|
||||
|
||||
self.net_pnl.block.cents.compute_transform2(
|
||||
self.net_pnl.compute_from_cumulative_pair(
|
||||
starting_lengths.height,
|
||||
&self.minimal.profit.block.cents,
|
||||
&self.minimal.loss.block.cents,
|
||||
|(i, profit, loss, ..)| {
|
||||
(
|
||||
i,
|
||||
CentsSigned::new(profit.inner() as i64 - loss.inner() as i64),
|
||||
)
|
||||
},
|
||||
&self.minimal.profit.cumulative.cents.height,
|
||||
&self.minimal.loss.cumulative.cents.height,
|
||||
|_, profit, loss| CentsSigned::new(profit.inner() as i64 - loss.inner() as i64),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rest_part2(
|
||||
@@ -171,8 +162,6 @@ impl RealizedCore {
|
||||
self.minimal
|
||||
.compute_rest_part2(prices, starting_lengths, height_to_supply, exit)?;
|
||||
|
||||
self.net_pnl.compute_rest(starting_lengths.height, exit)?;
|
||||
|
||||
self.sopr
|
||||
.ratio
|
||||
._24h
|
||||
|
||||
@@ -128,7 +128,7 @@ impl RealizedFull {
|
||||
.len()
|
||||
.min(self.cap_raw.len())
|
||||
.min(self.capitalized.cap_raw.len())
|
||||
.min(self.peak_regret.value.block.cents.len())
|
||||
.min(self.peak_regret.value.cumulative.cents.height.len())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -148,9 +148,7 @@ impl RealizedFull {
|
||||
.push(state.realized.capitalized_cap_raw());
|
||||
self.peak_regret
|
||||
.value
|
||||
.block
|
||||
.cents
|
||||
.push(state.realized.peak_regret());
|
||||
.push_block(state.realized.peak_regret());
|
||||
}
|
||||
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
@@ -158,7 +156,7 @@ impl RealizedFull {
|
||||
vecs.push(&mut self.capitalized.price.cents.height);
|
||||
vecs.push(&mut self.cap_raw as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.capitalized.cap_raw as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.peak_regret.value.block.cents);
|
||||
vecs.push(self.peak_regret.value.stored_mut());
|
||||
vecs
|
||||
}
|
||||
|
||||
@@ -169,9 +167,7 @@ impl RealizedFull {
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.core
|
||||
.compute_from_stateful(starting_lengths, others, exit)?;
|
||||
|
||||
Ok(())
|
||||
.compute_from_stateful(starting_lengths, others, exit)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -189,7 +185,7 @@ impl RealizedFull {
|
||||
};
|
||||
self.capitalized.price.cents.height.push(capitalized_price);
|
||||
|
||||
self.peak_regret.value.block.cents.push(accum.peak_regret());
|
||||
self.peak_regret.value.push_block(accum.peak_regret());
|
||||
|
||||
capitalized_price
|
||||
}
|
||||
@@ -199,12 +195,7 @@ impl RealizedFull {
|
||||
starting_lengths: &Lengths,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.core.compute_rest_part1(starting_lengths, exit)?;
|
||||
|
||||
self.peak_regret
|
||||
.value
|
||||
.compute_rest(starting_lengths.height, exit)?;
|
||||
Ok(())
|
||||
self.core.compute_rest_part1(starting_lengths, exit)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@@ -243,13 +234,13 @@ impl RealizedFull {
|
||||
}
|
||||
|
||||
// Gross PnL
|
||||
self.gross_pnl.block.cents.compute_add(
|
||||
self.gross_pnl.compute_from_cumulative_pair(
|
||||
starting_lengths.height,
|
||||
&self.core.minimal.profit.block.cents,
|
||||
&self.core.minimal.loss.block.cents,
|
||||
&self.core.minimal.profit.cumulative.cents.height,
|
||||
&self.core.minimal.loss.cumulative.cents.height,
|
||||
|_, profit, loss| profit + loss,
|
||||
exit,
|
||||
)?;
|
||||
self.gross_pnl.compute_rest(starting_lengths.height, exit)?;
|
||||
|
||||
// Net PnL 1m change relative to rcap and mcap
|
||||
self.net_pnl
|
||||
|
||||
@@ -61,22 +61,22 @@ impl RealizedMinimal {
|
||||
.cents
|
||||
.height
|
||||
.len()
|
||||
.min(self.profit.block.cents.len())
|
||||
.min(self.loss.block.cents.len())
|
||||
.min(self.profit.cumulative.cents.height.len())
|
||||
.min(self.loss.cumulative.cents.height.len())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn push_state(&mut self, state: &CohortState<impl RealizedOps, impl CostBasisOps>) {
|
||||
self.cap.cents.height.push(state.realized.cap());
|
||||
self.profit.block.cents.push(state.realized.profit());
|
||||
self.loss.block.cents.push(state.realized.loss());
|
||||
self.profit.push_block(state.realized.profit());
|
||||
self.loss.push_block(state.realized.loss());
|
||||
}
|
||||
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
vec![
|
||||
&mut self.cap.cents.height as &mut dyn AnyStoredVec,
|
||||
&mut self.profit.block.cents,
|
||||
&mut self.loss.block.cents,
|
||||
self.profit.stored_mut(),
|
||||
self.loss.stored_mut(),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -87,18 +87,16 @@ impl RealizedMinimal {
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
sum_others!(self, starting_lengths, others, exit; cap.cents.height);
|
||||
sum_others!(self, starting_lengths, others, exit; profit.block.cents);
|
||||
sum_others!(self, starting_lengths, others, exit; loss.block.cents);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rest_part1(
|
||||
&mut self,
|
||||
starting_lengths: &Lengths,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.profit.compute_rest(starting_lengths.height, exit)?;
|
||||
self.loss.compute_rest(starting_lengths.height, exit)?;
|
||||
self.profit.compute_sum_of_others(
|
||||
starting_lengths.height,
|
||||
&others.iter().map(|v| &v.profit).collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.loss.compute_sum_of_others(
|
||||
starting_lengths.height,
|
||||
&others.iter().map(|v| &v.loss).collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Dollars, Height, Version};
|
||||
use vecdb::{
|
||||
Database, EagerVec, ImportableVec, LazyVecFrom1, PcoVec, ReadableCloneableVec, Rw, StorageMode,
|
||||
};
|
||||
|
||||
use super::FiatType;
|
||||
|
||||
/// Raw per-block fiat data: cents (stored) + usd (lazy), no resolutions.
|
||||
#[derive(Traversable)]
|
||||
pub struct FiatBlock<C: FiatType, M: StorageMode = Rw> {
|
||||
pub usd: LazyVecFrom1<Height, Dollars, Height, C>,
|
||||
pub cents: M::Stored<EagerVec<PcoVec<Height, C>>>,
|
||||
}
|
||||
|
||||
impl<C: FiatType> FiatBlock<C> {
|
||||
pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result<Self> {
|
||||
let cents: EagerVec<PcoVec<Height, C>> =
|
||||
EagerVec::forced_import(db, &format!("{name}_cents"), version)?;
|
||||
let usd =
|
||||
LazyVecFrom1::transformed::<C::ToDollars>(name, version, cents.read_only_boxed_clone());
|
||||
Ok(Self { usd, cents })
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,29 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use vecdb::{Database, Exit, Rw, StorageMode};
|
||||
use vecdb::{
|
||||
AnyStoredVec, AnyVec, Database, Exit, ReadableVec, Rw, StorageMode, VecValue, WritableVec,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{
|
||||
FiatBlock, FiatPerBlock, FiatType, LazyRollingSumsFiatFromHeight, WindowStartVec, Windows,
|
||||
FiatPerBlock, FiatType, LazyFiatBlock, LazyRollingSumsFiatFromHeight, WindowStartVec,
|
||||
Windows,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct FiatPerBlockCumulativeWithSums<C: FiatType, M: StorageMode = Rw> {
|
||||
pub block: FiatBlock<C, M>,
|
||||
pub block: LazyFiatBlock<C>,
|
||||
pub cumulative: FiatPerBlock<C, M>,
|
||||
pub sum: LazyRollingSumsFiatFromHeight<C>,
|
||||
#[traversable(skip)]
|
||||
last_cumulative: Option<(usize, C)>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ONE;
|
||||
|
||||
impl<C: FiatType> FiatPerBlockCumulativeWithSums<C> {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
@@ -25,12 +32,18 @@ impl<C: FiatType> FiatPerBlockCumulativeWithSums<C> {
|
||||
indexes: &indexes::Vecs,
|
||||
cached_starts: &Windows<&WindowStartVec>,
|
||||
) -> Result<Self> {
|
||||
let block = FiatBlock::forced_import(db, name, version)?;
|
||||
let v = version + VERSION;
|
||||
let cumulative =
|
||||
FiatPerBlock::forced_import(db, &format!("{name}_cumulative"), version, indexes)?;
|
||||
FiatPerBlock::forced_import(db, &format!("{name}_cumulative"), v, indexes)?;
|
||||
let last_cumulative = cumulative
|
||||
.cents
|
||||
.height
|
||||
.collect_last()
|
||||
.map(|value| (cumulative.cents.height.len(), value));
|
||||
let block = LazyFiatBlock::from_cumulative(name, v, &cumulative);
|
||||
let sum = LazyRollingSumsFiatFromHeight::new(
|
||||
&format!("{name}_sum"),
|
||||
version,
|
||||
v,
|
||||
&cumulative.cents.height,
|
||||
cached_starts,
|
||||
indexes,
|
||||
@@ -39,17 +52,73 @@ impl<C: FiatType> FiatPerBlockCumulativeWithSums<C> {
|
||||
block,
|
||||
cumulative,
|
||||
sum,
|
||||
last_cumulative,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rest(&mut self, max_from: Height, exit: &Exit) -> Result<()>
|
||||
#[inline(always)]
|
||||
pub(crate) fn push_block(&mut self, value: C)
|
||||
where
|
||||
C: Default,
|
||||
C: Copy,
|
||||
{
|
||||
self.cumulative
|
||||
.cents
|
||||
.height
|
||||
.compute_cumulative(max_from, &self.block.cents, exit)?;
|
||||
let len = self.cumulative.cents.height.len();
|
||||
let mut cumulative = match self.last_cumulative {
|
||||
Some((cached_len, value)) if cached_len == len => value,
|
||||
_ => self
|
||||
.cumulative
|
||||
.cents
|
||||
.height
|
||||
.collect_last()
|
||||
.unwrap_or_default(),
|
||||
};
|
||||
cumulative += value;
|
||||
self.cumulative.cents.height.push(cumulative);
|
||||
self.last_cumulative = Some((len + 1, cumulative));
|
||||
}
|
||||
|
||||
pub(crate) fn compute_from_cumulative_pair<S1, S2>(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
source1: &impl ReadableVec<Height, S1>,
|
||||
source2: &impl ReadableVec<Height, S2>,
|
||||
mut transform: impl FnMut(Height, S1, S2) -> C,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
S1: VecValue,
|
||||
S2: VecValue,
|
||||
{
|
||||
self.cumulative.cents.height.compute_transform2(
|
||||
max_from,
|
||||
source1,
|
||||
source2,
|
||||
|(height, value1, value2, ..)| (height, transform(height, value1, value2)),
|
||||
exit,
|
||||
)?;
|
||||
self.last_cumulative = None;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn compute_sum_of_others(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.cumulative.cents.height.compute_sum_of_others(
|
||||
max_from,
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| &v.cumulative.cents.height)
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.last_cumulative = None;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn stored_mut(&mut self) -> &mut dyn AnyStoredVec {
|
||||
self.last_cumulative = None;
|
||||
&mut self.cumulative.cents.height
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Dollars, Height, Version};
|
||||
use vecdb::{LazyVecFrom1, ReadableCloneableVec};
|
||||
|
||||
use crate::internal::{FiatPerBlock, FiatType, LazyPreviousDeltaVec};
|
||||
|
||||
/// Per-block fiat data derived from stored cumulative cents.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyFiatBlock<C: FiatType> {
|
||||
pub usd: LazyVecFrom1<Height, Dollars, Height, C>,
|
||||
pub cents: LazyPreviousDeltaVec<Height, C>,
|
||||
}
|
||||
|
||||
impl<C: FiatType> LazyFiatBlock<C> {
|
||||
pub(crate) fn from_cumulative(
|
||||
name: &str,
|
||||
version: Version,
|
||||
cumulative: &FiatPerBlock<C>,
|
||||
) -> Self {
|
||||
let cents = LazyPreviousDeltaVec::new(
|
||||
&format!("{name}_cents"),
|
||||
version,
|
||||
cumulative.cents.height.read_only_boxed_clone(),
|
||||
);
|
||||
let usd =
|
||||
LazyVecFrom1::transformed::<C::ToDollars>(name, version, cents.read_only_boxed_clone());
|
||||
|
||||
Self { usd, cents }
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
mod base;
|
||||
mod block;
|
||||
mod cumulative_sum;
|
||||
mod cumulative_sum_with_deltas;
|
||||
mod lazy;
|
||||
mod lazy_block;
|
||||
mod lazy_rolling_sum;
|
||||
mod with_deltas;
|
||||
pub use base::*;
|
||||
pub use block::*;
|
||||
pub use cumulative_sum::*;
|
||||
pub use cumulative_sum_with_deltas::*;
|
||||
pub use lazy::*;
|
||||
pub use lazy_block::*;
|
||||
pub use lazy_rolling_sum::*;
|
||||
pub use with_deltas::*;
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Cents, Dollars, Height, Sats, Version};
|
||||
use vecdb::{
|
||||
Database, EagerVec, Exit, ImportableVec, LazyVecFrom1, PcoVec, ReadableCloneableVec, Rw,
|
||||
StorageMode,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
internal::{CentsUnsignedToDollars, SatsToBitcoin, SatsToCents},
|
||||
price,
|
||||
};
|
||||
|
||||
/// Raw per-block amount data: sats + cents (stored), btc + usd (lazy), no resolutions.
|
||||
#[derive(Traversable)]
|
||||
pub struct ValueBlock<M: StorageMode = Rw> {
|
||||
pub btc: LazyVecFrom1<Height, Bitcoin, Height, Sats>,
|
||||
pub sats: M::Stored<EagerVec<PcoVec<Height, Sats>>>,
|
||||
pub usd: LazyVecFrom1<Height, Dollars, Height, Cents>,
|
||||
pub cents: M::Stored<EagerVec<PcoVec<Height, Cents>>>,
|
||||
}
|
||||
|
||||
impl ValueBlock {
|
||||
pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result<Self> {
|
||||
let sats: EagerVec<PcoVec<Height, Sats>> =
|
||||
EagerVec::forced_import(db, &format!("{name}_sats"), version)?;
|
||||
let btc =
|
||||
LazyVecFrom1::transformed::<SatsToBitcoin>(name, version, sats.read_only_boxed_clone());
|
||||
let cents: EagerVec<PcoVec<Height, Cents>> =
|
||||
EagerVec::forced_import(db, &format!("{name}_cents"), version)?;
|
||||
let usd = LazyVecFrom1::transformed::<CentsUnsignedToDollars>(
|
||||
&format!("{name}_usd"),
|
||||
version,
|
||||
cents.read_only_boxed_clone(),
|
||||
);
|
||||
Ok(Self {
|
||||
btc,
|
||||
sats,
|
||||
usd,
|
||||
cents,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute_cents(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
prices: &price::Vecs,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.cents.compute_binary::<Sats, Cents, SatsToCents>(
|
||||
max_from,
|
||||
&self.sats,
|
||||
&prices.spot.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,23 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Sats, Version};
|
||||
use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
|
||||
use vecdb::{
|
||||
AnyVec, BinaryTransform, Database, Exit, ReadableVec, Rw, StorageMode, VecIndex, VecValue,
|
||||
WritableVec,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ValueBlock, ValuePerBlock},
|
||||
internal::{LazyValueBlock, SatsToCents, ValuePerBlock},
|
||||
price,
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct ValuePerBlockCumulative<M: StorageMode = Rw> {
|
||||
pub block: ValueBlock<M>,
|
||||
pub block: LazyValueBlock,
|
||||
pub cumulative: ValuePerBlock<M>,
|
||||
#[traversable(skip)]
|
||||
last_cumulative_sats: Option<(usize, Sats)>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ONE;
|
||||
@@ -25,47 +30,278 @@ impl ValuePerBlockCumulative {
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
let cumulative =
|
||||
ValuePerBlock::forced_import(db, &format!("{name}_cumulative"), v, indexes)?;
|
||||
let last_cumulative_sats = cumulative
|
||||
.sats
|
||||
.height
|
||||
.collect_last()
|
||||
.map(|value| (cumulative.sats.height.len(), value));
|
||||
let block = LazyValueBlock::from_cumulative(name, v, &cumulative);
|
||||
|
||||
Ok(Self {
|
||||
block: ValueBlock::forced_import(db, name, v)?,
|
||||
cumulative: ValuePerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_cumulative"),
|
||||
v,
|
||||
indexes,
|
||||
)?,
|
||||
block,
|
||||
cumulative,
|
||||
last_cumulative_sats,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
#[inline(always)]
|
||||
pub(crate) fn push_block_sats(&mut self, value: Sats) {
|
||||
let len = self.cumulative.sats.height.len();
|
||||
let mut cumulative = match self.last_cumulative_sats {
|
||||
Some((cached_len, value)) if cached_len == len => value,
|
||||
_ => self
|
||||
.cumulative
|
||||
.sats
|
||||
.height
|
||||
.collect_last()
|
||||
.unwrap_or_default(),
|
||||
};
|
||||
cumulative += value;
|
||||
self.cumulative.sats.height.push(cumulative);
|
||||
self.last_cumulative_sats = Some((len + 1, cumulative));
|
||||
}
|
||||
|
||||
pub(crate) fn compute_from<S>(
|
||||
&mut self,
|
||||
prices: &price::Vecs,
|
||||
max_from: Height,
|
||||
prices: &price::Vecs,
|
||||
source: &impl ReadableVec<Height, S>,
|
||||
transform: impl FnMut(Height, S) -> Sats,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.cumulative
|
||||
.sats
|
||||
.height
|
||||
.compute_cumulative(max_from, &self.block.sats, exit)?;
|
||||
) -> Result<()>
|
||||
where
|
||||
S: VecValue,
|
||||
{
|
||||
self.compute_sats_from(max_from, source, transform, exit)?;
|
||||
self.compute_cents(max_from, prices, exit)
|
||||
}
|
||||
|
||||
self.block.compute_cents(max_from, prices, exit)?;
|
||||
pub(crate) fn compute_from_pair<S1, S2>(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
prices: &price::Vecs,
|
||||
source1: &impl ReadableVec<Height, S1>,
|
||||
source2: &impl ReadableVec<Height, S2>,
|
||||
transform: impl FnMut(Height, S1, S2) -> Sats,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
S1: VecValue,
|
||||
S2: VecValue,
|
||||
{
|
||||
self.compute_sats_from_pair(max_from, source1, source2, transform, exit)?;
|
||||
self.compute_cents(max_from, prices, exit)
|
||||
}
|
||||
|
||||
self.cumulative
|
||||
.cents
|
||||
.height
|
||||
.compute_cumulative(max_from, &self.block.cents, exit)?;
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn compute_from_indexes<A, B>(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
prices: &price::Vecs,
|
||||
first_indexes: &impl ReadableVec<Height, A>,
|
||||
indexes_count: &impl ReadableVec<Height, B>,
|
||||
source: &impl ReadableVec<A, Sats>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
A: VecIndex + VecValue,
|
||||
B: VecValue,
|
||||
usize: From<B>,
|
||||
{
|
||||
self.compute_sats_from_indexes(
|
||||
max_from,
|
||||
first_indexes,
|
||||
indexes_count,
|
||||
source,
|
||||
|_| true,
|
||||
exit,
|
||||
)?;
|
||||
self.compute_cents(max_from, prices, exit)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn compute_filtered_from_indexes<A, B>(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
prices: &price::Vecs,
|
||||
first_indexes: &impl ReadableVec<Height, A>,
|
||||
indexes_count: &impl ReadableVec<Height, B>,
|
||||
source: &impl ReadableVec<A, Sats>,
|
||||
filter: impl FnMut(&Sats) -> bool,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
A: VecIndex + VecValue,
|
||||
B: VecValue,
|
||||
usize: From<B>,
|
||||
{
|
||||
self.compute_sats_from_indexes(
|
||||
max_from,
|
||||
first_indexes,
|
||||
indexes_count,
|
||||
source,
|
||||
filter,
|
||||
exit,
|
||||
)?;
|
||||
self.compute_cents(max_from, prices, exit)
|
||||
}
|
||||
|
||||
fn compute_sats_from<S>(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
source: &impl ReadableVec<Height, S>,
|
||||
mut transform: impl FnMut(Height, S) -> Sats,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
S: VecValue,
|
||||
{
|
||||
let mut cumulative = None;
|
||||
self.cumulative.sats.height.compute_transform(
|
||||
max_from,
|
||||
source,
|
||||
|(height, value, this)| {
|
||||
let cumulative = cumulative.get_or_insert_with(|| {
|
||||
height
|
||||
.decremented()
|
||||
.and_then(|height| this.collect_one(height))
|
||||
.unwrap_or_default()
|
||||
});
|
||||
*cumulative += transform(height, value);
|
||||
(height, *cumulative)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
self.last_cumulative_sats = None;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn compute_with(
|
||||
fn compute_sats_from_pair<S1, S2>(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
source1: &impl ReadableVec<Height, S1>,
|
||||
source2: &impl ReadableVec<Height, S2>,
|
||||
mut transform: impl FnMut(Height, S1, S2) -> Sats,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
S1: VecValue,
|
||||
S2: VecValue,
|
||||
{
|
||||
let mut cumulative = None;
|
||||
self.cumulative.sats.height.compute_transform2(
|
||||
max_from,
|
||||
source1,
|
||||
source2,
|
||||
|(height, value1, value2, this)| {
|
||||
let cumulative = cumulative.get_or_insert_with(|| {
|
||||
height
|
||||
.decremented()
|
||||
.and_then(|height| this.collect_one(height))
|
||||
.unwrap_or_default()
|
||||
});
|
||||
*cumulative += transform(height, value1, value2);
|
||||
(height, *cumulative)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
self.last_cumulative_sats = None;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compute_sats_from_indexes<A, B>(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
first_indexes: &impl ReadableVec<Height, A>,
|
||||
indexes_count: &impl ReadableVec<Height, B>,
|
||||
source: &impl ReadableVec<A, Sats>,
|
||||
mut filter: impl FnMut(&Sats) -> bool,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
A: VecIndex + VecValue,
|
||||
B: VecValue,
|
||||
usize: From<B>,
|
||||
{
|
||||
let target = &mut self.cumulative.sats.height;
|
||||
target.validate_computed_version_or_reset(
|
||||
first_indexes.version() + indexes_count.version() + source.version(),
|
||||
)?;
|
||||
target.truncate_if_needed(max_from)?;
|
||||
target.repeat_until_complete(exit, |target| {
|
||||
let skip = target.len();
|
||||
let end = target.batch_end(indexes_count.len());
|
||||
if skip >= end || skip >= first_indexes.len() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let source_start = first_indexes.collect_one_at(skip).unwrap().to_usize();
|
||||
let counts: Vec<usize> = indexes_count
|
||||
.collect_range_at(skip, end)
|
||||
.into_iter()
|
||||
.map(usize::from)
|
||||
.collect();
|
||||
let source_end = source_start + counts.iter().sum::<usize>();
|
||||
let mut cumulative = skip
|
||||
.checked_sub(1)
|
||||
.and_then(|index| target.collect_one_at(index))
|
||||
.unwrap_or_default();
|
||||
let mut group_index = 0;
|
||||
|
||||
while group_index < counts.len() && counts[group_index] == 0 {
|
||||
target.push(cumulative);
|
||||
group_index += 1;
|
||||
}
|
||||
|
||||
if group_index < counts.len() {
|
||||
let mut remaining = counts[group_index];
|
||||
source.fold_range_at(source_start, source_end, Sats::ZERO, |sum, value| {
|
||||
let sum = if filter(&value) { sum + value } else { sum };
|
||||
remaining -= 1;
|
||||
if remaining == 0 {
|
||||
cumulative += sum;
|
||||
target.push(cumulative);
|
||||
group_index += 1;
|
||||
while group_index < counts.len() && counts[group_index] == 0 {
|
||||
target.push(cumulative);
|
||||
group_index += 1;
|
||||
}
|
||||
if group_index < counts.len() {
|
||||
remaining = counts[group_index];
|
||||
}
|
||||
Sats::ZERO
|
||||
} else {
|
||||
sum
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
self.last_cumulative_sats = None;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn compute_cents(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
prices: &price::Vecs,
|
||||
exit: &Exit,
|
||||
compute_sats: impl FnOnce(&mut EagerVec<PcoVec<Height, Sats>>) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
compute_sats(&mut self.block.sats)?;
|
||||
self.compute(prices, max_from, exit)
|
||||
self.cumulative
|
||||
.cents
|
||||
.height
|
||||
.compute_cumulative_transformed_binary(
|
||||
max_from,
|
||||
&self.block.sats,
|
||||
&prices.spot.cents.height,
|
||||
SatsToCents::apply,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Sats, Version};
|
||||
use brk_types::{Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
|
||||
use vecdb::{Database, Exit, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
@@ -60,23 +60,12 @@ impl ValuePerBlockCumulativeRolling {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
prices: &price::Vecs,
|
||||
exit: &Exit,
|
||||
compute_sats: impl FnOnce(&mut EagerVec<PcoVec<Height, Sats>>) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
compute_sats(&mut self.block.sats)?;
|
||||
self.compute_rest(max_from, prices, exit)
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rest(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
prices: &price::Vecs,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.inner.compute(prices, max_from, exit)
|
||||
self.inner.compute_cents(max_from, prices, exit)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Sats, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
|
||||
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode, VecIndex, VecValue};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
@@ -45,18 +45,39 @@ impl ValuePerBlockFull {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn compute_from_indexes<A, B>(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
prices: &price::Vecs,
|
||||
first_indexes: &impl ReadableVec<Height, A>,
|
||||
indexes_count: &impl ReadableVec<Height, B>,
|
||||
source: &impl ReadableVec<A, Sats>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
A: VecIndex + VecValue,
|
||||
B: VecValue,
|
||||
usize: From<B>,
|
||||
{
|
||||
self.inner.compute_from_indexes(
|
||||
max_from,
|
||||
prices,
|
||||
first_indexes,
|
||||
indexes_count,
|
||||
source,
|
||||
exit,
|
||||
)?;
|
||||
self.compute_distribution(max_from, windows, exit)
|
||||
}
|
||||
|
||||
fn compute_distribution(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
exit: &Exit,
|
||||
compute_sats: impl FnOnce(&mut EagerVec<PcoVec<Height, Sats>>) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
compute_sats(&mut self.inner.block.sats)?;
|
||||
|
||||
self.inner.compute_rest(max_from, prices, exit)?;
|
||||
|
||||
self.distribution.compute(
|
||||
max_from,
|
||||
windows,
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Cents, Dollars, Height, Sats, Version};
|
||||
use vecdb::{LazyVecFrom1, ReadableCloneableVec};
|
||||
|
||||
use crate::internal::{CentsUnsignedToDollars, LazyPreviousDeltaVec, SatsToBitcoin, ValuePerBlock};
|
||||
|
||||
/// Per-block amount data derived from stored cumulative sats and cents.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyValueBlock {
|
||||
pub btc: LazyVecFrom1<Height, Bitcoin, Height, Sats>,
|
||||
pub sats: LazyPreviousDeltaVec<Height, Sats>,
|
||||
pub usd: LazyVecFrom1<Height, Dollars, Height, Cents>,
|
||||
pub cents: LazyPreviousDeltaVec<Height, Cents>,
|
||||
}
|
||||
|
||||
impl LazyValueBlock {
|
||||
pub(crate) fn from_cumulative(
|
||||
name: &str,
|
||||
version: Version,
|
||||
cumulative: &ValuePerBlock,
|
||||
) -> Self {
|
||||
let sats = LazyPreviousDeltaVec::new(
|
||||
&format!("{name}_sats"),
|
||||
version,
|
||||
cumulative.sats.height.read_only_boxed_clone(),
|
||||
);
|
||||
let btc =
|
||||
LazyVecFrom1::transformed::<SatsToBitcoin>(name, version, sats.read_only_boxed_clone());
|
||||
let cents = LazyPreviousDeltaVec::new(
|
||||
&format!("{name}_cents"),
|
||||
version,
|
||||
cumulative.cents.height.read_only_boxed_clone(),
|
||||
);
|
||||
let usd = LazyVecFrom1::transformed::<CentsUnsignedToDollars>(
|
||||
&format!("{name}_usd"),
|
||||
version,
|
||||
cents.read_only_boxed_clone(),
|
||||
);
|
||||
|
||||
Self {
|
||||
btc,
|
||||
sats,
|
||||
usd,
|
||||
cents,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
mod base;
|
||||
mod block;
|
||||
mod cumulative;
|
||||
mod cumulative_rolling;
|
||||
mod full;
|
||||
mod lazy;
|
||||
mod lazy_block;
|
||||
mod lazy_derived_resolutions;
|
||||
mod lazy_rolling_avg;
|
||||
mod lazy_rolling_sum;
|
||||
@@ -11,11 +11,11 @@ mod rolling_distribution;
|
||||
mod with_deltas;
|
||||
|
||||
pub use base::*;
|
||||
pub use block::*;
|
||||
pub use cumulative::*;
|
||||
pub use cumulative_rolling::*;
|
||||
pub use full::*;
|
||||
pub use lazy::*;
|
||||
pub use lazy_block::*;
|
||||
pub use lazy_derived_resolutions::*;
|
||||
pub use lazy_rolling_avg::*;
|
||||
pub use lazy_rolling_sum::*;
|
||||
|
||||
@@ -27,67 +27,59 @@ impl Vecs {
|
||||
let window_starts = lookback.window_starts();
|
||||
let (r_coinbase, r_fees) = rayon::join(
|
||||
|| {
|
||||
self.coinbase.compute(starting_height, prices, exit, |vec| {
|
||||
let mut txout_cursor = indexer.vecs.transactions.first_txout_index.cursor();
|
||||
let mut count_cursor = indexes.tx_index.output_count.cursor();
|
||||
self.coinbase.compute_from(
|
||||
starting_height,
|
||||
prices,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
|_, tx_index| {
|
||||
let mut txout_cursor = indexer.vecs.transactions.first_txout_index.cursor();
|
||||
let mut count_cursor = indexes.tx_index.output_count.cursor();
|
||||
|
||||
vec.compute_transform(
|
||||
starting_height,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
|(height, tx_index, ..)| {
|
||||
let ti = tx_index.to_usize();
|
||||
let ti = tx_index.to_usize();
|
||||
|
||||
txout_cursor.advance(ti - txout_cursor.position());
|
||||
let first_txout_index = txout_cursor.next().unwrap().to_usize();
|
||||
txout_cursor.advance(ti - txout_cursor.position());
|
||||
let first_txout_index = txout_cursor.next().unwrap().to_usize();
|
||||
|
||||
count_cursor.advance(ti - count_cursor.position());
|
||||
let output_count: usize = count_cursor.next().unwrap().into();
|
||||
count_cursor.advance(ti - count_cursor.position());
|
||||
let output_count: usize = count_cursor.next().unwrap().into();
|
||||
|
||||
let sats = indexer.vecs.outputs.value.fold_range_at(
|
||||
first_txout_index,
|
||||
first_txout_index + output_count,
|
||||
Sats::ZERO,
|
||||
|acc, v| acc + v,
|
||||
);
|
||||
(height, sats)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})
|
||||
indexer.vecs.outputs.value.fold_range_at(
|
||||
first_txout_index,
|
||||
first_txout_index + output_count,
|
||||
Sats::ZERO,
|
||||
|acc, v| acc + v,
|
||||
)
|
||||
},
|
||||
exit,
|
||||
)
|
||||
},
|
||||
|| {
|
||||
self.fees
|
||||
.compute(starting_height, &window_starts, prices, exit, |vec| {
|
||||
vec.compute_sum_from_indexes(
|
||||
starting_height,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
&indexes.height.tx_index_count,
|
||||
&transactions.fees.fee.tx_index,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})
|
||||
self.fees.compute_from_indexes(
|
||||
starting_height,
|
||||
&window_starts,
|
||||
prices,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
&indexes.height.tx_index_count,
|
||||
&transactions.fees.fee.tx_index,
|
||||
exit,
|
||||
)
|
||||
},
|
||||
);
|
||||
r_coinbase?;
|
||||
r_fees?;
|
||||
|
||||
self.subsidy.block.sats.compute_transform2(
|
||||
self.subsidy.compute_from_pair(
|
||||
starting_height,
|
||||
prices,
|
||||
&self.coinbase.block.sats,
|
||||
&self.fees.block.sats,
|
||||
|(height, coinbase, fees, ..)| {
|
||||
(
|
||||
height,
|
||||
coinbase.checked_sub(fees).unwrap_or_else(|| {
|
||||
panic!("coinbase {coinbase:?} < fees {fees:?} at {height:?}")
|
||||
}),
|
||||
)
|
||||
|height, coinbase, fees| {
|
||||
coinbase.checked_sub(fees).unwrap_or_else(|| {
|
||||
panic!("coinbase {coinbase:?} < fees {fees:?} at {height:?}")
|
||||
})
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
self.subsidy.compute_rest(starting_height, prices, exit)?;
|
||||
|
||||
self.output_volume.compute_subtract(
|
||||
starting_height,
|
||||
@@ -96,17 +88,17 @@ impl Vecs {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.unclaimed.block.sats.compute_transform(
|
||||
self.unclaimed.compute_from(
|
||||
starting_height,
|
||||
prices,
|
||||
&self.subsidy.block.sats,
|
||||
|(height, subsidy, ..)| {
|
||||
|height, subsidy| {
|
||||
let halving = Halving::from(height);
|
||||
let expected = Sats::FIFTY_BTC / 2_usize.pow(halving.to_usize() as u32);
|
||||
(height, expected.checked_sub(subsidy).unwrap())
|
||||
expected.checked_sub(subsidy).unwrap()
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
self.unclaimed.compute(prices, starting_height, exit)?;
|
||||
|
||||
self.fee_dominance
|
||||
.compute_binary::<Sats, Sats, RatioSats<PartsPerMillion32>, _, _, _, _>(
|
||||
|
||||
@@ -14,85 +14,88 @@ impl Vecs {
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let starting_lengths = indexer.safe_lengths();
|
||||
self.op_return
|
||||
.compute_with(starting_lengths.height, prices, exit, |height_vec| {
|
||||
// Validate computed versions against dependencies
|
||||
let dep_version = indexer.vecs.outputs.first_txout_index.version()
|
||||
+ indexer.vecs.outputs.output_type.version()
|
||||
+ indexer.vecs.outputs.value.version();
|
||||
height_vec.validate_computed_version_or_reset(dep_version)?;
|
||||
let height_vec = &mut self.op_return.cumulative.sats.height;
|
||||
|
||||
// Get target height
|
||||
let target_len = indexer.vecs.outputs.first_txout_index.len();
|
||||
if target_len == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
let target_height = Height::from(target_len - 1);
|
||||
// Validate computed versions against dependencies
|
||||
let dep_version = indexer.vecs.outputs.first_txout_index.version()
|
||||
+ indexer.vecs.outputs.output_type.version()
|
||||
+ indexer.vecs.outputs.value.version();
|
||||
height_vec.validate_computed_version_or_reset(dep_version)?;
|
||||
|
||||
// Find starting height for this vec
|
||||
let current_len = height_vec.len();
|
||||
let starting_height =
|
||||
Height::from(current_len.min(starting_lengths.height.to_usize()));
|
||||
// Get target height
|
||||
let target_len = indexer.vecs.outputs.first_txout_index.len();
|
||||
if target_len == 0 {
|
||||
self.op_return
|
||||
.compute_cents(starting_lengths.height, prices, exit)?;
|
||||
return Ok(());
|
||||
}
|
||||
let target_height = Height::from(target_len - 1);
|
||||
|
||||
if starting_height > target_height {
|
||||
return Ok(());
|
||||
}
|
||||
// Find starting height for this vec
|
||||
let current_len = height_vec.len();
|
||||
let starting_height = Height::from(current_len.min(starting_lengths.height.to_usize()));
|
||||
|
||||
// Pre-collect height-indexed data
|
||||
let first_txout_indexes: Vec<TxOutIndex> =
|
||||
indexer.vecs.outputs.first_txout_index.collect_range_at(
|
||||
starting_height.to_usize(),
|
||||
target_height.to_usize()
|
||||
+ 2.min(indexer.vecs.outputs.first_txout_index.len()),
|
||||
);
|
||||
if starting_height <= target_height {
|
||||
// Pre-collect height-indexed data
|
||||
let first_txout_indexes: Vec<TxOutIndex> =
|
||||
indexer.vecs.outputs.first_txout_index.collect_range_at(
|
||||
starting_height.to_usize(),
|
||||
target_height.to_usize() + 2.min(indexer.vecs.outputs.first_txout_index.len()),
|
||||
);
|
||||
|
||||
let mut output_types_buf: Vec<OutputType> = Vec::new();
|
||||
let mut values_buf: Vec<Sats> = Vec::new();
|
||||
let mut output_types_buf: Vec<OutputType> = Vec::new();
|
||||
let mut values_buf: Vec<Sats> = Vec::new();
|
||||
let mut cumulative = starting_height
|
||||
.decremented()
|
||||
.and_then(|height| height_vec.collect_one(height))
|
||||
.unwrap_or_default();
|
||||
|
||||
height_vec.truncate_if_needed(starting_height)?;
|
||||
height_vec.truncate_if_needed(starting_height)?;
|
||||
|
||||
// Iterate blocks
|
||||
for h in starting_height.to_usize()..=target_height.to_usize() {
|
||||
let local_idx = h - starting_height.to_usize();
|
||||
// Iterate blocks
|
||||
for h in starting_height.to_usize()..=target_height.to_usize() {
|
||||
let local_idx = h - starting_height.to_usize();
|
||||
|
||||
// Get output range for this block
|
||||
let first_txout_index = first_txout_indexes[local_idx];
|
||||
let next_first_txout_index =
|
||||
if let Some(&next) = first_txout_indexes.get(local_idx + 1) {
|
||||
next
|
||||
} else {
|
||||
TxOutIndex::from(indexer.vecs.outputs.value.len())
|
||||
};
|
||||
// Get output range for this block
|
||||
let first_txout_index = first_txout_indexes[local_idx];
|
||||
let next_first_txout_index =
|
||||
if let Some(&next) = first_txout_indexes.get(local_idx + 1) {
|
||||
next
|
||||
} else {
|
||||
TxOutIndex::from(indexer.vecs.outputs.value.len())
|
||||
};
|
||||
|
||||
let out_start = first_txout_index.to_usize();
|
||||
let out_end = next_first_txout_index.to_usize();
|
||||
let out_start = first_txout_index.to_usize();
|
||||
let out_end = next_first_txout_index.to_usize();
|
||||
|
||||
// Pre-collect both vecs into reusable buffers
|
||||
indexer.vecs.outputs.output_type.collect_range_into_at(
|
||||
out_start,
|
||||
out_end,
|
||||
&mut output_types_buf,
|
||||
);
|
||||
indexer.vecs.outputs.value.collect_range_into_at(
|
||||
out_start,
|
||||
out_end,
|
||||
&mut values_buf,
|
||||
);
|
||||
// Pre-collect both vecs into reusable buffers
|
||||
indexer.vecs.outputs.output_type.collect_range_into_at(
|
||||
out_start,
|
||||
out_end,
|
||||
&mut output_types_buf,
|
||||
);
|
||||
indexer.vecs.outputs.value.collect_range_into_at(
|
||||
out_start,
|
||||
out_end,
|
||||
&mut values_buf,
|
||||
);
|
||||
|
||||
let mut op_return_value = Sats::ZERO;
|
||||
for (ot, val) in output_types_buf.iter().zip(values_buf.iter()) {
|
||||
if *ot == OutputType::OpReturn {
|
||||
op_return_value += *val;
|
||||
}
|
||||
let mut op_return_value = Sats::ZERO;
|
||||
for (ot, val) in output_types_buf.iter().zip(values_buf.iter()) {
|
||||
if *ot == OutputType::OpReturn {
|
||||
op_return_value += *val;
|
||||
}
|
||||
|
||||
height_vec.push(op_return_value);
|
||||
}
|
||||
|
||||
height_vec.write()?;
|
||||
cumulative += op_return_value;
|
||||
height_vec.push(cumulative);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
height_vec.write()?;
|
||||
}
|
||||
|
||||
self.op_return
|
||||
.compute_cents(starting_lengths.height, prices, exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -87,16 +87,14 @@ impl Vecs {
|
||||
)?;
|
||||
}
|
||||
|
||||
self.rewards.compute(starting_height, prices, exit, |vec| {
|
||||
Ok(vec.compute_transform2(
|
||||
starting_height,
|
||||
&self.base.blocks_mined.block,
|
||||
&mining.rewards.coinbase.block.sats,
|
||||
|(h, mask, val, ..)| (h, MaskSats::apply(mask, val)),
|
||||
exit,
|
||||
)?)
|
||||
})?;
|
||||
|
||||
self.rewards.compute_from_pair(
|
||||
starting_height,
|
||||
prices,
|
||||
&self.base.blocks_mined.block,
|
||||
&mining.rewards.coinbase.block.sats,
|
||||
|_, mask, value| MaskSats::apply(mask, value),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,24 +17,21 @@ impl Vecs {
|
||||
) -> Result<()> {
|
||||
let starting_height = indexer.safe_lengths().height;
|
||||
|
||||
self.total
|
||||
.compute_with(starting_height, prices, exit, |sats| {
|
||||
Ok(sats.compute_transform2(
|
||||
starting_height,
|
||||
&outputs.value.op_return.block.sats,
|
||||
&mining.rewards.unclaimed.block.sats,
|
||||
|(h, op_return, unclaimed, ..)| {
|
||||
let genesis = if h.to_usize() == 0 {
|
||||
Sats::FIFTY_BTC
|
||||
} else {
|
||||
Sats::ZERO
|
||||
};
|
||||
(h, genesis + op_return + unclaimed)
|
||||
},
|
||||
exit,
|
||||
)?)
|
||||
})?;
|
||||
|
||||
self.total.compute_from_pair(
|
||||
starting_height,
|
||||
prices,
|
||||
&outputs.value.op_return.block.sats,
|
||||
&mining.rewards.unclaimed.block.sats,
|
||||
|height, op_return, unclaimed| {
|
||||
let genesis = if height.to_usize() == 0 {
|
||||
Sats::FIFTY_BTC
|
||||
} else {
|
||||
Sats::ZERO
|
||||
};
|
||||
genesis + op_return + unclaimed
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,18 +20,15 @@ impl Vecs {
|
||||
) -> Result<()> {
|
||||
let starting_height = indexer.safe_lengths().height;
|
||||
|
||||
self.transfer_volume
|
||||
.compute(starting_height, prices, exit, |sats_vec| {
|
||||
Ok(sats_vec.compute_filtered_sum_from_indexes(
|
||||
starting_height,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
&indexes.height.tx_index_count,
|
||||
&fees_vecs.input_value,
|
||||
|sats| !sats.is_max(),
|
||||
exit,
|
||||
)?)
|
||||
})?;
|
||||
|
||||
self.transfer_volume.compute_filtered_from_indexes(
|
||||
starting_height,
|
||||
prices,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
&indexes.height.tx_index_count,
|
||||
&fees_vecs.input_value,
|
||||
|sats| !sats.is_max(),
|
||||
exit,
|
||||
)?;
|
||||
let tx_sums = count_vecs.total.rolling.sum.0.as_array();
|
||||
let tx_per_sec = self.tx_per_sec.as_mut_array();
|
||||
for (i, &secs) in Windows::<()>::SECS.iter().enumerate() {
|
||||
|
||||
Reference in New Issue
Block a user