computer: snapshot

This commit is contained in:
nym21
2025-12-01 23:23:35 +01:00
parent 35e567cfb6
commit b6ec133368
13 changed files with 937 additions and 228 deletions

View File

@@ -5,6 +5,7 @@ mod from_dateindex;
mod from_height;
mod from_height_strict;
mod from_txindex;
mod price_percentiles;
mod ratio_from_dateindex;
mod sd_from_dateindex;
mod source;
@@ -20,6 +21,7 @@ pub use from_dateindex::*;
pub use from_height::*;
pub use from_height_strict::*;
pub use from_txindex::*;
pub use price_percentiles::*;
pub use ratio_from_dateindex::*;
pub use sd_from_dateindex::*;
pub use source::*;

View File

@@ -0,0 +1,94 @@
use brk_error::Result;
use brk_traversable::{Traversable, TreeNode};
use brk_types::{Dollars, Height, Version};
use vecdb::{AnyExportableVec, Database, EagerVec, Exit, PcoVec};
use crate::{indexes, Indexes};
use super::{ComputedVecsFromHeight, Source, VecBuilderOptions};
pub const PERCENTILES: [u8; 21] = [
0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100,
];
pub const PERCENTILES_LEN: usize = PERCENTILES.len();
#[derive(Clone)]
pub struct PricePercentiles {
pub vecs: [Option<ComputedVecsFromHeight<Dollars>>; PERCENTILES_LEN],
}
const VERSION: Version = Version::ZERO;
impl PricePercentiles {
pub fn forced_import(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
compute: bool,
) -> Result<Self> {
let vecs = PERCENTILES.map(|p| {
compute.then(|| {
ComputedVecsFromHeight::forced_import(
db,
&format!("{name}_price_pct{p:02}"),
Source::Compute,
version + VERSION,
indexes,
VecBuilderOptions::default().add_last(),
)
.unwrap()
})
});
Ok(Self { vecs })
}
pub fn truncate_push(
&mut self,
height: Height,
percentile_prices: &[Dollars; PERCENTILES_LEN],
) -> Result<()> {
for (i, vec) in self.vecs.iter_mut().enumerate() {
if let Some(v) = vec {
v.height.as_mut().unwrap().truncate_push(height, percentile_prices[i])?;
}
}
Ok(())
}
pub fn compute_rest(
&mut self,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
for vec in self.vecs.iter_mut().flatten() {
vec.compute_rest(indexes, starting_indexes, exit, None::<&EagerVec<PcoVec<Height, Dollars>>>)?;
}
Ok(())
}
pub fn get(&self, percentile: u8) -> Option<&ComputedVecsFromHeight<Dollars>> {
PERCENTILES
.iter()
.position(|&p| p == percentile)
.and_then(|i| self.vecs[i].as_ref())
}
}
impl Traversable for PricePercentiles {
fn to_tree_node(&self) -> TreeNode {
TreeNode::Branch(
PERCENTILES
.iter()
.zip(self.vecs.iter())
.filter_map(|(p, v)| v.as_ref().map(|v| (format!("pct{p:02}"), v.to_tree_node())))
.collect(),
)
}
fn iter_any_exportable(&self) -> impl Iterator<Item = &dyn AnyExportableVec> {
self.vecs.iter().flatten().flat_map(|p| p.iter_any_exportable())
}
}

View File

@@ -229,8 +229,6 @@ impl Computer {
)?;
info!("Computed pools in {:?}", i.elapsed());
return Ok(());
info!("Computing stateful...");
self.stateful.compute(
indexer,
@@ -241,6 +239,8 @@ impl Computer {
exit,
)?;
return Ok(());
info!("Computing cointime...");
self.cointime.compute(
&self.indexes,

View File

@@ -1,9 +1,10 @@
use std::mem;
use std::{collections::hash_map::Entry, mem};
use brk_grouper::ByAddressType;
use brk_types::{OutputType, TypeIndex};
use derive_deref::{Deref, DerefMut};
use rustc_hash::FxHashMap;
use smallvec::{Array, SmallVec};
#[derive(Debug, Deref, DerefMut)]
pub struct AddressTypeToTypeIndexMap<T>(ByAddressType<FxHashMap<TypeIndex, T>>);
@@ -44,12 +45,17 @@ impl<T> AddressTypeToTypeIndexMap<T> {
}
pub fn into_sorted_iter(self) -> impl Iterator<Item = (OutputType, Vec<(TypeIndex, T)>)> {
self.0.into_iter_typed().map(|(output_type, map)| {
self.0.into_iter().map(|(output_type, map)| {
let mut sorted: Vec<_> = map.into_iter().collect();
sorted.sort_unstable_by_key(|(typeindex, _)| *typeindex);
(output_type, sorted)
})
}
#[allow(clippy::should_implement_trait)]
pub fn into_iter(self) -> impl Iterator<Item = (OutputType, FxHashMap<TypeIndex, T>)> {
self.0.into_iter()
}
}
impl<T> Default for AddressTypeToTypeIndexMap<T> {
@@ -67,23 +73,26 @@ impl<T> Default for AddressTypeToTypeIndexMap<T> {
}
}
impl<T> AddressTypeToTypeIndexMap<Vec<T>>
impl<T> AddressTypeToTypeIndexMap<SmallVec<T>>
where
T: Copy,
T: Array,
{
pub fn merge_vec(mut self, other: Self) -> Self {
for (address_type, other_map) in other.0.into_iter_typed() {
for (address_type, other_map) in other.0.into_iter() {
let self_map = self.0.get_mut_unwrap(address_type);
for (typeindex, mut other_vec) in other_map {
self_map
.entry(typeindex)
.and_modify(|self_vec| {
match self_map.entry(typeindex) {
Entry::Occupied(mut entry) => {
let self_vec = entry.get_mut();
if other_vec.len() > self_vec.len() {
mem::swap(self_vec, &mut other_vec);
}
self_vec.extend(other_vec.iter().copied());
})
.or_insert(other_vec);
self_vec.extend(other_vec);
}
Entry::Vacant(entry) => {
entry.insert(other_vec);
}
}
}
}
self

View File

@@ -12,7 +12,8 @@ use crate::{
Indexes,
grouped::{
ComputedHeightValueVecs, ComputedRatioVecsFromDateIndex, ComputedValueVecsFromDateIndex,
ComputedVecsFromDateIndex, ComputedVecsFromHeight, Source, VecBuilderOptions,
ComputedValueVecsFromHeight, ComputedVecsFromDateIndex, ComputedVecsFromHeight,
PricePercentiles, Source, VecBuilderOptions,
},
indexes, price,
states::CohortState,
@@ -41,9 +42,11 @@ pub struct Vecs {
pub height_to_unrealized_profit: Option<EagerVec<PcoVec<Height, Dollars>>>,
pub height_to_value_created: Option<EagerVec<PcoVec<Height, Dollars>>>,
pub height_to_value_destroyed: Option<EagerVec<PcoVec<Height, Dollars>>>,
pub height_to_sent: EagerVec<PcoVec<Height, Sats>>,
pub height_to_satblocks_destroyed: EagerVec<PcoVec<Height, Sats>>,
pub height_to_satdays_destroyed: EagerVec<PcoVec<Height, Sats>>,
pub indexes_to_sent: ComputedValueVecsFromHeight,
pub indexes_to_coinblocks_destroyed: ComputedVecsFromHeight<StoredF64>,
pub indexes_to_coindays_destroyed: ComputedVecsFromHeight<StoredF64>,
pub dateindex_to_sopr: Option<EagerVec<PcoVec<DateIndex, StoredF64>>>,
@@ -79,6 +82,7 @@ pub struct Vecs {
pub indexes_to_total_realized_pnl: Option<ComputedVecsFromDateIndex<Dollars>>,
pub indexes_to_min_price_paid: Option<ComputedVecsFromHeight<Dollars>>,
pub indexes_to_max_price_paid: Option<ComputedVecsFromHeight<Dollars>>,
pub price_percentiles: Option<PricePercentiles>,
pub height_to_supply_half_value: ComputedHeightValueVecs,
pub indexes_to_supply_half: ComputedValueVecsFromDateIndex,
pub height_to_neg_unrealized_loss: Option<EagerVec<PcoVec<Height, Dollars>>>,
@@ -356,6 +360,16 @@ impl Vecs {
)
.unwrap()
}),
price_percentiles: (compute_dollars && extended).then(|| {
PricePercentiles::forced_import(
db,
&suffix(""),
version + Version::ZERO,
indexes,
true,
)
.unwrap()
}),
height_to_supply: EagerVec::forced_import(
db,
&suffix("supply"),
@@ -1075,6 +1089,11 @@ impl Vecs {
)
.unwrap()
}),
height_to_sent: EagerVec::forced_import(
db,
&suffix("sent"),
version + Version::ZERO,
)?,
height_to_satblocks_destroyed: EagerVec::forced_import(
db,
&suffix("satblocks_destroyed"),
@@ -1085,6 +1104,15 @@ impl Vecs {
&suffix("satdays_destroyed"),
version + Version::ZERO,
)?,
indexes_to_sent: ComputedValueVecsFromHeight::forced_import(
db,
&suffix("sent"),
Source::Compute,
version + Version::ZERO,
VecBuilderOptions::default().add_sum(),
compute_dollars,
indexes,
)?,
indexes_to_coinblocks_destroyed: ComputedVecsFromHeight::forced_import(
db,
&suffix("coinblocks_destroyed"),
@@ -1190,6 +1218,7 @@ impl Vecs {
self.height_to_max_price_paid
.as_ref()
.map_or(usize::MAX, |v| v.len()),
self.height_to_sent.len(),
self.height_to_satdays_destroyed.len(),
self.height_to_satblocks_destroyed.len(),
]
@@ -1235,6 +1264,11 @@ impl Vecs {
base_version + self.height_to_utxo_count.inner_version(),
)?;
self.height_to_sent
.validate_computed_version_or_reset(
base_version + self.height_to_sent.inner_version(),
)?;
self.height_to_satblocks_destroyed
.validate_computed_version_or_reset(
base_version + self.height_to_satblocks_destroyed.inner_version(),
@@ -1441,6 +1475,8 @@ impl Vecs {
self.height_to_utxo_count
.truncate_push(height, StoredU64::from(state.supply.utxo_count))?;
self.height_to_sent.truncate_push(height, state.sent)?;
self.height_to_satblocks_destroyed
.truncate_push(height, state.satblocks_destroyed)?;
@@ -1569,6 +1605,7 @@ impl Vecs {
) -> Result<()> {
self.height_to_supply.safe_flush(exit)?;
self.height_to_utxo_count.safe_flush(exit)?;
self.height_to_sent.safe_flush(exit)?;
self.height_to_satdays_destroyed.safe_flush(exit)?;
self.height_to_satblocks_destroyed.safe_flush(exit)?;
@@ -1672,6 +1709,15 @@ impl Vecs {
.as_slice(),
exit,
)?;
self.height_to_sent.compute_sum_of_others(
starting_indexes.height,
others
.iter()
.map(|v| &v.height_to_sent)
.collect::<Vec<_>>()
.as_slice(),
exit,
)?;
self.height_to_satblocks_destroyed.compute_sum_of_others(
starting_indexes.height,
others
@@ -1999,6 +2045,9 @@ impl Vecs {
Ok(())
})?;
self.indexes_to_sent
.compute_rest(indexes, price, starting_indexes, exit, Some(&self.height_to_sent))?;
self.indexes_to_coinblocks_destroyed
.compute_all(indexes, starting_indexes, exit, |v| {
v.compute_transform(

View File

@@ -778,7 +778,6 @@ impl Vecs {
let mut height_to_first_p2wshaddressindex_iter =
height_to_first_p2wshaddressindex.into_iter();
let mut height_to_first_txindex_iter = height_to_first_txindex.into_iter();
let mut height_to_txindex_count_iter = height_to_txindex_count.into_iter();
let mut height_to_first_txinindex_iter = height_to_first_txinindex.into_iter();
let mut height_to_first_txoutindex_iter = height_to_first_txoutindex.into_iter();
let mut height_to_input_count_iter = height_to_input_count.into_iter();
@@ -908,17 +907,21 @@ impl Vecs {
mut height_to_sent,
addresstype_to_typedindex_to_sent_data,
mut stored_or_new_addresstype_to_typeindex_to_addressdatawithsource,
mut combined_txindex_vecs,
) = thread::scope(|scope| {
scope.spawn(|| {
self.utxo_cohorts
.tick_tock_next_block(&chain_state, timestamp);
});
let (transacted, addresstype_to_typedindex_to_received_data, receiving_addresstype_to_typeindex_to_addressdatawithsource) = (first_txoutindex..first_txoutindex + usize::from(output_count))
let (transacted, addresstype_to_typedindex_to_received_data, receiving_addresstype_to_typeindex_to_addressdatawithsource, output_txindex_vecs) = (first_txoutindex..first_txoutindex + usize::from(output_count))
.into_par_iter()
.map(|i| {
let txoutindex = TxOutIndex::from(i);
let local_idx = i - first_txoutindex;
let txindex = txoutindex_to_txindex[local_idx];
let value = txoutindex_to_value
.read_unwrap(txoutindex, &ir.txoutindex_to_value);
@@ -926,7 +929,7 @@ impl Vecs {
.read_unwrap(txoutindex, &ir.txoutindex_to_outputtype);
if output_type.is_not_address() {
return (value, output_type, None);
return (txindex, value, output_type, None);
}
let typeindex = txoutindex_to_typeindex
@@ -943,17 +946,19 @@ impl Vecs {
&self.addresses_data,
);
(value, output_type, Some((typeindex, addressdata_opt)))
(txindex, value, output_type, Some(( typeindex, addressdata_opt)))
}).fold(
|| {
(
Transacted::default(),
AddressTypeToVec::<(TypeIndex, Sats)>::default(),
AddressTypeToTypeIndexMap::default()
AddressTypeToTypeIndexMap::default(),
AddressTypeToTypeIndexMap::<TxIndexVec>::default(),
)
},
|(mut transacted, mut addresstype_to_typedindex_to_data, mut addresstype_to_typeindex_to_addressdatawithsource),
|(mut transacted, mut addresstype_to_typedindex_to_data, mut addresstype_to_typeindex_to_addressdatawithsource, mut txindex_vecs),
(
txindex,
value,
output_type,
typeindex_with_addressdata_opt,
@@ -967,23 +972,33 @@ impl Vecs {
.insert_for_type(output_type, typeindex, addressdata);
}
let addr_type = output_type;
addresstype_to_typedindex_to_data
.get_mut(output_type)
.get_mut(addr_type)
.unwrap()
.push((typeindex, value));
txindex_vecs
.get_mut(addr_type)
.unwrap()
.entry(typeindex)
.or_insert_with(TxIndexVec::new)
.push(txindex);
}
(transacted, addresstype_to_typedindex_to_data, addresstype_to_typeindex_to_addressdatawithsource)
(transacted, addresstype_to_typedindex_to_data, addresstype_to_typeindex_to_addressdatawithsource, txindex_vecs)
}).reduce(
|| {
(
Transacted::default(),
AddressTypeToVec::<(TypeIndex, Sats)>::default(),
AddressTypeToTypeIndexMap::default()
AddressTypeToTypeIndexMap::default(),
AddressTypeToTypeIndexMap::<TxIndexVec>::default(),
)
},
|(transacted, addresstype_to_typedindex_to_data, addresstype_to_typeindex_to_addressdatawithsource), (transacted2, addresstype_to_typedindex_to_data2, addresstype_to_typeindex_to_addressdatawithsource2)| {
(transacted + transacted2, addresstype_to_typedindex_to_data.merge(addresstype_to_typedindex_to_data2), addresstype_to_typeindex_to_addressdatawithsource.merge(addresstype_to_typeindex_to_addressdatawithsource2))
|(transacted, addresstype_to_typedindex_to_data, addresstype_to_typeindex_to_addressdatawithsource, txindex_vecs), (transacted2, addresstype_to_typedindex_to_data2, addresstype_to_typeindex_to_addressdatawithsource2, txindex_vecs2)| {
(transacted + transacted2, addresstype_to_typedindex_to_data.merge(addresstype_to_typedindex_to_data2), addresstype_to_typeindex_to_addressdatawithsource.merge(addresstype_to_typeindex_to_addressdatawithsource2), txindex_vecs.merge_vec(txindex_vecs2))
},
);
@@ -992,185 +1007,230 @@ impl Vecs {
height_to_sent,
addresstype_to_typedindex_to_sent_data,
sending_addresstype_to_typeindex_to_addressdatawithsource,
) =
(first_txinindex + 1..first_txinindex + usize::from(input_count))
.into_par_iter()
.map(|i| {
let txinindex = TxInIndex::from(i);
input_txindex_vecs,
) = (first_txinindex + 1..first_txinindex + usize::from(input_count))
.into_par_iter()
.map(|i| {
let txinindex = TxInIndex::from(i);
let outpoint = txinindex_to_outpoint
.read_unwrap(txinindex, &ir.txinindex_to_outpoint);
let local_idx = i - first_txinindex;
let txindex = txinindex_to_txindex[local_idx];
let txoutindex = txindex_to_first_txoutindex.read_unwrap(
outpoint.txindex(),
&ir.txindex_to_first_txoutindex,
) + outpoint.vout();
let outpoint = txinindex_to_outpoint
.read_unwrap(txinindex, &ir.txinindex_to_outpoint);
let value = txoutindex_to_value
.read_unwrap(txoutindex, &ir.txoutindex_to_value);
let txoutindex = txindex_to_first_txoutindex
.read_unwrap(outpoint.txindex(), &ir.txindex_to_first_txoutindex)
+ outpoint.vout();
let input_type = txoutindex_to_outputtype
.read_unwrap(txoutindex, &ir.txoutindex_to_outputtype);
let value = txoutindex_to_value
.read_unwrap(txoutindex, &ir.txoutindex_to_value);
let prev_height =
*txoutindex_range_to_height.get(txoutindex).unwrap();
let input_type = txoutindex_to_outputtype
.read_unwrap(txoutindex, &ir.txoutindex_to_outputtype);
if input_type.is_not_address() {
return (prev_height, value, input_type, None);
}
let prev_height = *txoutindex_range_to_height.get(txoutindex).unwrap();
let typeindex = txoutindex_to_typeindex
.read_unwrap(txoutindex, &ir.txoutindex_to_typeindex);
if input_type.is_not_address() {
return (txindex, prev_height, value, input_type, None);
}
let addressdata_opt = Self::get_addressdatawithsource(
input_type,
typeindex,
&first_addressindexes,
&addresstype_to_typeindex_to_loadedaddressdata,
&addresstype_to_typeindex_to_emptyaddressdata,
&vr,
&self.any_address_indexes,
&self.addresses_data,
);
let typeindex = txoutindex_to_typeindex
.read_unwrap(txoutindex, &ir.txoutindex_to_typeindex);
let addressdata_opt = Self::get_addressdatawithsource(
input_type,
typeindex,
&first_addressindexes,
&addresstype_to_typeindex_to_loadedaddressdata,
&addresstype_to_typeindex_to_emptyaddressdata,
&vr,
&self.any_address_indexes,
&self.addresses_data,
);
(
txindex,
prev_height,
value,
input_type,
Some((typeindex, addressdata_opt)),
)
})
.fold(
|| {
(
prev_height,
value,
input_type,
Some((typeindex, addressdata_opt)),
FxHashMap::<Height, Transacted>::default(),
HeightToAddressTypeToVec::<(TypeIndex, Sats)>::default(),
AddressTypeToTypeIndexMap::default(),
AddressTypeToTypeIndexMap::<TxIndexVec>::default(),
)
})
.fold(
|| {
(
FxHashMap::<Height, Transacted>::default(),
HeightToAddressTypeToVec::<(TypeIndex, Sats)>::default(),
AddressTypeToTypeIndexMap::default(),
)
},
|(
mut height_to_transacted,
mut height_to_addresstype_to_typedindex_to_data,
mut addresstype_to_typeindex_to_addressdatawithsource,
),
(
prev_height,
value,
output_type,
typeindex_with_addressdata_opt,
)| {
height_to_transacted
.entry(prev_height)
.or_default()
.iterate(value, output_type);
},
|(
mut height_to_transacted,
mut height_to_addresstype_to_typedindex_to_data,
mut addresstype_to_typeindex_to_addressdatawithsource,
mut txindex_vecs,
),
(
txindex,
prev_height,
value,
output_type,
typeindex_with_addressdata_opt,
)| {
height_to_transacted
.entry(prev_height)
.or_default()
.iterate(value, output_type);
if let Some((typeindex, addressdata_opt)) =
typeindex_with_addressdata_opt
{
if let Some(addressdata) = addressdata_opt {
addresstype_to_typeindex_to_addressdatawithsource
.insert_for_type(
output_type,
typeindex,
addressdata,
);
}
height_to_addresstype_to_typedindex_to_data
.entry(prev_height)
.or_default()
.get_mut(output_type)
.unwrap()
.push((typeindex, value));
if let Some((typeindex, addressdata_opt)) =
typeindex_with_addressdata_opt
{
if let Some(addressdata) = addressdata_opt {
addresstype_to_typeindex_to_addressdatawithsource
.insert_for_type(output_type, typeindex, addressdata);
}
let addr_type = output_type;
height_to_addresstype_to_typedindex_to_data
.entry(prev_height)
.or_default()
.get_mut(addr_type)
.unwrap()
.push((typeindex, value));
txindex_vecs
.get_mut(addr_type)
.unwrap()
.entry(typeindex)
.or_insert_with(TxIndexVec::new)
.push(txindex);
}
(
height_to_transacted,
height_to_addresstype_to_typedindex_to_data,
addresstype_to_typeindex_to_addressdatawithsource,
txindex_vecs,
)
},
)
.reduce(
|| {
(
FxHashMap::<Height, Transacted>::default(),
HeightToAddressTypeToVec::<(TypeIndex, Sats)>::default(),
AddressTypeToTypeIndexMap::default(),
AddressTypeToTypeIndexMap::<TxIndexVec>::default(),
)
},
|(
height_to_transacted,
addresstype_to_typedindex_to_data,
addresstype_to_typeindex_to_addressdatawithsource,
txindex_vecs,
),
(
height_to_transacted2,
addresstype_to_typedindex_to_data2,
addresstype_to_typeindex_to_addressdatawithsource2,
txindex_vecs2,
)| {
let (mut height_to_transacted, height_to_transacted_consumed) =
if height_to_transacted.len() > height_to_transacted2.len() {
(height_to_transacted, height_to_transacted2)
} else {
(height_to_transacted2, height_to_transacted)
};
height_to_transacted_consumed
.into_iter()
.for_each(|(k, v)| {
*height_to_transacted.entry(k).or_default() += v;
});
let (
mut addresstype_to_typedindex_to_data,
addresstype_to_typedindex_to_data_consumed,
) = if addresstype_to_typedindex_to_data.len()
> addresstype_to_typedindex_to_data2.len()
{
(
height_to_transacted,
height_to_addresstype_to_typedindex_to_data,
addresstype_to_typeindex_to_addressdatawithsource,
addresstype_to_typedindex_to_data,
addresstype_to_typedindex_to_data2,
)
},
)
.reduce(
|| {
} else {
(
FxHashMap::<Height, Transacted>::default(),
HeightToAddressTypeToVec::<(TypeIndex, Sats)>::default(),
AddressTypeToTypeIndexMap::default(),
addresstype_to_typedindex_to_data2,
addresstype_to_typedindex_to_data,
)
},
|(
};
addresstype_to_typedindex_to_data_consumed
.0
.into_iter()
.for_each(|(k, v)| {
addresstype_to_typedindex_to_data
.entry(k)
.or_default()
.merge_mut(v);
});
(
height_to_transacted,
addresstype_to_typedindex_to_data,
addresstype_to_typeindex_to_addressdatawithsource,
),
(
height_to_transacted2,
addresstype_to_typedindex_to_data2,
addresstype_to_typeindex_to_addressdatawithsource2,
)| {
let (mut height_to_transacted, height_to_transacted_consumed) =
if height_to_transacted.len() > height_to_transacted2.len()
{
(height_to_transacted, height_to_transacted2)
} else {
(height_to_transacted2, height_to_transacted)
};
height_to_transacted_consumed
.into_iter()
.for_each(|(k, v)| {
*height_to_transacted.entry(k).or_default() += v;
});
let (
mut addresstype_to_typedindex_to_data,
addresstype_to_typedindex_to_data_consumed,
) = if addresstype_to_typedindex_to_data.len()
> addresstype_to_typedindex_to_data2.len()
{
(
addresstype_to_typedindex_to_data,
addresstype_to_typedindex_to_data2,
)
} else {
(
addresstype_to_typedindex_to_data2,
addresstype_to_typedindex_to_data,
)
};
addresstype_to_typedindex_to_data_consumed
.0
.into_iter()
.for_each(|(k, v)| {
addresstype_to_typedindex_to_data
.entry(k)
.or_default()
.merge_mut(v);
});
(
height_to_transacted,
addresstype_to_typedindex_to_data,
addresstype_to_typeindex_to_addressdatawithsource.merge(
addresstype_to_typeindex_to_addressdatawithsource2,
),
)
},
);
addresstype_to_typeindex_to_addressdatawithsource
.merge(addresstype_to_typeindex_to_addressdatawithsource2),
txindex_vecs.merge_vec(txindex_vecs2),
)
},
);
let addresstype_to_typeindex_to_addressdatawithsource =
receiving_addresstype_to_typeindex_to_addressdatawithsource
.merge(sending_addresstype_to_typeindex_to_addressdatawithsource);
let combined_txindex_vecs = output_txindex_vecs.merge_vec(input_txindex_vecs);
(
transacted,
addresstype_to_typedindex_to_received_data,
height_to_sent,
addresstype_to_typedindex_to_sent_data,
addresstype_to_typeindex_to_addressdatawithsource,
combined_txindex_vecs,
)
});
combined_txindex_vecs
.par_values_mut()
.flat_map(|typeindex_to_txindexes| typeindex_to_txindexes.par_iter_mut())
.map(|(_, v)| v)
.filter(|txindex_vec| txindex_vec.len() > 1)
.for_each(|txindex_vec| {
txindex_vec.sort_unstable();
txindex_vec.dedup();
});
for (address_type, typeindex, txindex_vec) in combined_txindex_vecs
.into_iter()
.flat_map(|(t, m)| m.into_iter().map(move |(i, v)| (t, i, v)))
{
let tx_count = txindex_vec.len() as u32;
if let Some(addressdata) = addresstype_to_typeindex_to_loadedaddressdata
.get_mut_unwrap(address_type)
.get_mut(&typeindex)
{
addressdata.deref_mut().tx_count += tx_count;
} else if let Some(addressdata) = addresstype_to_typeindex_to_emptyaddressdata
.get_mut_unwrap(address_type)
.get_mut(&typeindex)
{
addressdata.deref_mut().tx_count += tx_count;
}
}
thread::scope(|scope| {
scope.spawn(|| {
addresstype_to_typedindex_to_received_data.process_received(
@@ -1331,7 +1391,7 @@ impl Vecs {
starting_indexes.height,
&self
.addresstype_to_height_to_addr_count
.iter_typed()
.iter()
.map(|(_, v)| v)
.collect::<Vec<_>>(),
exit,
@@ -1345,7 +1405,7 @@ impl Vecs {
starting_indexes.height,
&self
.addresstype_to_height_to_empty_addr_count
.iter_typed()
.iter()
.map(|(_, v)| v)
.collect::<Vec<_>>(),
exit,
@@ -1562,10 +1622,10 @@ impl Vecs {
self.height_to_unspendable_supply.safe_flush(exit)?;
self.height_to_opreturn_supply.safe_flush(exit)?;
self.addresstype_to_height_to_addr_count
.iter_mut()
.values_mut()
.try_for_each(|v| v.safe_flush(exit))?;
self.addresstype_to_height_to_empty_addr_count
.iter_mut()
.values_mut()
.try_for_each(|v| v.safe_flush(exit))?;
let mut addresstype_to_typeindex_to_new_or_updated_anyaddressindex =
@@ -1713,7 +1773,7 @@ impl AddressTypeToVec<(TypeIndex, Sats)> {
WithAddressDataSource<LoadedAddressData>,
>,
) {
self.unwrap().into_iter_typed().for_each(|(_type, vec)| {
self.unwrap().into_iter().for_each(|(_type, vec)| {
vec.into_iter().for_each(|(type_index, value)| {
let mut is_new = false;
let mut from_any_empty = false;
@@ -1832,7 +1892,7 @@ impl HeightToAddressTypeToVec<(TypeIndex, Sats)> {
.unwrap()
.is_more_than_hour();
v.unwrap().into_iter_typed().try_for_each(|(_type, vec)| {
v.unwrap().into_iter().try_for_each(|(_type, vec)| {
vec.into_iter().try_for_each(|(type_index, value)| {
let typeindex_to_loadedaddressdata =
addresstype_to_typeindex_to_loadedaddressdata.get_mut_unwrap(_type);

View File

@@ -3,13 +3,17 @@ use std::{cmp::Ordering, path::Path};
use brk_error::Result;
use brk_types::{CheckedSub, Dollars, Height, Sats};
use crate::{PriceToAmount, RealizedState, SupplyState, UnrealizedState};
use crate::{
grouped::{PERCENTILES, PERCENTILES_LEN},
PriceToAmount, RealizedState, SupplyState, UnrealizedState,
};
#[derive(Clone)]
pub struct CohortState {
pub supply: SupplyState,
pub realized: Option<RealizedState>,
pub sent: Sats,
pub satblocks_destroyed: Sats,
pub satdays_destroyed: Sats,
@@ -21,6 +25,7 @@ impl CohortState {
Self {
supply: SupplyState::default(),
realized: compute_dollars.then_some(RealizedState::NAN),
sent: Sats::ZERO,
satblocks_destroyed: Sats::ZERO,
satdays_destroyed: Sats::ZERO,
price_to_amount: compute_dollars.then_some(PriceToAmount::create(path, name)),
@@ -52,6 +57,7 @@ impl CohortState {
}
pub fn reset_single_iteration_values(&mut self) {
self.sent = Sats::ZERO;
self.satdays_destroyed = Sats::ZERO;
self.satblocks_destroyed = Sats::ZERO;
if let Some(realized) = self.realized.as_mut() {
@@ -211,8 +217,8 @@ impl CohortState {
self.supply -= supply_state;
if supply_state.value > Sats::ZERO {
self.sent += supply_state.value;
self.satblocks_destroyed += supply_state.value * blocks_old;
self.satdays_destroyed +=
Sats::from((u64::from(supply_state.value) as f64 * days_old).floor() as u64);
@@ -240,6 +246,42 @@ impl CohortState {
}
}
/// Computes prices at PERCENTILES in a single pass.
/// Returns an array of prices corresponding to each percentile.
pub fn compute_percentile_prices(&self) -> [Dollars; PERCENTILES_LEN] {
let mut result = [Dollars::NAN; PERCENTILES_LEN];
let price_to_amount = match self.price_to_amount.as_ref() {
Some(p) => p,
None => return result,
};
if price_to_amount.is_empty() || self.supply.value == Sats::ZERO {
return result;
}
let total = u64::from(self.supply.value);
let targets = PERCENTILES.map(|p| total * u64::from(p) / 100);
let mut accumulated = 0u64;
let mut pct_idx = 0;
for (&price, &sats) in price_to_amount.iter() {
accumulated += u64::from(sats);
while pct_idx < PERCENTILES_LEN && accumulated >= targets[pct_idx] {
result[pct_idx] = price;
pct_idx += 1;
}
if pct_idx >= PERCENTILES_LEN {
break;
}
}
result
}
pub fn compute_unrealized_states(
&self,
height_price: Dollars,

View File

@@ -103,7 +103,7 @@ impl<T> ByAddressType<T> {
}
#[inline]
pub fn iter(&self) -> impl Iterator<Item = &T> {
pub fn values(&self) -> impl Iterator<Item = &T> {
[
&self.p2pk65,
&self.p2pk33,
@@ -118,7 +118,7 @@ impl<T> ByAddressType<T> {
}
#[inline]
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut T> {
[
&mut self.p2pk65,
&mut self.p2pk33,
@@ -133,7 +133,7 @@ impl<T> ByAddressType<T> {
}
#[inline]
pub fn par_iter(&mut self) -> impl ParallelIterator<Item = &T>
pub fn par_values(&mut self) -> impl ParallelIterator<Item = &T>
where
T: Send + Sync,
{
@@ -151,7 +151,7 @@ impl<T> ByAddressType<T> {
}
#[inline]
pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = &mut T>
pub fn par_values_mut(&mut self) -> impl ParallelIterator<Item = &mut T>
where
T: Send + Sync,
{
@@ -169,7 +169,7 @@ impl<T> ByAddressType<T> {
}
#[inline]
pub fn iter_typed(&self) -> impl Iterator<Item = (OutputType, &T)> {
pub fn iter(&self) -> impl Iterator<Item = (OutputType, &T)> {
[
(OutputType::P2PK65, &self.p2pk65),
(OutputType::P2PK33, &self.p2pk33),
@@ -184,7 +184,8 @@ impl<T> ByAddressType<T> {
}
#[inline]
pub fn into_iter_typed(self) -> impl Iterator<Item = (OutputType, T)> {
#[allow(clippy::should_implement_trait)]
pub fn into_iter(self) -> impl Iterator<Item = (OutputType, T)> {
[
(OutputType::P2PK65, self.p2pk65),
(OutputType::P2PK33, self.p2pk33),
@@ -199,7 +200,7 @@ impl<T> ByAddressType<T> {
}
#[inline]
pub fn iter_typed_mut(&mut self) -> impl Iterator<Item = (OutputType, &mut T)> {
pub fn iter_mut(&mut self) -> impl Iterator<Item = (OutputType, &mut T)> {
[
(OutputType::P2PK65, &mut self.p2pk65),
(OutputType::P2PK33, &mut self.p2pk33),
@@ -283,7 +284,7 @@ where
impl<T> ByAddressType<Option<T>> {
pub fn take(&mut self) {
self.iter_mut().for_each(|opt| {
self.values_mut().for_each(|opt| {
opt.take();
});
}

View File

@@ -127,17 +127,17 @@ impl Stores {
.into_iter()
.chain(
self.addresstype_to_addresshash_to_addressindex
.iter()
.values()
.map(|s| s as &dyn AnyStore),
)
.chain(
self.addresstype_to_addressindex_and_txindex
.iter()
.values()
.map(|s| s as &dyn AnyStore),
)
.chain(
self.addresstype_to_addressindex_and_unspentoutpoint
.iter()
.values()
.map(|s| s as &dyn AnyStore),
)
.map(|store| {
@@ -158,17 +158,17 @@ impl Stores {
.into_par_iter()
.chain(
self.addresstype_to_addresshash_to_addressindex
.par_iter_mut()
.par_values_mut()
.map(|s| s as &mut dyn AnyStore),
)
.chain(
self.addresstype_to_addressindex_and_txindex
.par_iter_mut()
.par_values_mut()
.map(|s| s as &mut dyn AnyStore),
)
.chain(
self.addresstype_to_addressindex_and_unspentoutpoint
.par_iter_mut()
.par_values_mut()
.map(|s| s as &mut dyn AnyStore),
) // Changed from par_iter_mut()
.map(|store| {
@@ -195,15 +195,15 @@ impl Stores {
&& self.height_to_coinbase_tag.is_empty()?
&& self
.addresstype_to_addresshash_to_addressindex
.iter()
.values()
.try_fold(true, |acc, s| s.is_empty().map(|empty| acc && empty))?
&& self
.addresstype_to_addressindex_and_txindex
.iter()
.values()
.try_fold(true, |acc, s| s.is_empty().map(|empty| acc && empty))?
&& self
.addresstype_to_addressindex_and_unspentoutpoint
.iter()
.values()
.try_fold(true, |acc, s| s.is_empty().map(|empty| acc && empty))?
{
return Ok(());

View File

@@ -32,4 +32,4 @@ serde = { workspace = true }
serde_json = { workspace = true }
tokio = { workspace = true }
tower-http = { version = "0.6.7", features = ["compression-full", "trace"] }
tracing = "0.1.41"
tracing = "0.1.43"