mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 22:59:58 -07:00
global: snapshot
This commit is contained in:
@@ -69,7 +69,7 @@ impl Vecs {
|
||||
|
||||
let Some(min_height) = indexer
|
||||
.vecs
|
||||
.txindex_to_height
|
||||
.tx.txindex_to_height
|
||||
.iter()?
|
||||
.get(min_txindex)
|
||||
.map(|h| h.min(starting_indexes.height))
|
||||
@@ -77,12 +77,12 @@ impl Vecs {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let mut height_to_first_txindex_iter = indexer.vecs.height_to_first_txindex.iter()?;
|
||||
let mut height_to_first_txindex_iter = indexer.vecs.tx.height_to_first_txindex.iter()?;
|
||||
|
||||
parser
|
||||
.read(
|
||||
Some(min_height),
|
||||
Some((indexer.vecs.height_to_first_txindex.len() - 1).into()),
|
||||
Some((indexer.vecs.tx.height_to_first_txindex.len() - 1).into()),
|
||||
)
|
||||
.iter()
|
||||
.try_for_each(|block| -> Result<()> {
|
||||
|
||||
@@ -250,8 +250,8 @@ impl Vecs {
|
||||
let txindex_to_weight = LazyVecFrom2::init(
|
||||
"weight",
|
||||
version + Version::ZERO,
|
||||
indexer.vecs.txindex_to_base_size.boxed_clone(),
|
||||
indexer.vecs.txindex_to_total_size.boxed_clone(),
|
||||
indexer.vecs.tx.txindex_to_base_size.boxed_clone(),
|
||||
indexer.vecs.tx.txindex_to_total_size.boxed_clone(),
|
||||
|index: TxIndex, txindex_to_base_size_iter, txindex_to_total_size_iter| {
|
||||
let index = index.to_usize();
|
||||
txindex_to_base_size_iter.get_at(index).map(|base_size| {
|
||||
@@ -279,8 +279,8 @@ impl Vecs {
|
||||
let txindex_to_is_coinbase = LazyVecFrom2::init(
|
||||
"is_coinbase",
|
||||
version + Version::ZERO,
|
||||
indexer.vecs.txindex_to_height.boxed_clone(),
|
||||
indexer.vecs.height_to_first_txindex.boxed_clone(),
|
||||
indexer.vecs.tx.txindex_to_height.boxed_clone(),
|
||||
indexer.vecs.tx.height_to_first_txindex.boxed_clone(),
|
||||
|index: TxIndex, txindex_to_height_iter, height_to_first_txindex_iter| {
|
||||
txindex_to_height_iter.get(index).map(|height| {
|
||||
let txindex = height_to_first_txindex_iter.get_unwrap(height);
|
||||
@@ -652,7 +652,7 @@ impl Vecs {
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_range(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_weight,
|
||||
&indexer.vecs.block.height_to_weight,
|
||||
|h| (h, StoredU32::from(1_u32)),
|
||||
exit,
|
||||
)?;
|
||||
@@ -692,10 +692,10 @@ impl Vecs {
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let mut height_to_timestamp_iter = indexer.vecs.height_to_timestamp.iter()?;
|
||||
let mut height_to_timestamp_iter = indexer.vecs.block.height_to_timestamp.iter()?;
|
||||
self.height_to_interval.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_timestamp,
|
||||
&indexer.vecs.block.height_to_timestamp,
|
||||
|(height, timestamp, ..)| {
|
||||
let interval = height.decremented().map_or(Timestamp::ZERO, |prev_h| {
|
||||
let prev_timestamp = height_to_timestamp_iter.get_unwrap(prev_h);
|
||||
@@ -719,19 +719,19 @@ impl Vecs {
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&indexer.vecs.height_to_weight),
|
||||
Some(&indexer.vecs.block.height_to_weight),
|
||||
)?;
|
||||
|
||||
self.indexes_to_block_size.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&indexer.vecs.height_to_total_size),
|
||||
Some(&indexer.vecs.block.height_to_total_size),
|
||||
)?;
|
||||
|
||||
self.height_to_vbytes.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_weight,
|
||||
&indexer.vecs.block.height_to_weight,
|
||||
|(h, w, ..)| {
|
||||
(
|
||||
h,
|
||||
@@ -748,7 +748,7 @@ impl Vecs {
|
||||
Some(&self.height_to_vbytes),
|
||||
)?;
|
||||
|
||||
let mut height_to_timestamp_iter = indexer.vecs.height_to_timestamp.iter()?;
|
||||
let mut height_to_timestamp_iter = indexer.vecs.block.height_to_timestamp.iter()?;
|
||||
|
||||
self.difficultyepoch_to_timestamp.compute_transform(
|
||||
starting_indexes.difficultyepoch,
|
||||
@@ -806,15 +806,15 @@ impl Vecs {
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&indexer.vecs.height_to_difficulty),
|
||||
Some(&indexer.vecs.block.height_to_difficulty),
|
||||
)?;
|
||||
|
||||
self.indexes_to_tx_count
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_count_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_first_txindex,
|
||||
&indexer.vecs.txindex_to_txid,
|
||||
&indexer.vecs.tx.height_to_first_txindex,
|
||||
&indexer.vecs.tx.txindex_to_txid,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
@@ -838,12 +838,12 @@ impl Vecs {
|
||||
|
||||
let compute_indexes_to_tx_vany =
|
||||
|indexes_to_tx_vany: &mut ComputedVecsFromHeight<StoredU64>, txversion| {
|
||||
let mut txindex_to_txversion_iter = indexer.vecs.txindex_to_txversion.iter()?;
|
||||
let mut txindex_to_txversion_iter = indexer.vecs.tx.txindex_to_txversion.iter()?;
|
||||
indexes_to_tx_vany.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
vec.compute_filtered_count_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_first_txindex,
|
||||
&indexer.vecs.txindex_to_txid,
|
||||
&indexer.vecs.tx.height_to_first_txindex,
|
||||
&indexer.vecs.tx.txindex_to_txid,
|
||||
|txindex| {
|
||||
let v = txindex_to_txversion_iter.get_unwrap(txindex);
|
||||
v == txversion
|
||||
@@ -857,8 +857,8 @@ impl Vecs {
|
||||
compute_indexes_to_tx_vany(&mut self.indexes_to_tx_v2, TxVersion::TWO)?;
|
||||
compute_indexes_to_tx_vany(&mut self.indexes_to_tx_v3, TxVersion::THREE)?;
|
||||
|
||||
let txoutindex_to_value = &indexer.vecs.txoutindex_to_value;
|
||||
let txoutindex_to_value_reader = indexer.vecs.txoutindex_to_value.create_reader();
|
||||
let txoutindex_to_value = &indexer.vecs.txout.txoutindex_to_value;
|
||||
let txoutindex_to_value_reader = indexer.vecs.txout.txoutindex_to_value.create_reader();
|
||||
self.txinindex_to_value.compute_transform(
|
||||
starting_indexes.txinindex,
|
||||
&indexes.txinindex_to_txoutindex,
|
||||
@@ -877,7 +877,7 @@ impl Vecs {
|
||||
|
||||
self.txindex_to_input_value.compute_sum_from_indexes(
|
||||
starting_indexes.txindex,
|
||||
&indexer.vecs.txindex_to_first_txinindex,
|
||||
&indexer.vecs.tx.txindex_to_first_txinindex,
|
||||
&indexes.txindex_to_input_count,
|
||||
&self.txinindex_to_value,
|
||||
exit,
|
||||
@@ -885,9 +885,9 @@ impl Vecs {
|
||||
|
||||
self.txindex_to_output_value.compute_sum_from_indexes(
|
||||
starting_indexes.txindex,
|
||||
&indexer.vecs.txindex_to_first_txoutindex,
|
||||
&indexer.vecs.tx.txindex_to_first_txoutindex,
|
||||
&indexes.txindex_to_output_count,
|
||||
&indexer.vecs.txoutindex_to_value,
|
||||
&indexer.vecs.txout.txoutindex_to_value,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -918,7 +918,7 @@ impl Vecs {
|
||||
.compute_all(indexes, price, starting_indexes, exit, |v| {
|
||||
v.compute_filtered_sum_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_first_txindex,
|
||||
&indexer.vecs.tx.height_to_first_txindex,
|
||||
&indexes.height_to_txindex_count,
|
||||
&self.txindex_to_input_value,
|
||||
|sats| !sats.is_max(),
|
||||
@@ -963,12 +963,12 @@ impl Vecs {
|
||||
self.indexes_to_coinbase
|
||||
.compute_all(indexes, price, starting_indexes, exit, |vec| {
|
||||
let mut txindex_to_first_txoutindex_iter =
|
||||
indexer.vecs.txindex_to_first_txoutindex.iter()?;
|
||||
indexer.vecs.tx.txindex_to_first_txoutindex.iter()?;
|
||||
let mut txindex_to_output_count_iter = indexes.txindex_to_output_count.iter();
|
||||
let mut txoutindex_to_value_iter = indexer.vecs.txoutindex_to_value.iter()?;
|
||||
let mut txoutindex_to_value_iter = indexer.vecs.txout.txoutindex_to_value.iter()?;
|
||||
vec.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_first_txindex,
|
||||
&indexer.vecs.tx.height_to_first_txindex,
|
||||
|(height, txindex, ..)| {
|
||||
let first_txoutindex = txindex_to_first_txoutindex_iter
|
||||
.get_unwrap(txindex)
|
||||
@@ -1093,8 +1093,8 @@ impl Vecs {
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_count_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_first_p2aaddressindex,
|
||||
&indexer.vecs.p2aaddressindex_to_p2abytes,
|
||||
&indexer.vecs.address.height_to_first_p2aaddressindex,
|
||||
&indexer.vecs.address.p2aaddressindex_to_p2abytes,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
@@ -1104,8 +1104,8 @@ impl Vecs {
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_count_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_first_p2msoutputindex,
|
||||
&indexer.vecs.p2msoutputindex_to_txindex,
|
||||
&indexer.vecs.output.height_to_first_p2msoutputindex,
|
||||
&indexer.vecs.output.p2msoutputindex_to_txindex,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
@@ -1115,8 +1115,8 @@ impl Vecs {
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_count_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_first_p2pk33addressindex,
|
||||
&indexer.vecs.p2pk33addressindex_to_p2pk33bytes,
|
||||
&indexer.vecs.address.height_to_first_p2pk33addressindex,
|
||||
&indexer.vecs.address.p2pk33addressindex_to_p2pk33bytes,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
@@ -1126,8 +1126,8 @@ impl Vecs {
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_count_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_first_p2pk65addressindex,
|
||||
&indexer.vecs.p2pk65addressindex_to_p2pk65bytes,
|
||||
&indexer.vecs.address.height_to_first_p2pk65addressindex,
|
||||
&indexer.vecs.address.p2pk65addressindex_to_p2pk65bytes,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
@@ -1137,8 +1137,8 @@ impl Vecs {
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_count_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_first_p2pkhaddressindex,
|
||||
&indexer.vecs.p2pkhaddressindex_to_p2pkhbytes,
|
||||
&indexer.vecs.address.height_to_first_p2pkhaddressindex,
|
||||
&indexer.vecs.address.p2pkhaddressindex_to_p2pkhbytes,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
@@ -1148,8 +1148,8 @@ impl Vecs {
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_count_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_first_p2shaddressindex,
|
||||
&indexer.vecs.p2shaddressindex_to_p2shbytes,
|
||||
&indexer.vecs.address.height_to_first_p2shaddressindex,
|
||||
&indexer.vecs.address.p2shaddressindex_to_p2shbytes,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
@@ -1159,8 +1159,8 @@ impl Vecs {
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_count_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_first_p2traddressindex,
|
||||
&indexer.vecs.p2traddressindex_to_p2trbytes,
|
||||
&indexer.vecs.address.height_to_first_p2traddressindex,
|
||||
&indexer.vecs.address.p2traddressindex_to_p2trbytes,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
@@ -1170,8 +1170,8 @@ impl Vecs {
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_count_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_first_p2wpkhaddressindex,
|
||||
&indexer.vecs.p2wpkhaddressindex_to_p2wpkhbytes,
|
||||
&indexer.vecs.address.height_to_first_p2wpkhaddressindex,
|
||||
&indexer.vecs.address.p2wpkhaddressindex_to_p2wpkhbytes,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
@@ -1181,8 +1181,8 @@ impl Vecs {
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_count_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_first_p2wshaddressindex,
|
||||
&indexer.vecs.p2wshaddressindex_to_p2wshbytes,
|
||||
&indexer.vecs.address.height_to_first_p2wshaddressindex,
|
||||
&indexer.vecs.address.p2wshaddressindex_to_p2wshbytes,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
@@ -1192,8 +1192,8 @@ impl Vecs {
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_count_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_first_opreturnindex,
|
||||
&indexer.vecs.opreturnindex_to_txindex,
|
||||
&indexer.vecs.output.height_to_first_opreturnindex,
|
||||
&indexer.vecs.output.opreturnindex_to_txindex,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
@@ -1203,8 +1203,8 @@ impl Vecs {
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_count_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_first_unknownoutputindex,
|
||||
&indexer.vecs.unknownoutputindex_to_txindex,
|
||||
&indexer.vecs.output.height_to_first_unknownoutputindex,
|
||||
&indexer.vecs.output.unknownoutputindex_to_txindex,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
@@ -1214,8 +1214,8 @@ impl Vecs {
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_count_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_first_emptyoutputindex,
|
||||
&indexer.vecs.emptyoutputindex_to_txindex,
|
||||
&indexer.vecs.output.height_to_first_emptyoutputindex,
|
||||
&indexer.vecs.output.emptyoutputindex_to_txindex,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
@@ -1299,7 +1299,7 @@ impl Vecs {
|
||||
let multiplier = 2.0_f64.powi(32) / 600.0;
|
||||
v.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_difficulty,
|
||||
&indexer.vecs.block.height_to_difficulty,
|
||||
|(i, v, ..)| (i, StoredF32::from(*v * multiplier)),
|
||||
exit,
|
||||
)?;
|
||||
@@ -1418,7 +1418,7 @@ impl Vecs {
|
||||
|v| {
|
||||
v.compute_percentage_change(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_difficulty,
|
||||
&indexer.vecs.block.height_to_difficulty,
|
||||
1,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -73,7 +73,7 @@ impl Vecs {
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let height_to_timestamp = &indexer.vecs.height_to_timestamp;
|
||||
let height_to_timestamp = &indexer.vecs.block.height_to_timestamp;
|
||||
let index = starting_indexes
|
||||
.height
|
||||
.min(Height::from(self.height_to_price_ohlc_in_cents.len()));
|
||||
|
||||
@@ -172,7 +172,7 @@ where
|
||||
self.height.compute(
|
||||
starting_indexes.height,
|
||||
txindex,
|
||||
&indexer.vecs.height_to_first_txindex,
|
||||
&indexer.vecs.tx.height_to_first_txindex,
|
||||
&indexes.height_to_txindex_count,
|
||||
exit,
|
||||
)?;
|
||||
@@ -182,7 +182,7 @@ where
|
||||
self.height.compute(
|
||||
starting_indexes.height,
|
||||
txindex,
|
||||
&indexer.vecs.height_to_first_txindex,
|
||||
&indexer.vecs.tx.height_to_first_txindex,
|
||||
&indexes.height_to_txindex_count,
|
||||
exit,
|
||||
)?;
|
||||
@@ -252,7 +252,7 @@ impl ComputedVecsFromTxindex<Bitcoin> {
|
||||
let mut last_iter = sats.height.last.as_ref().map(|v| v.into_iter());
|
||||
let mut cumulative_iter = sats.height.cumulative.as_ref().map(|v| v.into_iter());
|
||||
|
||||
(starting_index.to_usize()..indexer.vecs.height_to_weight.len())
|
||||
(starting_index.to_usize()..indexer.vecs.block.height_to_weight.len())
|
||||
.map(Height::from)
|
||||
.try_for_each(|height| -> Result<()> {
|
||||
if let Some(first) = self.height.first.as_mut() {
|
||||
@@ -375,7 +375,7 @@ impl ComputedVecsFromTxindex<Dollars> {
|
||||
let mut last_iter = bitcoin.height.last.as_ref().map(|v| v.into_iter());
|
||||
let mut cumulative_iter = bitcoin.height.cumulative.as_ref().map(|v| v.into_iter());
|
||||
|
||||
(starting_index.to_usize()..indexer.vecs.height_to_weight.len())
|
||||
(starting_index.to_usize()..indexer.vecs.block.height_to_weight.len())
|
||||
.map(Height::from)
|
||||
.try_for_each(|height| -> Result<()> {
|
||||
let price = *close_iter.get_unwrap(height);
|
||||
|
||||
@@ -74,7 +74,7 @@ impl ComputedValueVecsFromTxindex {
|
||||
&name_usd,
|
||||
version + VERSION,
|
||||
bitcoin_txindex.boxed_clone(),
|
||||
indexer.vecs.txindex_to_height.boxed_clone(),
|
||||
indexer.vecs.tx.txindex_to_height.boxed_clone(),
|
||||
price.chainindexes_to_price_close.height.boxed_clone(),
|
||||
|txindex: TxIndex,
|
||||
txindex_to_btc_iter,
|
||||
|
||||
@@ -122,57 +122,57 @@ impl Vecs {
|
||||
|
||||
let this = Self {
|
||||
txinindex_to_txoutindex: eager!("txoutindex"),
|
||||
txoutindex_to_txoutindex: lazy!("txoutindex", indexer.vecs.txoutindex_to_value),
|
||||
txinindex_to_txinindex: lazy!("txinindex", indexer.vecs.txinindex_to_outpoint),
|
||||
txoutindex_to_txoutindex: lazy!("txoutindex", indexer.vecs.txout.txoutindex_to_value),
|
||||
txinindex_to_txinindex: lazy!("txinindex", indexer.vecs.txin.txinindex_to_outpoint),
|
||||
p2pk33addressindex_to_p2pk33addressindex: lazy!(
|
||||
"p2pk33addressindex",
|
||||
indexer.vecs.p2pk33addressindex_to_p2pk33bytes
|
||||
indexer.vecs.address.p2pk33addressindex_to_p2pk33bytes
|
||||
),
|
||||
p2pk65addressindex_to_p2pk65addressindex: lazy!(
|
||||
"p2pk65addressindex",
|
||||
indexer.vecs.p2pk65addressindex_to_p2pk65bytes
|
||||
indexer.vecs.address.p2pk65addressindex_to_p2pk65bytes
|
||||
),
|
||||
p2pkhaddressindex_to_p2pkhaddressindex: lazy!(
|
||||
"p2pkhaddressindex",
|
||||
indexer.vecs.p2pkhaddressindex_to_p2pkhbytes
|
||||
indexer.vecs.address.p2pkhaddressindex_to_p2pkhbytes
|
||||
),
|
||||
p2shaddressindex_to_p2shaddressindex: lazy!(
|
||||
"p2shaddressindex",
|
||||
indexer.vecs.p2shaddressindex_to_p2shbytes
|
||||
indexer.vecs.address.p2shaddressindex_to_p2shbytes
|
||||
),
|
||||
p2traddressindex_to_p2traddressindex: lazy!(
|
||||
"p2traddressindex",
|
||||
indexer.vecs.p2traddressindex_to_p2trbytes
|
||||
indexer.vecs.address.p2traddressindex_to_p2trbytes
|
||||
),
|
||||
p2wpkhaddressindex_to_p2wpkhaddressindex: lazy!(
|
||||
"p2wpkhaddressindex",
|
||||
indexer.vecs.p2wpkhaddressindex_to_p2wpkhbytes
|
||||
indexer.vecs.address.p2wpkhaddressindex_to_p2wpkhbytes
|
||||
),
|
||||
p2wshaddressindex_to_p2wshaddressindex: lazy!(
|
||||
"p2wshaddressindex",
|
||||
indexer.vecs.p2wshaddressindex_to_p2wshbytes
|
||||
indexer.vecs.address.p2wshaddressindex_to_p2wshbytes
|
||||
),
|
||||
p2aaddressindex_to_p2aaddressindex: lazy!(
|
||||
"p2aaddressindex",
|
||||
indexer.vecs.p2aaddressindex_to_p2abytes
|
||||
indexer.vecs.address.p2aaddressindex_to_p2abytes
|
||||
),
|
||||
p2msoutputindex_to_p2msoutputindex: lazy!(
|
||||
"p2msoutputindex",
|
||||
indexer.vecs.p2msoutputindex_to_txindex
|
||||
indexer.vecs.output.p2msoutputindex_to_txindex
|
||||
),
|
||||
emptyoutputindex_to_emptyoutputindex: lazy!(
|
||||
"emptyoutputindex",
|
||||
indexer.vecs.emptyoutputindex_to_txindex
|
||||
indexer.vecs.output.emptyoutputindex_to_txindex
|
||||
),
|
||||
unknownoutputindex_to_unknownoutputindex: lazy!(
|
||||
"unknownoutputindex",
|
||||
indexer.vecs.unknownoutputindex_to_txindex
|
||||
indexer.vecs.output.unknownoutputindex_to_txindex
|
||||
),
|
||||
opreturnindex_to_opreturnindex: lazy!(
|
||||
"opreturnindex",
|
||||
indexer.vecs.opreturnindex_to_txindex
|
||||
indexer.vecs.output.opreturnindex_to_txindex
|
||||
),
|
||||
txindex_to_txindex: lazy!("txindex", indexer.vecs.txindex_to_txid),
|
||||
txindex_to_txindex: lazy!("txindex", indexer.vecs.tx.txindex_to_txid),
|
||||
txindex_to_input_count: eager!("input_count"),
|
||||
txindex_to_output_count: eager!("output_count"),
|
||||
dateindex_to_date: eager!("date"),
|
||||
@@ -251,11 +251,11 @@ impl Vecs {
|
||||
// TxInIndex
|
||||
// ---
|
||||
|
||||
let txindex_to_first_txoutindex = &indexer.vecs.txindex_to_first_txoutindex;
|
||||
let txindex_to_first_txoutindex = &indexer.vecs.tx.txindex_to_first_txoutindex;
|
||||
let txindex_to_first_txoutindex_reader = txindex_to_first_txoutindex.create_reader();
|
||||
self.txinindex_to_txoutindex.compute_transform(
|
||||
starting_indexes.txinindex,
|
||||
&indexer.vecs.txinindex_to_outpoint,
|
||||
&indexer.vecs.txin.txinindex_to_outpoint,
|
||||
|(txinindex, outpoint, ..)| {
|
||||
if unlikely(outpoint.is_coinbase()) {
|
||||
return (txinindex, TxOutIndex::COINBASE);
|
||||
@@ -274,22 +274,22 @@ impl Vecs {
|
||||
|
||||
self.txindex_to_input_count.compute_count_from_indexes(
|
||||
starting_indexes.txindex,
|
||||
&indexer.vecs.txindex_to_first_txinindex,
|
||||
&indexer.vecs.txinindex_to_outpoint,
|
||||
&indexer.vecs.tx.txindex_to_first_txinindex,
|
||||
&indexer.vecs.txin.txinindex_to_outpoint,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.txindex_to_output_count.compute_count_from_indexes(
|
||||
starting_indexes.txindex,
|
||||
&indexer.vecs.txindex_to_first_txoutindex,
|
||||
&indexer.vecs.txoutindex_to_value,
|
||||
&indexer.vecs.tx.txindex_to_first_txoutindex,
|
||||
&indexer.vecs.txout.txoutindex_to_value,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.height_to_txindex_count.compute_count_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_first_txindex,
|
||||
&indexer.vecs.txindex_to_txid,
|
||||
&indexer.vecs.tx.height_to_first_txindex,
|
||||
&indexer.vecs.tx.txindex_to_txid,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -299,13 +299,13 @@ impl Vecs {
|
||||
|
||||
self.height_to_height.compute_from_index(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_weight,
|
||||
&indexer.vecs.block.height_to_weight,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.height_to_date.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_timestamp,
|
||||
&indexer.vecs.block.height_to_timestamp,
|
||||
|(h, t, ..)| (h, Date::from(t)),
|
||||
exit,
|
||||
)?;
|
||||
@@ -313,7 +313,7 @@ impl Vecs {
|
||||
let mut prev_timestamp_fixed = None;
|
||||
self.height_to_timestamp_fixed.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_timestamp,
|
||||
&indexer.vecs.block.height_to_timestamp,
|
||||
|(h, timestamp, height_to_timestamp_fixed_iter)| {
|
||||
if prev_timestamp_fixed.is_none()
|
||||
&& let Some(prev_h) = h.decremented()
|
||||
@@ -389,7 +389,7 @@ impl Vecs {
|
||||
self.dateindex_to_height_count.compute_count_from_indexes(
|
||||
starting_dateindex,
|
||||
&self.dateindex_to_first_height,
|
||||
&indexer.vecs.height_to_weight,
|
||||
&indexer.vecs.block.height_to_weight,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -442,7 +442,7 @@ impl Vecs {
|
||||
|
||||
self.height_to_difficultyepoch.compute_from_index(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_weight,
|
||||
&indexer.vecs.block.height_to_weight,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -626,7 +626,7 @@ impl Vecs {
|
||||
|
||||
self.height_to_halvingepoch.compute_from_index(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_weight,
|
||||
&indexer.vecs.block.height_to_weight,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -122,28 +122,28 @@ impl Vecs {
|
||||
self.height_to_pool.version() + indexer.stores.height_to_coinbase_tag.version(),
|
||||
)?;
|
||||
|
||||
let mut height_to_first_txindex_iter = indexer.vecs.height_to_first_txindex.iter()?;
|
||||
let mut height_to_first_txindex_iter = indexer.vecs.tx.height_to_first_txindex.iter()?;
|
||||
let mut txindex_to_first_txoutindex_iter =
|
||||
indexer.vecs.txindex_to_first_txoutindex.iter()?;
|
||||
indexer.vecs.tx.txindex_to_first_txoutindex.iter()?;
|
||||
let mut txindex_to_output_count_iter = indexes.txindex_to_output_count.iter();
|
||||
let mut txoutindex_to_outputtype_iter = indexer.vecs.txoutindex_to_outputtype.iter()?;
|
||||
let mut txoutindex_to_typeindex_iter = indexer.vecs.txoutindex_to_typeindex.iter()?;
|
||||
let mut txoutindex_to_outputtype_iter = indexer.vecs.txout.txoutindex_to_outputtype.iter()?;
|
||||
let mut txoutindex_to_typeindex_iter = indexer.vecs.txout.txoutindex_to_typeindex.iter()?;
|
||||
let mut p2pk65addressindex_to_p2pk65bytes_iter =
|
||||
indexer.vecs.p2pk65addressindex_to_p2pk65bytes.iter()?;
|
||||
indexer.vecs.address.p2pk65addressindex_to_p2pk65bytes.iter()?;
|
||||
let mut p2pk33addressindex_to_p2pk33bytes_iter =
|
||||
indexer.vecs.p2pk33addressindex_to_p2pk33bytes.iter()?;
|
||||
indexer.vecs.address.p2pk33addressindex_to_p2pk33bytes.iter()?;
|
||||
let mut p2pkhaddressindex_to_p2pkhbytes_iter =
|
||||
indexer.vecs.p2pkhaddressindex_to_p2pkhbytes.iter()?;
|
||||
indexer.vecs.address.p2pkhaddressindex_to_p2pkhbytes.iter()?;
|
||||
let mut p2shaddressindex_to_p2shbytes_iter =
|
||||
indexer.vecs.p2shaddressindex_to_p2shbytes.iter()?;
|
||||
indexer.vecs.address.p2shaddressindex_to_p2shbytes.iter()?;
|
||||
let mut p2wpkhaddressindex_to_p2wpkhbytes_iter =
|
||||
indexer.vecs.p2wpkhaddressindex_to_p2wpkhbytes.iter()?;
|
||||
indexer.vecs.address.p2wpkhaddressindex_to_p2wpkhbytes.iter()?;
|
||||
let mut p2wshaddressindex_to_p2wshbytes_iter =
|
||||
indexer.vecs.p2wshaddressindex_to_p2wshbytes.iter()?;
|
||||
indexer.vecs.address.p2wshaddressindex_to_p2wshbytes.iter()?;
|
||||
let mut p2traddressindex_to_p2trbytes_iter =
|
||||
indexer.vecs.p2traddressindex_to_p2trbytes.iter()?;
|
||||
indexer.vecs.address.p2traddressindex_to_p2trbytes.iter()?;
|
||||
let mut p2aaddressindex_to_p2abytes_iter =
|
||||
indexer.vecs.p2aaddressindex_to_p2abytes.iter()?;
|
||||
indexer.vecs.address.p2aaddressindex_to_p2abytes.iter()?;
|
||||
|
||||
let unknown = self.pools.get_unknown();
|
||||
|
||||
|
||||
@@ -381,18 +381,18 @@ impl Vecs {
|
||||
.as_ref()
|
||||
.map(|price| price.timeindexes_to_price_close.dateindex.u());
|
||||
let height_to_date_fixed = &indexes.height_to_date_fixed;
|
||||
let height_to_first_p2aaddressindex = &indexer.vecs.height_to_first_p2aaddressindex;
|
||||
let height_to_first_p2pk33addressindex = &indexer.vecs.height_to_first_p2pk33addressindex;
|
||||
let height_to_first_p2pk65addressindex = &indexer.vecs.height_to_first_p2pk65addressindex;
|
||||
let height_to_first_p2pkhaddressindex = &indexer.vecs.height_to_first_p2pkhaddressindex;
|
||||
let height_to_first_p2shaddressindex = &indexer.vecs.height_to_first_p2shaddressindex;
|
||||
let height_to_first_p2traddressindex = &indexer.vecs.height_to_first_p2traddressindex;
|
||||
let height_to_first_p2wpkhaddressindex = &indexer.vecs.height_to_first_p2wpkhaddressindex;
|
||||
let height_to_first_p2wshaddressindex = &indexer.vecs.height_to_first_p2wshaddressindex;
|
||||
let height_to_first_txindex = &indexer.vecs.height_to_first_txindex;
|
||||
let height_to_first_p2aaddressindex = &indexer.vecs.address.height_to_first_p2aaddressindex;
|
||||
let height_to_first_p2pk33addressindex = &indexer.vecs.address.height_to_first_p2pk33addressindex;
|
||||
let height_to_first_p2pk65addressindex = &indexer.vecs.address.height_to_first_p2pk65addressindex;
|
||||
let height_to_first_p2pkhaddressindex = &indexer.vecs.address.height_to_first_p2pkhaddressindex;
|
||||
let height_to_first_p2shaddressindex = &indexer.vecs.address.height_to_first_p2shaddressindex;
|
||||
let height_to_first_p2traddressindex = &indexer.vecs.address.height_to_first_p2traddressindex;
|
||||
let height_to_first_p2wpkhaddressindex = &indexer.vecs.address.height_to_first_p2wpkhaddressindex;
|
||||
let height_to_first_p2wshaddressindex = &indexer.vecs.address.height_to_first_p2wshaddressindex;
|
||||
let height_to_first_txindex = &indexer.vecs.tx.height_to_first_txindex;
|
||||
let height_to_txindex_count = chain.indexes_to_tx_count.height.u();
|
||||
let height_to_first_txinindex = &indexer.vecs.height_to_first_txinindex;
|
||||
let height_to_first_txoutindex = &indexer.vecs.height_to_first_txoutindex;
|
||||
let height_to_first_txinindex = &indexer.vecs.txin.height_to_first_txinindex;
|
||||
let height_to_first_txoutindex = &indexer.vecs.txout.height_to_first_txoutindex;
|
||||
let height_to_input_count = chain.indexes_to_input_count.height.unwrap_sum();
|
||||
let height_to_output_count = chain.indexes_to_output_count.height.unwrap_sum();
|
||||
let height_to_price_close = price
|
||||
@@ -406,15 +406,15 @@ impl Vecs {
|
||||
.height
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
let txindex_to_first_txoutindex = &indexer.vecs.txindex_to_first_txoutindex;
|
||||
let txindex_to_height = &indexer.vecs.txindex_to_height;
|
||||
let txindex_to_first_txoutindex = &indexer.vecs.tx.txindex_to_first_txoutindex;
|
||||
let txindex_to_height = &indexer.vecs.tx.txindex_to_height;
|
||||
let txindex_to_input_count = &indexes.txindex_to_input_count;
|
||||
let txindex_to_output_count = &indexes.txindex_to_output_count;
|
||||
let txinindex_to_outpoint = &indexer.vecs.txinindex_to_outpoint;
|
||||
let txoutindex_to_outputtype = &indexer.vecs.txoutindex_to_outputtype;
|
||||
let txoutindex_to_txindex = &indexer.vecs.txoutindex_to_txindex;
|
||||
let txoutindex_to_typeindex = &indexer.vecs.txoutindex_to_typeindex;
|
||||
let txoutindex_to_value = &indexer.vecs.txoutindex_to_value;
|
||||
let txinindex_to_outpoint = &indexer.vecs.txin.txinindex_to_outpoint;
|
||||
let txoutindex_to_outputtype = &indexer.vecs.txout.txoutindex_to_outputtype;
|
||||
let txoutindex_to_txindex = &indexer.vecs.txout.txoutindex_to_txindex;
|
||||
let txoutindex_to_typeindex = &indexer.vecs.txout.txoutindex_to_typeindex;
|
||||
let txoutindex_to_value = &indexer.vecs.txout.txoutindex_to_value;
|
||||
|
||||
let mut height_to_price_close_iter = height_to_price_close.as_ref().map(|v| v.into_iter());
|
||||
let mut height_to_timestamp_fixed_iter = height_to_timestamp_fixed.into_iter();
|
||||
@@ -501,7 +501,7 @@ impl Vecs {
|
||||
};
|
||||
|
||||
let starting_height = starting_indexes.height.min(stateful_starting_height);
|
||||
let last_height = Height::from(indexer.vecs.height_to_blockhash.stamp());
|
||||
let last_height = Height::from(indexer.vecs.block.height_to_blockhash.stamp());
|
||||
if starting_height <= last_height {
|
||||
let stamp = starting_height.into();
|
||||
let starting_height = if starting_height.is_not_zero() {
|
||||
|
||||
@@ -16,11 +16,11 @@ pub struct IndexerReaders {
|
||||
impl IndexerReaders {
|
||||
pub fn new(indexer: &Indexer) -> Self {
|
||||
Self {
|
||||
txinindex_to_outpoint: indexer.vecs.txinindex_to_outpoint.create_reader(),
|
||||
txindex_to_first_txoutindex: indexer.vecs.txindex_to_first_txoutindex.create_reader(),
|
||||
txoutindex_to_value: indexer.vecs.txoutindex_to_value.create_reader(),
|
||||
txoutindex_to_outputtype: indexer.vecs.txoutindex_to_outputtype.create_reader(),
|
||||
txoutindex_to_typeindex: indexer.vecs.txoutindex_to_typeindex.create_reader(),
|
||||
txinindex_to_outpoint: indexer.vecs.txin.txinindex_to_outpoint.create_reader(),
|
||||
txindex_to_first_txoutindex: indexer.vecs.tx.txindex_to_first_txoutindex.create_reader(),
|
||||
txoutindex_to_value: indexer.vecs.txout.txoutindex_to_value.create_reader(),
|
||||
txoutindex_to_outputtype: indexer.vecs.txout.txoutindex_to_outputtype.create_reader(),
|
||||
txoutindex_to_typeindex: indexer.vecs.txout.txoutindex_to_typeindex.create_reader(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,9 +61,9 @@ pub fn process_blocks(
|
||||
|
||||
// References to vectors using correct field paths
|
||||
// From indexer.vecs:
|
||||
let height_to_first_txindex = &indexer.vecs.height_to_first_txindex;
|
||||
let height_to_first_txoutindex = &indexer.vecs.height_to_first_txoutindex;
|
||||
let height_to_first_txinindex = &indexer.vecs.height_to_first_txinindex;
|
||||
let height_to_first_txindex = &indexer.vecs.tx.height_to_first_txindex;
|
||||
let height_to_first_txoutindex = &indexer.vecs.txout.height_to_first_txoutindex;
|
||||
let height_to_first_txinindex = &indexer.vecs.txin.height_to_first_txinindex;
|
||||
|
||||
// From chain (via .height.u() or .height.unwrap_sum() patterns):
|
||||
let height_to_tx_count = chain.indexes_to_tx_count.height.u();
|
||||
@@ -114,14 +114,14 @@ pub fn process_blocks(
|
||||
let mut vr = VecsReaders::new(&vecs.any_address_indexes, &vecs.addresses_data);
|
||||
|
||||
// Create iterators for first address indexes per type
|
||||
let mut first_p2a_iter = indexer.vecs.height_to_first_p2aaddressindex.into_iter();
|
||||
let mut first_p2pk33_iter = indexer.vecs.height_to_first_p2pk33addressindex.into_iter();
|
||||
let mut first_p2pk65_iter = indexer.vecs.height_to_first_p2pk65addressindex.into_iter();
|
||||
let mut first_p2pkh_iter = indexer.vecs.height_to_first_p2pkhaddressindex.into_iter();
|
||||
let mut first_p2sh_iter = indexer.vecs.height_to_first_p2shaddressindex.into_iter();
|
||||
let mut first_p2tr_iter = indexer.vecs.height_to_first_p2traddressindex.into_iter();
|
||||
let mut first_p2wpkh_iter = indexer.vecs.height_to_first_p2wpkhaddressindex.into_iter();
|
||||
let mut first_p2wsh_iter = indexer.vecs.height_to_first_p2wshaddressindex.into_iter();
|
||||
let mut first_p2a_iter = indexer.vecs.address.height_to_first_p2aaddressindex.into_iter();
|
||||
let mut first_p2pk33_iter = indexer.vecs.address.height_to_first_p2pk33addressindex.into_iter();
|
||||
let mut first_p2pk65_iter = indexer.vecs.address.height_to_first_p2pk65addressindex.into_iter();
|
||||
let mut first_p2pkh_iter = indexer.vecs.address.height_to_first_p2pkhaddressindex.into_iter();
|
||||
let mut first_p2sh_iter = indexer.vecs.address.height_to_first_p2shaddressindex.into_iter();
|
||||
let mut first_p2tr_iter = indexer.vecs.address.height_to_first_p2traddressindex.into_iter();
|
||||
let mut first_p2wpkh_iter = indexer.vecs.address.height_to_first_p2wpkhaddressindex.into_iter();
|
||||
let mut first_p2wsh_iter = indexer.vecs.address.height_to_first_p2wshaddressindex.into_iter();
|
||||
|
||||
// Track running totals - recover from previous height if resuming
|
||||
let (mut unspendable_supply, mut opreturn_supply, mut addresstype_to_addr_count, mut addresstype_to_empty_addr_count) =
|
||||
@@ -210,9 +210,9 @@ pub fn process_blocks(
|
||||
first_txoutindex,
|
||||
output_count,
|
||||
&txoutindex_to_txindex,
|
||||
&indexer.vecs.txoutindex_to_value,
|
||||
&indexer.vecs.txoutindex_to_outputtype,
|
||||
&indexer.vecs.txoutindex_to_typeindex,
|
||||
&indexer.vecs.txout.txoutindex_to_value,
|
||||
&indexer.vecs.txout.txoutindex_to_outputtype,
|
||||
&indexer.vecs.txout.txoutindex_to_typeindex,
|
||||
&ir,
|
||||
&first_addressindexes,
|
||||
&loaded_cache,
|
||||
@@ -228,11 +228,11 @@ pub fn process_blocks(
|
||||
first_txinindex + 1, // Skip coinbase
|
||||
input_count - 1,
|
||||
&txinindex_to_txindex[1..], // Skip coinbase
|
||||
&indexer.vecs.txinindex_to_outpoint,
|
||||
&indexer.vecs.txindex_to_first_txoutindex,
|
||||
&indexer.vecs.txoutindex_to_value,
|
||||
&indexer.vecs.txoutindex_to_outputtype,
|
||||
&indexer.vecs.txoutindex_to_typeindex,
|
||||
&indexer.vecs.txin.txinindex_to_outpoint,
|
||||
&indexer.vecs.tx.txindex_to_first_txoutindex,
|
||||
&indexer.vecs.txout.txoutindex_to_value,
|
||||
&indexer.vecs.txout.txoutindex_to_outputtype,
|
||||
&indexer.vecs.txout.txoutindex_to_typeindex,
|
||||
&txoutindex_to_height,
|
||||
&ir,
|
||||
&first_addressindexes,
|
||||
|
||||
@@ -21,11 +21,11 @@ pub struct IndexerReaders {
|
||||
impl IndexerReaders {
|
||||
pub fn new(indexer: &Indexer) -> Self {
|
||||
Self {
|
||||
txinindex_to_outpoint: indexer.vecs.txinindex_to_outpoint.create_reader(),
|
||||
txindex_to_first_txoutindex: indexer.vecs.txindex_to_first_txoutindex.create_reader(),
|
||||
txoutindex_to_value: indexer.vecs.txoutindex_to_value.create_reader(),
|
||||
txoutindex_to_outputtype: indexer.vecs.txoutindex_to_outputtype.create_reader(),
|
||||
txoutindex_to_typeindex: indexer.vecs.txoutindex_to_typeindex.create_reader(),
|
||||
txinindex_to_outpoint: indexer.vecs.txin.txinindex_to_outpoint.create_reader(),
|
||||
txindex_to_first_txoutindex: indexer.vecs.tx.txindex_to_first_txoutindex.create_reader(),
|
||||
txoutindex_to_value: indexer.vecs.txout.txoutindex_to_value.create_reader(),
|
||||
txoutindex_to_outputtype: indexer.vecs.txout.txoutindex_to_outputtype.create_reader(),
|
||||
txoutindex_to_typeindex: indexer.vecs.txout.txoutindex_to_typeindex.create_reader(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,7 +271,7 @@ impl Vecs {
|
||||
};
|
||||
|
||||
// 3. Get last height from indexer
|
||||
let last_height = Height::from(indexer.vecs.height_to_blockhash.len().saturating_sub(1));
|
||||
let last_height = Height::from(indexer.vecs.block.height_to_blockhash.len().saturating_sub(1));
|
||||
|
||||
// 4. Process blocks
|
||||
if starting_height <= last_height {
|
||||
|
||||
@@ -1,239 +0,0 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_grouper::{CohortContext, Filter, Filtered};
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, DateIndex, Dollars, Height, StoredU64, Version};
|
||||
use vecdb::{
|
||||
AnyStoredVec, AnyVec, Database, EagerVec, Exit, GenericStoredVec, ImportableVec, IterableVec,
|
||||
PcoVec, TypedVecIterator,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
Indexes,
|
||||
grouped::{ComputedVecsFromHeight, Source, VecBuilderOptions},
|
||||
indexes, price,
|
||||
stateful::{
|
||||
common,
|
||||
r#trait::{CohortVecs, DynCohortVecs},
|
||||
},
|
||||
states::AddressCohortState,
|
||||
utils::OptionExt,
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
starting_height: Option<Height>,
|
||||
|
||||
#[traversable(skip)]
|
||||
pub state: Option<AddressCohortState>,
|
||||
|
||||
#[traversable(flatten)]
|
||||
pub inner: common::Vecs,
|
||||
|
||||
pub height_to_addr_count: EagerVec<PcoVec<Height, StoredU64>>,
|
||||
pub indexes_to_addr_count: ComputedVecsFromHeight<StoredU64>,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
db: &Database,
|
||||
filter: Filter,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
states_path: Option<&Path>,
|
||||
) -> Result<Self> {
|
||||
let compute_dollars = price.is_some();
|
||||
|
||||
let full_name = filter.to_full_name(CohortContext::Address);
|
||||
let suffix = |s: &str| {
|
||||
if full_name.is_empty() {
|
||||
s.to_string()
|
||||
} else {
|
||||
format!("{full_name}_{s}")
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
starting_height: None,
|
||||
state: states_path.map(|states_path| {
|
||||
AddressCohortState::new(states_path, &full_name, compute_dollars)
|
||||
}),
|
||||
height_to_addr_count: EagerVec::forced_import(
|
||||
db,
|
||||
&suffix("addr_count"),
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
indexes_to_addr_count: ComputedVecsFromHeight::forced_import(
|
||||
db,
|
||||
&suffix("addr_count"),
|
||||
Source::None,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
inner: common::Vecs::forced_import(
|
||||
db,
|
||||
filter,
|
||||
CohortContext::Address,
|
||||
version,
|
||||
indexes,
|
||||
price,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl DynCohortVecs for Vecs {
|
||||
fn min_height_vecs_len(&self) -> usize {
|
||||
std::cmp::min(
|
||||
self.height_to_addr_count.len(),
|
||||
self.inner.min_height_vecs_len(),
|
||||
)
|
||||
}
|
||||
|
||||
fn reset_state_starting_height(&mut self) {
|
||||
self.starting_height = Some(Height::ZERO);
|
||||
}
|
||||
|
||||
fn import_state(&mut self, starting_height: Height) -> Result<Height> {
|
||||
let starting_height = self
|
||||
.inner
|
||||
.import_state(starting_height, &mut self.state.um().inner)?;
|
||||
|
||||
self.starting_height = Some(starting_height);
|
||||
|
||||
if let Some(prev_height) = starting_height.decremented() {
|
||||
self.state.um().addr_count = *self
|
||||
.height_to_addr_count
|
||||
.into_iter()
|
||||
.get_unwrap(prev_height);
|
||||
}
|
||||
|
||||
Ok(starting_height)
|
||||
}
|
||||
|
||||
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> {
|
||||
self.height_to_addr_count
|
||||
.validate_computed_version_or_reset(
|
||||
base_version + self.height_to_addr_count.inner_version(),
|
||||
)?;
|
||||
|
||||
self.inner.validate_computed_versions(base_version)
|
||||
}
|
||||
|
||||
fn truncate_push(&mut self, height: Height) -> Result<()> {
|
||||
if self.starting_height.unwrap() > height {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.height_to_addr_count
|
||||
.truncate_push(height, self.state.u().addr_count.into())?;
|
||||
|
||||
self.inner
|
||||
.truncate_push(height, &self.state.u().inner)
|
||||
}
|
||||
|
||||
fn compute_then_truncate_push_unrealized_states(
|
||||
&mut self,
|
||||
height: Height,
|
||||
height_price: Option<Dollars>,
|
||||
dateindex: Option<DateIndex>,
|
||||
date_price: Option<Option<Dollars>>,
|
||||
) -> Result<()> {
|
||||
self.inner.compute_then_truncate_push_unrealized_states(
|
||||
height,
|
||||
height_price,
|
||||
dateindex,
|
||||
date_price,
|
||||
&self.state.u().inner,
|
||||
)
|
||||
}
|
||||
|
||||
fn safe_flush_stateful_vecs(&mut self, height: Height, exit: &Exit) -> Result<()> {
|
||||
self.height_to_addr_count.safe_write(exit)?;
|
||||
|
||||
self.inner
|
||||
.safe_flush_stateful_vecs(height, exit, &mut self.state.um().inner)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compute_rest_part1(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.indexes_to_addr_count.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&self.height_to_addr_count),
|
||||
)?;
|
||||
|
||||
self.inner
|
||||
.compute_rest_part1(indexes, price, starting_indexes, exit)
|
||||
}
|
||||
}
|
||||
|
||||
impl CohortVecs for Vecs {
|
||||
fn compute_from_stateful(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.height_to_addr_count.compute_sum_of_others(
|
||||
starting_indexes.height,
|
||||
others
|
||||
.iter()
|
||||
.map(|v| &v.height_to_addr_count)
|
||||
.collect::<Vec<_>>()
|
||||
.as_slice(),
|
||||
exit,
|
||||
)?;
|
||||
self.inner.compute_from_stateful(
|
||||
starting_indexes,
|
||||
&others.iter().map(|v| &v.inner).collect::<Vec<_>>(),
|
||||
exit,
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compute_rest_part2(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
height_to_supply: &impl IterableVec<Height, Bitcoin>,
|
||||
dateindex_to_supply: &impl IterableVec<DateIndex, Bitcoin>,
|
||||
height_to_market_cap: Option<&impl IterableVec<Height, Dollars>>,
|
||||
dateindex_to_market_cap: Option<&impl IterableVec<DateIndex, Dollars>>,
|
||||
height_to_realized_cap: Option<&impl IterableVec<Height, Dollars>>,
|
||||
dateindex_to_realized_cap: Option<&impl IterableVec<DateIndex, Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.inner.compute_rest_part2(
|
||||
indexes,
|
||||
price,
|
||||
starting_indexes,
|
||||
height_to_supply,
|
||||
dateindex_to_supply,
|
||||
height_to_market_cap,
|
||||
dateindex_to_market_cap,
|
||||
height_to_realized_cap,
|
||||
dateindex_to_realized_cap,
|
||||
exit,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Filtered for Vecs {
|
||||
fn filter(&self) -> &Filter {
|
||||
&self.inner.filter
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_grouper::{AddressGroups, AmountFilter, Filter, Filtered};
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, DateIndex, Dollars, Height, Version};
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
use rayon::prelude::*;
|
||||
use vecdb::{Database, Exit, IterableVec};
|
||||
|
||||
use crate::{
|
||||
Indexes, indexes, price,
|
||||
stateful::{
|
||||
address_cohort,
|
||||
r#trait::{CohortVecs, DynCohortVecs},
|
||||
},
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::new(0);
|
||||
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
pub struct Vecs(AddressGroups<address_cohort::Vecs>);
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
states_path: &Path,
|
||||
) -> Result<Self> {
|
||||
Ok(Self(AddressGroups::new(|filter| {
|
||||
let states_path = match &filter {
|
||||
Filter::Amount(AmountFilter::Range(_)) => Some(states_path),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
address_cohort::Vecs::forced_import(
|
||||
db,
|
||||
filter,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
)
|
||||
.unwrap()
|
||||
})))
|
||||
}
|
||||
|
||||
pub fn compute_overlapping_vecs(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let by_size_range = &self.0.amount_range;
|
||||
|
||||
[
|
||||
self.0
|
||||
.ge_amount
|
||||
.par_iter_mut()
|
||||
.map(|vecs| {
|
||||
let filter = vecs.filter().clone();
|
||||
(
|
||||
vecs,
|
||||
by_size_range
|
||||
.iter()
|
||||
.filter(|other| filter.includes(other.filter()))
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
self.0
|
||||
.lt_amount
|
||||
.par_iter_mut()
|
||||
.map(|vecs| {
|
||||
let filter = vecs.filter().clone();
|
||||
(
|
||||
vecs,
|
||||
by_size_range
|
||||
.iter()
|
||||
.filter(|other| filter.includes(other.filter()))
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.try_for_each(|(vecs, stateful)| {
|
||||
vecs.compute_from_stateful(starting_indexes, &stateful, exit)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute_rest_part1(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.par_iter_mut()
|
||||
.try_for_each(|v| v.compute_rest_part1(indexes, price, starting_indexes, exit))
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn compute_rest_part2(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
height_to_supply: &impl IterableVec<Height, Bitcoin>,
|
||||
dateindex_to_supply: &impl IterableVec<DateIndex, Bitcoin>,
|
||||
height_to_market_cap: Option<&impl IterableVec<Height, Dollars>>,
|
||||
dateindex_to_market_cap: Option<&impl IterableVec<DateIndex, Dollars>>,
|
||||
height_to_realized_cap: Option<&impl IterableVec<Height, Dollars>>,
|
||||
dateindex_to_realized_cap: Option<&impl IterableVec<DateIndex, Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.0.par_iter_mut().try_for_each(|v| {
|
||||
v.compute_rest_part2(
|
||||
indexes,
|
||||
price,
|
||||
starting_indexes,
|
||||
height_to_supply,
|
||||
dateindex_to_supply,
|
||||
height_to_market_cap,
|
||||
dateindex_to_market_cap,
|
||||
height_to_realized_cap,
|
||||
dateindex_to_realized_cap,
|
||||
exit,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn safe_flush_stateful_vecs(&mut self, height: Height, exit: &Exit) -> Result<()> {
|
||||
self.par_iter_separate_mut()
|
||||
.try_for_each(|v| v.safe_flush_stateful_vecs(height, exit))
|
||||
}
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
use brk_error::{Error, Result};
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
AnyAddressIndex, EmptyAddressData, EmptyAddressIndex, Height, LoadedAddressData,
|
||||
LoadedAddressIndex, OutputType, P2AAddressIndex, P2PK33AddressIndex, P2PK65AddressIndex,
|
||||
P2PKHAddressIndex, P2SHAddressIndex, P2TRAddressIndex, P2WPKHAddressIndex, P2WSHAddressIndex,
|
||||
TypeIndex,
|
||||
};
|
||||
use vecdb::{AnyStoredVec, BytesVec, GenericStoredVec, Reader, Stamp};
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct AnyAddressIndexesVecs {
|
||||
pub p2pk33: BytesVec<P2PK33AddressIndex, AnyAddressIndex>,
|
||||
pub p2pk65: BytesVec<P2PK65AddressIndex, AnyAddressIndex>,
|
||||
pub p2pkh: BytesVec<P2PKHAddressIndex, AnyAddressIndex>,
|
||||
pub p2sh: BytesVec<P2SHAddressIndex, AnyAddressIndex>,
|
||||
pub p2tr: BytesVec<P2TRAddressIndex, AnyAddressIndex>,
|
||||
pub p2wpkh: BytesVec<P2WPKHAddressIndex, AnyAddressIndex>,
|
||||
pub p2wsh: BytesVec<P2WSHAddressIndex, AnyAddressIndex>,
|
||||
pub p2a: BytesVec<P2AAddressIndex, AnyAddressIndex>,
|
||||
}
|
||||
|
||||
impl AnyAddressIndexesVecs {
|
||||
pub fn min_stamped_height(&self) -> Height {
|
||||
Height::from(self.p2pk33.stamp())
|
||||
.incremented()
|
||||
.min(Height::from(self.p2pk65.stamp()).incremented())
|
||||
.min(Height::from(self.p2pkh.stamp()).incremented())
|
||||
.min(Height::from(self.p2sh.stamp()).incremented())
|
||||
.min(Height::from(self.p2tr.stamp()).incremented())
|
||||
.min(Height::from(self.p2wpkh.stamp()).incremented())
|
||||
.min(Height::from(self.p2wsh.stamp()).incremented())
|
||||
.min(Height::from(self.p2a.stamp()).incremented())
|
||||
}
|
||||
|
||||
pub fn rollback_before(&mut self, stamp: Stamp) -> Result<[Stamp; 8]> {
|
||||
Ok([
|
||||
self.p2pk33.rollback_before(stamp)?,
|
||||
self.p2pk65.rollback_before(stamp)?,
|
||||
self.p2pkh.rollback_before(stamp)?,
|
||||
self.p2sh.rollback_before(stamp)?,
|
||||
self.p2tr.rollback_before(stamp)?,
|
||||
self.p2wpkh.rollback_before(stamp)?,
|
||||
self.p2wsh.rollback_before(stamp)?,
|
||||
self.p2a.rollback_before(stamp)?,
|
||||
])
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) -> Result<()> {
|
||||
self.p2pk33.reset()?;
|
||||
self.p2pk65.reset()?;
|
||||
self.p2pkh.reset()?;
|
||||
self.p2sh.reset()?;
|
||||
self.p2tr.reset()?;
|
||||
self.p2wpkh.reset()?;
|
||||
self.p2wsh.reset()?;
|
||||
self.p2a.reset()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_anyaddressindex(
|
||||
&self,
|
||||
address_type: OutputType,
|
||||
typeindex: TypeIndex,
|
||||
reader: &Reader,
|
||||
) -> AnyAddressIndex {
|
||||
match address_type {
|
||||
OutputType::P2PK33 => self
|
||||
.p2pk33
|
||||
.get_pushed_or_read_at_unwrap(typeindex.into(), reader),
|
||||
OutputType::P2PK65 => self
|
||||
.p2pk65
|
||||
.get_pushed_or_read_at_unwrap(typeindex.into(), reader),
|
||||
OutputType::P2PKH => self
|
||||
.p2pkh
|
||||
.get_pushed_or_read_at_unwrap(typeindex.into(), reader),
|
||||
OutputType::P2SH => self
|
||||
.p2sh
|
||||
.get_pushed_or_read_at_unwrap(typeindex.into(), reader),
|
||||
OutputType::P2TR => self
|
||||
.p2tr
|
||||
.get_pushed_or_read_at_unwrap(typeindex.into(), reader),
|
||||
OutputType::P2WPKH => self
|
||||
.p2wpkh
|
||||
.get_pushed_or_read_at_unwrap(typeindex.into(), reader),
|
||||
OutputType::P2WSH => self
|
||||
.p2wsh
|
||||
.get_pushed_or_read_at_unwrap(typeindex.into(), reader),
|
||||
OutputType::P2A => self
|
||||
.p2a
|
||||
.get_pushed_or_read_at_unwrap(typeindex.into(), reader),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_anyaddressindex_once(
|
||||
&self,
|
||||
address_type: OutputType,
|
||||
typeindex: TypeIndex,
|
||||
) -> Result<AnyAddressIndex> {
|
||||
match address_type {
|
||||
OutputType::P2PK33 => self
|
||||
.p2pk33
|
||||
.read_at_once(typeindex.into())
|
||||
.map_err(|e| e.into()),
|
||||
OutputType::P2PK65 => self
|
||||
.p2pk65
|
||||
.read_at_once(typeindex.into())
|
||||
.map_err(|e| e.into()),
|
||||
OutputType::P2PKH => self
|
||||
.p2pkh
|
||||
.read_at_once(typeindex.into())
|
||||
.map_err(|e| e.into()),
|
||||
OutputType::P2SH => self
|
||||
.p2sh
|
||||
.read_at_once(typeindex.into())
|
||||
.map_err(|e| e.into()),
|
||||
OutputType::P2TR => self
|
||||
.p2tr
|
||||
.read_at_once(typeindex.into())
|
||||
.map_err(|e| e.into()),
|
||||
OutputType::P2WPKH => self
|
||||
.p2wpkh
|
||||
.read_at_once(typeindex.into())
|
||||
.map_err(|e| e.into()),
|
||||
OutputType::P2WSH => self
|
||||
.p2wsh
|
||||
.read_at_once(typeindex.into())
|
||||
.map_err(|e| e.into()),
|
||||
OutputType::P2A => self
|
||||
.p2a
|
||||
.read_at_once(typeindex.into())
|
||||
.map_err(|e| e.into()),
|
||||
_ => Err(Error::UnsupportedType(address_type.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_or_push(
|
||||
&mut self,
|
||||
address_type: OutputType,
|
||||
typeindex: TypeIndex,
|
||||
anyaddressindex: AnyAddressIndex,
|
||||
) -> Result<()> {
|
||||
(match address_type {
|
||||
OutputType::P2PK33 => self
|
||||
.p2pk33
|
||||
.update_or_push(typeindex.into(), anyaddressindex),
|
||||
OutputType::P2PK65 => self
|
||||
.p2pk65
|
||||
.update_or_push(typeindex.into(), anyaddressindex),
|
||||
OutputType::P2PKH => self.p2pkh.update_or_push(typeindex.into(), anyaddressindex),
|
||||
OutputType::P2SH => self.p2sh.update_or_push(typeindex.into(), anyaddressindex),
|
||||
OutputType::P2TR => self.p2tr.update_or_push(typeindex.into(), anyaddressindex),
|
||||
OutputType::P2WPKH => self
|
||||
.p2wpkh
|
||||
.update_or_push(typeindex.into(), anyaddressindex),
|
||||
OutputType::P2WSH => self.p2wsh.update_or_push(typeindex.into(), anyaddressindex),
|
||||
OutputType::P2A => self.p2a.update_or_push(typeindex.into(), anyaddressindex),
|
||||
_ => unreachable!(),
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn stamped_flush_maybe_with_changes(
|
||||
&mut self,
|
||||
stamp: Stamp,
|
||||
with_changes: bool,
|
||||
) -> Result<()> {
|
||||
self.p2pk33
|
||||
.stamped_flush_maybe_with_changes(stamp, with_changes)?;
|
||||
self.p2pk65
|
||||
.stamped_flush_maybe_with_changes(stamp, with_changes)?;
|
||||
self.p2pkh
|
||||
.stamped_flush_maybe_with_changes(stamp, with_changes)?;
|
||||
self.p2sh
|
||||
.stamped_flush_maybe_with_changes(stamp, with_changes)?;
|
||||
self.p2tr
|
||||
.stamped_flush_maybe_with_changes(stamp, with_changes)?;
|
||||
self.p2wpkh
|
||||
.stamped_flush_maybe_with_changes(stamp, with_changes)?;
|
||||
self.p2wsh
|
||||
.stamped_flush_maybe_with_changes(stamp, with_changes)?;
|
||||
self.p2a
|
||||
.stamped_flush_maybe_with_changes(stamp, with_changes)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct AddressesDataVecs {
|
||||
pub loaded: BytesVec<LoadedAddressIndex, LoadedAddressData>,
|
||||
pub empty: BytesVec<EmptyAddressIndex, EmptyAddressData>,
|
||||
}
|
||||
|
||||
impl AddressesDataVecs {
|
||||
pub fn min_stamped_height(&self) -> Height {
|
||||
Height::from(self.loaded.stamp())
|
||||
.incremented()
|
||||
.min(Height::from(self.empty.stamp()).incremented())
|
||||
}
|
||||
|
||||
pub fn rollback_before(&mut self, stamp: Stamp) -> Result<[Stamp; 2]> {
|
||||
Ok([
|
||||
self.loaded.rollback_before(stamp)?,
|
||||
self.empty.rollback_before(stamp)?,
|
||||
])
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) -> Result<()> {
|
||||
self.loaded.reset()?;
|
||||
self.empty.reset()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn stamped_flush_maybe_with_changes(
|
||||
&mut self,
|
||||
stamp: Stamp,
|
||||
with_changes: bool,
|
||||
) -> Result<()> {
|
||||
self.loaded
|
||||
.stamped_flush_maybe_with_changes(stamp, with_changes)?;
|
||||
self.empty
|
||||
.stamped_flush_maybe_with_changes(stamp, with_changes)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
use brk_grouper::ByAddressType;
|
||||
use brk_types::Height;
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
use vecdb::TypedVecIterator;
|
||||
|
||||
use super::AddressTypeToHeightToAddressCount;
|
||||
|
||||
#[derive(Debug, Default, Deref, DerefMut)]
|
||||
pub struct AddressTypeToAddressCount(ByAddressType<u64>);
|
||||
|
||||
impl From<(&AddressTypeToHeightToAddressCount, Height)> for AddressTypeToAddressCount {
|
||||
#[inline]
|
||||
fn from((groups, starting_height): (&AddressTypeToHeightToAddressCount, Height)) -> Self {
|
||||
if let Some(prev_height) = starting_height.decremented() {
|
||||
Self(ByAddressType {
|
||||
p2pk65: groups.p2pk65.into_iter().get_unwrap(prev_height).into(),
|
||||
p2pk33: groups.p2pk33.into_iter().get_unwrap(prev_height).into(),
|
||||
p2pkh: groups.p2pkh.into_iter().get_unwrap(prev_height).into(),
|
||||
p2sh: groups.p2sh.into_iter().get_unwrap(prev_height).into(),
|
||||
p2wpkh: groups.p2wpkh.into_iter().get_unwrap(prev_height).into(),
|
||||
p2wsh: groups.p2wsh.into_iter().get_unwrap(prev_height).into(),
|
||||
p2tr: groups.p2tr.into_iter().get_unwrap(prev_height).into(),
|
||||
p2a: groups.p2a.into_iter().get_unwrap(prev_height).into(),
|
||||
})
|
||||
} else {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
use brk_error::Result;
|
||||
use brk_grouper::ByAddressType;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, StoredU64};
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
use vecdb::{PcoVec, EagerVec, GenericStoredVec};
|
||||
|
||||
use super::AddressTypeToAddressCount;
|
||||
|
||||
#[derive(Debug, Clone, Deref, DerefMut, Traversable)]
|
||||
pub struct AddressTypeToHeightToAddressCount(ByAddressType<EagerVec<PcoVec<Height, StoredU64>>>);
|
||||
|
||||
impl From<ByAddressType<EagerVec<PcoVec<Height, StoredU64>>>> for AddressTypeToHeightToAddressCount {
|
||||
#[inline]
|
||||
fn from(value: ByAddressType<EagerVec<PcoVec<Height, StoredU64>>>) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddressTypeToHeightToAddressCount {
|
||||
pub fn truncate_push(
|
||||
&mut self,
|
||||
height: Height,
|
||||
addresstype_to_usize: &AddressTypeToAddressCount,
|
||||
) -> Result<()> {
|
||||
self.p2pk65
|
||||
.truncate_push(height, addresstype_to_usize.p2pk65.into())?;
|
||||
self.p2pk33
|
||||
.truncate_push(height, addresstype_to_usize.p2pk33.into())?;
|
||||
self.p2pkh
|
||||
.truncate_push(height, addresstype_to_usize.p2pkh.into())?;
|
||||
self.p2sh
|
||||
.truncate_push(height, addresstype_to_usize.p2sh.into())?;
|
||||
self.p2wpkh
|
||||
.truncate_push(height, addresstype_to_usize.p2wpkh.into())?;
|
||||
self.p2wsh
|
||||
.truncate_push(height, addresstype_to_usize.p2wsh.into())?;
|
||||
self.p2tr
|
||||
.truncate_push(height, addresstype_to_usize.p2tr.into())?;
|
||||
self.p2a
|
||||
.truncate_push(height, addresstype_to_usize.p2a.into())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use brk_types::Height;
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
|
||||
use crate::stateful::AddressTypeToVec;
|
||||
|
||||
#[derive(Debug, Default, Deref, DerefMut)]
|
||||
pub struct HeightToAddressTypeToVec<T>(pub BTreeMap<Height, AddressTypeToVec<T>>);
|
||||
@@ -1,80 +0,0 @@
|
||||
use brk_error::Result;
|
||||
use brk_grouper::ByAddressType;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::StoredU64;
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
use vecdb::Exit;
|
||||
|
||||
use crate::{Indexes, grouped::ComputedVecsFromHeight, indexes};
|
||||
|
||||
use super::AddressTypeToHeightToAddressCount;
|
||||
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
pub struct AddressTypeToIndexesToAddressCount(ByAddressType<ComputedVecsFromHeight<StoredU64>>);
|
||||
|
||||
impl From<ByAddressType<ComputedVecsFromHeight<StoredU64>>> for AddressTypeToIndexesToAddressCount {
|
||||
#[inline]
|
||||
fn from(value: ByAddressType<ComputedVecsFromHeight<StoredU64>>) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddressTypeToIndexesToAddressCount {
|
||||
pub fn compute(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
addresstype_to_height_to_addresscount: &AddressTypeToHeightToAddressCount,
|
||||
) -> Result<()> {
|
||||
self.p2pk65.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&addresstype_to_height_to_addresscount.p2pk65),
|
||||
)?;
|
||||
self.p2pk33.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&addresstype_to_height_to_addresscount.p2pk33),
|
||||
)?;
|
||||
self.p2pkh.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&addresstype_to_height_to_addresscount.p2pkh),
|
||||
)?;
|
||||
self.p2sh.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&addresstype_to_height_to_addresscount.p2sh),
|
||||
)?;
|
||||
self.p2wpkh.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&addresstype_to_height_to_addresscount.p2wpkh),
|
||||
)?;
|
||||
self.p2wsh.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&addresstype_to_height_to_addresscount.p2wsh),
|
||||
)?;
|
||||
self.p2tr.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&addresstype_to_height_to_addresscount.p2tr),
|
||||
)?;
|
||||
self.p2a.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&addresstype_to_height_to_addresscount.p2a),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
mod addresscount;
|
||||
mod height_to_addresscount;
|
||||
mod height_to_vec;
|
||||
mod indexes_to_addresscount;
|
||||
mod typeindex_map;
|
||||
mod vec;
|
||||
|
||||
pub use addresscount::*;
|
||||
pub use height_to_addresscount::*;
|
||||
pub use height_to_vec::*;
|
||||
pub use indexes_to_addresscount::*;
|
||||
pub use typeindex_map::*;
|
||||
pub use vec::*;
|
||||
@@ -1,100 +0,0 @@
|
||||
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>>);
|
||||
|
||||
impl<T> AddressTypeToTypeIndexMap<T> {
|
||||
pub fn merge(mut self, mut other: Self) -> Self {
|
||||
Self::merge_(&mut self.p2pk65, &mut other.p2pk65);
|
||||
Self::merge_(&mut self.p2pk33, &mut other.p2pk33);
|
||||
Self::merge_(&mut self.p2pkh, &mut other.p2pkh);
|
||||
Self::merge_(&mut self.p2sh, &mut other.p2sh);
|
||||
Self::merge_(&mut self.p2wpkh, &mut other.p2wpkh);
|
||||
Self::merge_(&mut self.p2wsh, &mut other.p2wsh);
|
||||
Self::merge_(&mut self.p2tr, &mut other.p2tr);
|
||||
Self::merge_(&mut self.p2a, &mut other.p2a);
|
||||
self
|
||||
}
|
||||
|
||||
fn merge_(own: &mut FxHashMap<TypeIndex, T>, other: &mut FxHashMap<TypeIndex, T>) {
|
||||
if own.len() < other.len() {
|
||||
mem::swap(own, other);
|
||||
}
|
||||
own.extend(other.drain());
|
||||
}
|
||||
|
||||
// pub fn get_for_type(&self, address_type: OutputType, typeindex: &TypeIndex) -> Option<&T> {
|
||||
// self.get(address_type).unwrap().get(typeindex)
|
||||
// }
|
||||
|
||||
pub fn insert_for_type(&mut self, address_type: OutputType, typeindex: TypeIndex, value: T) {
|
||||
self.get_mut(address_type).unwrap().insert(typeindex, value);
|
||||
}
|
||||
|
||||
pub fn remove_for_type(&mut self, address_type: OutputType, typeindex: &TypeIndex) -> T {
|
||||
self.get_mut(address_type)
|
||||
.unwrap()
|
||||
.remove(typeindex)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn into_sorted_iter(self) -> impl Iterator<Item = (OutputType, Vec<(TypeIndex, T)>)> {
|
||||
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> {
|
||||
fn default() -> Self {
|
||||
Self(ByAddressType {
|
||||
p2pk65: FxHashMap::default(),
|
||||
p2pk33: FxHashMap::default(),
|
||||
p2pkh: FxHashMap::default(),
|
||||
p2sh: FxHashMap::default(),
|
||||
p2wpkh: FxHashMap::default(),
|
||||
p2wsh: FxHashMap::default(),
|
||||
p2tr: FxHashMap::default(),
|
||||
p2a: FxHashMap::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AddressTypeToTypeIndexMap<SmallVec<T>>
|
||||
where
|
||||
T: Array,
|
||||
{
|
||||
pub fn merge_vec(mut self, other: Self) -> Self {
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(other_vec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
use std::mem;
|
||||
|
||||
use brk_grouper::ByAddressType;
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
|
||||
#[derive(Debug, Deref, DerefMut)]
|
||||
pub struct AddressTypeToVec<T>(ByAddressType<Vec<T>>);
|
||||
|
||||
impl<T> AddressTypeToVec<T> {
|
||||
pub fn merge(mut self, mut other: Self) -> Self {
|
||||
Self::merge_(&mut self.p2pk65, &mut other.p2pk65);
|
||||
Self::merge_(&mut self.p2pk33, &mut other.p2pk33);
|
||||
Self::merge_(&mut self.p2pkh, &mut other.p2pkh);
|
||||
Self::merge_(&mut self.p2sh, &mut other.p2sh);
|
||||
Self::merge_(&mut self.p2wpkh, &mut other.p2wpkh);
|
||||
Self::merge_(&mut self.p2wsh, &mut other.p2wsh);
|
||||
Self::merge_(&mut self.p2tr, &mut other.p2tr);
|
||||
Self::merge_(&mut self.p2a, &mut other.p2a);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn merge_mut(&mut self, mut other: Self) {
|
||||
Self::merge_(&mut self.p2pk65, &mut other.p2pk65);
|
||||
Self::merge_(&mut self.p2pk33, &mut other.p2pk33);
|
||||
Self::merge_(&mut self.p2pkh, &mut other.p2pkh);
|
||||
Self::merge_(&mut self.p2sh, &mut other.p2sh);
|
||||
Self::merge_(&mut self.p2wpkh, &mut other.p2wpkh);
|
||||
Self::merge_(&mut self.p2wsh, &mut other.p2wsh);
|
||||
Self::merge_(&mut self.p2tr, &mut other.p2tr);
|
||||
Self::merge_(&mut self.p2a, &mut other.p2a);
|
||||
}
|
||||
|
||||
fn merge_(own: &mut Vec<T>, other: &mut Vec<T>) {
|
||||
if own.len() >= other.len() {
|
||||
own.append(other);
|
||||
} else {
|
||||
other.append(own);
|
||||
mem::swap(own, other);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap(self) -> ByAddressType<Vec<T>> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for AddressTypeToVec<T> {
|
||||
fn default() -> Self {
|
||||
Self(ByAddressType {
|
||||
p2pk65: vec![],
|
||||
p2pk33: vec![],
|
||||
p2pkh: vec![],
|
||||
p2sh: vec![],
|
||||
p2wpkh: vec![],
|
||||
p2wsh: vec![],
|
||||
p2tr: vec![],
|
||||
p2a: vec![],
|
||||
})
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,914 +0,0 @@
|
||||
//! Import and validation methods for Vecs.
|
||||
//!
|
||||
//! This module contains methods for:
|
||||
//! - `forced_import`: Creating a new Vecs instance from database
|
||||
//! - `import_state`: Importing state when resuming from checkpoint
|
||||
//! - `validate_computed_versions`: Version validation
|
||||
//! - `min_height_vecs_len`: Finding minimum vector length
|
||||
|
||||
use brk_error::{Error, Result};
|
||||
use brk_grouper::{CohortContext, Filter};
|
||||
use brk_types::{DateIndex, Dollars, Height, Sats, StoredF32, StoredF64, Version};
|
||||
use vecdb::{
|
||||
AnyVec, Database, EagerVec, GenericStoredVec, ImportableVec, IterableCloneableVec, PcoVec,
|
||||
StoredVec, TypedVecIterator,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
grouped::{
|
||||
ComputedHeightValueVecs, ComputedRatioVecsFromDateIndex, ComputedValueVecsFromDateIndex,
|
||||
ComputedValueVecsFromHeight, ComputedVecsFromDateIndex, ComputedVecsFromHeight,
|
||||
PricePercentiles, Source, VecBuilderOptions,
|
||||
},
|
||||
indexes, price,
|
||||
states::CohortState,
|
||||
utils::OptionExt,
|
||||
};
|
||||
|
||||
use super::Vecs;
|
||||
|
||||
impl Vecs {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn forced_import(
|
||||
db: &Database,
|
||||
filter: Filter,
|
||||
context: CohortContext,
|
||||
parent_version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
) -> Result<Self> {
|
||||
let compute_dollars = price.is_some();
|
||||
let extended = filter.is_extended(context);
|
||||
let compute_rel_to_all = filter.compute_rel_to_all();
|
||||
let compute_adjusted = filter.compute_adjusted(context);
|
||||
|
||||
let version = parent_version + Version::ZERO;
|
||||
|
||||
let name_prefix = filter.to_full_name(context);
|
||||
let suffix = |s: &str| {
|
||||
if name_prefix.is_empty() {
|
||||
s.to_string()
|
||||
} else {
|
||||
format!("{name_prefix}_{s}")
|
||||
}
|
||||
};
|
||||
|
||||
// Helper macros for imports
|
||||
macro_rules! eager {
|
||||
($idx:ty, $val:ty, $name:expr, $v:expr) => {
|
||||
EagerVec::<PcoVec<$idx, $val>>::forced_import(db, &suffix($name), version + $v)
|
||||
.unwrap()
|
||||
};
|
||||
}
|
||||
macro_rules! computed_h {
|
||||
($name:expr, $source:expr, $v:expr, $opts:expr $(,)?) => {
|
||||
ComputedVecsFromHeight::forced_import(
|
||||
db,
|
||||
&suffix($name),
|
||||
$source,
|
||||
version + $v,
|
||||
indexes,
|
||||
$opts,
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
}
|
||||
macro_rules! computed_di {
|
||||
($name:expr, $source:expr, $v:expr, $opts:expr $(,)?) => {
|
||||
ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&suffix($name),
|
||||
$source,
|
||||
version + $v,
|
||||
indexes,
|
||||
$opts,
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
}
|
||||
|
||||
// Common version patterns
|
||||
let v0 = Version::ZERO;
|
||||
let v1 = Version::ONE;
|
||||
let v2 = Version::TWO;
|
||||
let v3 = Version::new(3);
|
||||
let last = || VecBuilderOptions::default().add_last();
|
||||
let sum = || VecBuilderOptions::default().add_sum();
|
||||
let sum_cum = || VecBuilderOptions::default().add_sum().add_cumulative();
|
||||
|
||||
// Pre-create dateindex vecs that are used in computed vecs
|
||||
let dateindex_to_supply_in_profit =
|
||||
compute_dollars.then(|| eager!(DateIndex, Sats, "supply_in_profit", v0));
|
||||
let dateindex_to_supply_in_loss =
|
||||
compute_dollars.then(|| eager!(DateIndex, Sats, "supply_in_loss", v0));
|
||||
let dateindex_to_unrealized_profit =
|
||||
compute_dollars.then(|| eager!(DateIndex, Dollars, "unrealized_profit", v0));
|
||||
let dateindex_to_unrealized_loss =
|
||||
compute_dollars.then(|| eager!(DateIndex, Dollars, "unrealized_loss", v0));
|
||||
|
||||
Ok(Self {
|
||||
filter,
|
||||
|
||||
// ==================== SUPPLY & UTXO COUNT ====================
|
||||
height_to_supply: EagerVec::forced_import(db, &suffix("supply"), version + v0)?,
|
||||
height_to_supply_value: ComputedHeightValueVecs::forced_import(
|
||||
db,
|
||||
&suffix("supply"),
|
||||
Source::None,
|
||||
version + v0,
|
||||
compute_dollars,
|
||||
)?,
|
||||
indexes_to_supply: ComputedValueVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&suffix("supply"),
|
||||
Source::Compute,
|
||||
version + v1,
|
||||
last(),
|
||||
compute_dollars,
|
||||
indexes,
|
||||
)?,
|
||||
height_to_utxo_count: EagerVec::forced_import(db, &suffix("utxo_count"), version + v0)?,
|
||||
indexes_to_utxo_count: computed_h!("utxo_count", Source::None, v0, last()),
|
||||
height_to_supply_half_value: ComputedHeightValueVecs::forced_import(
|
||||
db,
|
||||
&suffix("supply_half"),
|
||||
Source::Compute,
|
||||
version + v0,
|
||||
compute_dollars,
|
||||
)?,
|
||||
indexes_to_supply_half: ComputedValueVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&suffix("supply_half"),
|
||||
Source::Compute,
|
||||
version + v0,
|
||||
last(),
|
||||
compute_dollars,
|
||||
indexes,
|
||||
)?,
|
||||
|
||||
// ==================== ACTIVITY ====================
|
||||
height_to_sent: EagerVec::forced_import(db, &suffix("sent"), version + v0)?,
|
||||
indexes_to_sent: ComputedValueVecsFromHeight::forced_import(
|
||||
db,
|
||||
&suffix("sent"),
|
||||
Source::None,
|
||||
version + v0,
|
||||
sum(),
|
||||
compute_dollars,
|
||||
indexes,
|
||||
)?,
|
||||
height_to_satblocks_destroyed: EagerVec::forced_import(
|
||||
db,
|
||||
&suffix("satblocks_destroyed"),
|
||||
version + v0,
|
||||
)?,
|
||||
height_to_satdays_destroyed: EagerVec::forced_import(
|
||||
db,
|
||||
&suffix("satdays_destroyed"),
|
||||
version + v0,
|
||||
)?,
|
||||
indexes_to_coinblocks_destroyed: computed_h!(
|
||||
"coinblocks_destroyed",
|
||||
Source::Compute,
|
||||
v2,
|
||||
sum_cum(),
|
||||
),
|
||||
indexes_to_coindays_destroyed: computed_h!(
|
||||
"coindays_destroyed",
|
||||
Source::Compute,
|
||||
v2,
|
||||
sum_cum(),
|
||||
),
|
||||
|
||||
// ==================== REALIZED CAP & PRICE ====================
|
||||
height_to_realized_cap: compute_dollars
|
||||
.then(|| eager!(Height, Dollars, "realized_cap", v0)),
|
||||
indexes_to_realized_cap: compute_dollars
|
||||
.then(|| computed_h!("realized_cap", Source::None, v0, last())),
|
||||
indexes_to_realized_price: compute_dollars
|
||||
.then(|| computed_h!("realized_price", Source::Compute, v0, last())),
|
||||
indexes_to_realized_price_extra: compute_dollars.then(|| {
|
||||
ComputedRatioVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&suffix("realized_price"),
|
||||
Source::None,
|
||||
version + v0,
|
||||
indexes,
|
||||
extended,
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
indexes_to_realized_cap_rel_to_own_market_cap: (compute_dollars && extended).then(
|
||||
|| {
|
||||
computed_h!(
|
||||
"realized_cap_rel_to_own_market_cap",
|
||||
Source::Compute,
|
||||
v0,
|
||||
last()
|
||||
)
|
||||
},
|
||||
),
|
||||
indexes_to_realized_cap_30d_delta: compute_dollars
|
||||
.then(|| computed_di!("realized_cap_30d_delta", Source::Compute, v0, last())),
|
||||
|
||||
// ==================== REALIZED PROFIT & LOSS ====================
|
||||
height_to_realized_profit: compute_dollars
|
||||
.then(|| eager!(Height, Dollars, "realized_profit", v0)),
|
||||
indexes_to_realized_profit: compute_dollars
|
||||
.then(|| computed_h!("realized_profit", Source::None, v0, sum_cum())),
|
||||
height_to_realized_loss: compute_dollars
|
||||
.then(|| eager!(Height, Dollars, "realized_loss", v0)),
|
||||
indexes_to_realized_loss: compute_dollars
|
||||
.then(|| computed_h!("realized_loss", Source::None, v0, sum_cum())),
|
||||
indexes_to_neg_realized_loss: compute_dollars
|
||||
.then(|| computed_h!("neg_realized_loss", Source::Compute, v1, sum_cum())),
|
||||
indexes_to_net_realized_pnl: compute_dollars
|
||||
.then(|| computed_h!("net_realized_pnl", Source::Compute, v0, sum_cum())),
|
||||
indexes_to_realized_value: compute_dollars
|
||||
.then(|| computed_h!("realized_value", Source::Compute, v0, sum())),
|
||||
indexes_to_realized_profit_rel_to_realized_cap: compute_dollars.then(|| {
|
||||
computed_h!(
|
||||
"realized_profit_rel_to_realized_cap",
|
||||
Source::Compute,
|
||||
v0,
|
||||
sum()
|
||||
)
|
||||
}),
|
||||
indexes_to_realized_loss_rel_to_realized_cap: compute_dollars.then(|| {
|
||||
computed_h!(
|
||||
"realized_loss_rel_to_realized_cap",
|
||||
Source::Compute,
|
||||
v0,
|
||||
sum()
|
||||
)
|
||||
}),
|
||||
indexes_to_net_realized_pnl_rel_to_realized_cap: compute_dollars.then(|| {
|
||||
computed_h!(
|
||||
"net_realized_pnl_rel_to_realized_cap",
|
||||
Source::Compute,
|
||||
v1,
|
||||
sum()
|
||||
)
|
||||
}),
|
||||
height_to_total_realized_pnl: compute_dollars
|
||||
.then(|| eager!(Height, Dollars, "total_realized_pnl", v0)),
|
||||
indexes_to_total_realized_pnl: compute_dollars
|
||||
.then(|| computed_di!("total_realized_pnl", Source::Compute, v1, sum())),
|
||||
dateindex_to_realized_profit_to_loss_ratio: (compute_dollars && extended)
|
||||
.then(|| eager!(DateIndex, StoredF64, "realized_profit_to_loss_ratio", v1)),
|
||||
|
||||
// ==================== VALUE CREATED & DESTROYED ====================
|
||||
height_to_value_created: compute_dollars
|
||||
.then(|| eager!(Height, Dollars, "value_created", v0)),
|
||||
indexes_to_value_created: compute_dollars
|
||||
.then(|| computed_h!("value_created", Source::None, v0, sum())),
|
||||
height_to_value_destroyed: compute_dollars
|
||||
.then(|| eager!(Height, Dollars, "value_destroyed", v0)),
|
||||
indexes_to_value_destroyed: compute_dollars
|
||||
.then(|| computed_h!("value_destroyed", Source::None, v0, sum())),
|
||||
height_to_adjusted_value_created: (compute_dollars && compute_adjusted)
|
||||
.then(|| eager!(Height, Dollars, "adjusted_value_created", v0)),
|
||||
indexes_to_adjusted_value_created: (compute_dollars && compute_adjusted)
|
||||
.then(|| computed_h!("adjusted_value_created", Source::None, v0, sum())),
|
||||
height_to_adjusted_value_destroyed: (compute_dollars && compute_adjusted)
|
||||
.then(|| eager!(Height, Dollars, "adjusted_value_destroyed", v0)),
|
||||
indexes_to_adjusted_value_destroyed: (compute_dollars && compute_adjusted)
|
||||
.then(|| computed_h!("adjusted_value_destroyed", Source::None, v0, sum())),
|
||||
|
||||
// ==================== SOPR ====================
|
||||
dateindex_to_sopr: compute_dollars.then(|| eager!(DateIndex, StoredF64, "sopr", v1)),
|
||||
dateindex_to_sopr_7d_ema: compute_dollars
|
||||
.then(|| eager!(DateIndex, StoredF64, "sopr_7d_ema", v1)),
|
||||
dateindex_to_sopr_30d_ema: compute_dollars
|
||||
.then(|| eager!(DateIndex, StoredF64, "sopr_30d_ema", v1)),
|
||||
dateindex_to_adjusted_sopr: (compute_dollars && compute_adjusted)
|
||||
.then(|| eager!(DateIndex, StoredF64, "adjusted_sopr", v1)),
|
||||
dateindex_to_adjusted_sopr_7d_ema: (compute_dollars && compute_adjusted)
|
||||
.then(|| eager!(DateIndex, StoredF64, "adjusted_sopr_7d_ema", v1)),
|
||||
dateindex_to_adjusted_sopr_30d_ema: (compute_dollars && compute_adjusted)
|
||||
.then(|| eager!(DateIndex, StoredF64, "adjusted_sopr_30d_ema", v1)),
|
||||
|
||||
// ==================== SELL SIDE RISK ====================
|
||||
dateindex_to_sell_side_risk_ratio: compute_dollars
|
||||
.then(|| eager!(DateIndex, StoredF32, "sell_side_risk_ratio", v1)),
|
||||
dateindex_to_sell_side_risk_ratio_7d_ema: compute_dollars
|
||||
.then(|| eager!(DateIndex, StoredF32, "sell_side_risk_ratio_7d_ema", v1)),
|
||||
dateindex_to_sell_side_risk_ratio_30d_ema: compute_dollars
|
||||
.then(|| eager!(DateIndex, StoredF32, "sell_side_risk_ratio_30d_ema", v1)),
|
||||
|
||||
// ==================== SUPPLY IN PROFIT/LOSS ====================
|
||||
height_to_supply_in_profit: compute_dollars
|
||||
.then(|| eager!(Height, Sats, "supply_in_profit", v0)),
|
||||
indexes_to_supply_in_profit: compute_dollars.then(|| {
|
||||
ComputedValueVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&suffix("supply_in_profit"),
|
||||
dateindex_to_supply_in_profit
|
||||
.as_ref()
|
||||
.map(|v| v.boxed_clone())
|
||||
.into(),
|
||||
version + v0,
|
||||
last(),
|
||||
compute_dollars,
|
||||
indexes,
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
height_to_supply_in_loss: compute_dollars
|
||||
.then(|| eager!(Height, Sats, "supply_in_loss", v0)),
|
||||
indexes_to_supply_in_loss: compute_dollars.then(|| {
|
||||
ComputedValueVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&suffix("supply_in_loss"),
|
||||
dateindex_to_supply_in_loss
|
||||
.as_ref()
|
||||
.map(|v| v.boxed_clone())
|
||||
.into(),
|
||||
version + v0,
|
||||
last(),
|
||||
compute_dollars,
|
||||
indexes,
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
dateindex_to_supply_in_profit,
|
||||
dateindex_to_supply_in_loss,
|
||||
height_to_supply_in_profit_value: compute_dollars.then(|| {
|
||||
ComputedHeightValueVecs::forced_import(
|
||||
db,
|
||||
&suffix("supply_in_profit"),
|
||||
Source::None,
|
||||
version + v0,
|
||||
compute_dollars,
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
height_to_supply_in_loss_value: compute_dollars.then(|| {
|
||||
ComputedHeightValueVecs::forced_import(
|
||||
db,
|
||||
&suffix("supply_in_loss"),
|
||||
Source::None,
|
||||
version + v0,
|
||||
compute_dollars,
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
|
||||
// ==================== UNREALIZED PROFIT & LOSS ====================
|
||||
height_to_unrealized_profit: compute_dollars
|
||||
.then(|| eager!(Height, Dollars, "unrealized_profit", v0)),
|
||||
indexes_to_unrealized_profit: compute_dollars.then(|| {
|
||||
ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&suffix("unrealized_profit"),
|
||||
dateindex_to_unrealized_profit
|
||||
.as_ref()
|
||||
.map(|v| v.boxed_clone())
|
||||
.into(),
|
||||
version + v0,
|
||||
indexes,
|
||||
last(),
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
height_to_unrealized_loss: compute_dollars
|
||||
.then(|| eager!(Height, Dollars, "unrealized_loss", v0)),
|
||||
indexes_to_unrealized_loss: compute_dollars.then(|| {
|
||||
ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&suffix("unrealized_loss"),
|
||||
dateindex_to_unrealized_loss
|
||||
.as_ref()
|
||||
.map(|v| v.boxed_clone())
|
||||
.into(),
|
||||
version + v0,
|
||||
indexes,
|
||||
last(),
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
dateindex_to_unrealized_profit,
|
||||
dateindex_to_unrealized_loss,
|
||||
height_to_neg_unrealized_loss: compute_dollars
|
||||
.then(|| eager!(Height, Dollars, "neg_unrealized_loss", v0)),
|
||||
indexes_to_neg_unrealized_loss: compute_dollars
|
||||
.then(|| computed_di!("neg_unrealized_loss", Source::Compute, v0, last())),
|
||||
height_to_net_unrealized_pnl: compute_dollars
|
||||
.then(|| eager!(Height, Dollars, "net_unrealized_pnl", v0)),
|
||||
indexes_to_net_unrealized_pnl: compute_dollars
|
||||
.then(|| computed_di!("net_unrealized_pnl", Source::Compute, v0, last())),
|
||||
height_to_total_unrealized_pnl: compute_dollars
|
||||
.then(|| eager!(Height, Dollars, "total_unrealized_pnl", v0)),
|
||||
indexes_to_total_unrealized_pnl: compute_dollars
|
||||
.then(|| computed_di!("total_unrealized_pnl", Source::Compute, v0, last())),
|
||||
|
||||
// ==================== PRICE PAID ====================
|
||||
height_to_min_price_paid: compute_dollars
|
||||
.then(|| eager!(Height, Dollars, "min_price_paid", v0)),
|
||||
indexes_to_min_price_paid: compute_dollars
|
||||
.then(|| computed_h!("min_price_paid", Source::None, v0, last())),
|
||||
height_to_max_price_paid: compute_dollars
|
||||
.then(|| eager!(Height, Dollars, "max_price_paid", v0)),
|
||||
indexes_to_max_price_paid: compute_dollars
|
||||
.then(|| computed_h!("max_price_paid", Source::None, v0, last())),
|
||||
price_percentiles: (compute_dollars && extended).then(|| {
|
||||
PricePercentiles::forced_import(db, &suffix(""), version + v0, indexes, true)
|
||||
.unwrap()
|
||||
}),
|
||||
|
||||
// ==================== RELATIVE METRICS: UNREALIZED vs MARKET CAP ====================
|
||||
height_to_unrealized_profit_rel_to_market_cap: compute_dollars
|
||||
.then(|| eager!(Height, StoredF32, "unrealized_profit_rel_to_market_cap", v0)),
|
||||
height_to_unrealized_loss_rel_to_market_cap: compute_dollars
|
||||
.then(|| eager!(Height, StoredF32, "unrealized_loss_rel_to_market_cap", v0)),
|
||||
height_to_neg_unrealized_loss_rel_to_market_cap: compute_dollars.then(|| {
|
||||
eager!(
|
||||
Height,
|
||||
StoredF32,
|
||||
"neg_unrealized_loss_rel_to_market_cap",
|
||||
v0
|
||||
)
|
||||
}),
|
||||
height_to_net_unrealized_pnl_rel_to_market_cap: compute_dollars.then(|| {
|
||||
eager!(
|
||||
Height,
|
||||
StoredF32,
|
||||
"net_unrealized_pnl_rel_to_market_cap",
|
||||
v1
|
||||
)
|
||||
}),
|
||||
indexes_to_unrealized_profit_rel_to_market_cap: compute_dollars.then(|| {
|
||||
computed_di!(
|
||||
"unrealized_profit_rel_to_market_cap",
|
||||
Source::Compute,
|
||||
v1,
|
||||
last()
|
||||
)
|
||||
}),
|
||||
indexes_to_unrealized_loss_rel_to_market_cap: compute_dollars.then(|| {
|
||||
computed_di!(
|
||||
"unrealized_loss_rel_to_market_cap",
|
||||
Source::Compute,
|
||||
v1,
|
||||
last()
|
||||
)
|
||||
}),
|
||||
indexes_to_neg_unrealized_loss_rel_to_market_cap: compute_dollars.then(|| {
|
||||
computed_di!(
|
||||
"neg_unrealized_loss_rel_to_market_cap",
|
||||
Source::Compute,
|
||||
v1,
|
||||
last()
|
||||
)
|
||||
}),
|
||||
indexes_to_net_unrealized_pnl_rel_to_market_cap: compute_dollars.then(|| {
|
||||
computed_di!(
|
||||
"net_unrealized_pnl_rel_to_market_cap",
|
||||
Source::Compute,
|
||||
v1,
|
||||
last()
|
||||
)
|
||||
}),
|
||||
|
||||
// ==================== RELATIVE METRICS: UNREALIZED vs OWN MARKET CAP ====================
|
||||
height_to_unrealized_profit_rel_to_own_market_cap: (compute_dollars
|
||||
&& extended
|
||||
&& compute_rel_to_all)
|
||||
.then(|| {
|
||||
eager!(
|
||||
Height,
|
||||
StoredF32,
|
||||
"unrealized_profit_rel_to_own_market_cap",
|
||||
v1
|
||||
)
|
||||
}),
|
||||
height_to_unrealized_loss_rel_to_own_market_cap: (compute_dollars
|
||||
&& extended
|
||||
&& compute_rel_to_all)
|
||||
.then(|| {
|
||||
eager!(
|
||||
Height,
|
||||
StoredF32,
|
||||
"unrealized_loss_rel_to_own_market_cap",
|
||||
v1
|
||||
)
|
||||
}),
|
||||
height_to_neg_unrealized_loss_rel_to_own_market_cap: (compute_dollars
|
||||
&& extended
|
||||
&& compute_rel_to_all)
|
||||
.then(|| {
|
||||
eager!(
|
||||
Height,
|
||||
StoredF32,
|
||||
"neg_unrealized_loss_rel_to_own_market_cap",
|
||||
v1
|
||||
)
|
||||
}),
|
||||
height_to_net_unrealized_pnl_rel_to_own_market_cap: (compute_dollars
|
||||
&& extended
|
||||
&& compute_rel_to_all)
|
||||
.then(|| {
|
||||
eager!(
|
||||
Height,
|
||||
StoredF32,
|
||||
"net_unrealized_pnl_rel_to_own_market_cap",
|
||||
v2
|
||||
)
|
||||
}),
|
||||
indexes_to_unrealized_profit_rel_to_own_market_cap: (compute_dollars
|
||||
&& extended
|
||||
&& compute_rel_to_all)
|
||||
.then(|| {
|
||||
computed_di!(
|
||||
"unrealized_profit_rel_to_own_market_cap",
|
||||
Source::Compute,
|
||||
v2,
|
||||
last()
|
||||
)
|
||||
}),
|
||||
indexes_to_unrealized_loss_rel_to_own_market_cap: (compute_dollars
|
||||
&& extended
|
||||
&& compute_rel_to_all)
|
||||
.then(|| {
|
||||
computed_di!(
|
||||
"unrealized_loss_rel_to_own_market_cap",
|
||||
Source::Compute,
|
||||
v2,
|
||||
last()
|
||||
)
|
||||
}),
|
||||
indexes_to_neg_unrealized_loss_rel_to_own_market_cap: (compute_dollars
|
||||
&& extended
|
||||
&& compute_rel_to_all)
|
||||
.then(|| {
|
||||
computed_di!(
|
||||
"neg_unrealized_loss_rel_to_own_market_cap",
|
||||
Source::Compute,
|
||||
v2,
|
||||
last()
|
||||
)
|
||||
}),
|
||||
indexes_to_net_unrealized_pnl_rel_to_own_market_cap: (compute_dollars
|
||||
&& extended
|
||||
&& compute_rel_to_all)
|
||||
.then(|| {
|
||||
computed_di!(
|
||||
"net_unrealized_pnl_rel_to_own_market_cap",
|
||||
Source::Compute,
|
||||
v2,
|
||||
last()
|
||||
)
|
||||
}),
|
||||
|
||||
// ==================== RELATIVE METRICS: UNREALIZED vs OWN TOTAL UNREALIZED ====================
|
||||
height_to_unrealized_profit_rel_to_own_total_unrealized_pnl: (compute_dollars
|
||||
&& extended)
|
||||
.then(|| {
|
||||
eager!(
|
||||
Height,
|
||||
StoredF32,
|
||||
"unrealized_profit_rel_to_own_total_unrealized_pnl",
|
||||
v0
|
||||
)
|
||||
}),
|
||||
height_to_unrealized_loss_rel_to_own_total_unrealized_pnl: (compute_dollars
|
||||
&& extended)
|
||||
.then(|| {
|
||||
eager!(
|
||||
Height,
|
||||
StoredF32,
|
||||
"unrealized_loss_rel_to_own_total_unrealized_pnl",
|
||||
v0
|
||||
)
|
||||
}),
|
||||
height_to_neg_unrealized_loss_rel_to_own_total_unrealized_pnl: (compute_dollars
|
||||
&& extended)
|
||||
.then(|| {
|
||||
eager!(
|
||||
Height,
|
||||
StoredF32,
|
||||
"neg_unrealized_loss_rel_to_own_total_unrealized_pnl",
|
||||
v0
|
||||
)
|
||||
}),
|
||||
height_to_net_unrealized_pnl_rel_to_own_total_unrealized_pnl: (compute_dollars
|
||||
&& extended)
|
||||
.then(|| {
|
||||
eager!(
|
||||
Height,
|
||||
StoredF32,
|
||||
"net_unrealized_pnl_rel_to_own_total_unrealized_pnl",
|
||||
v1
|
||||
)
|
||||
}),
|
||||
indexes_to_unrealized_profit_rel_to_own_total_unrealized_pnl: (compute_dollars
|
||||
&& extended)
|
||||
.then(|| {
|
||||
computed_di!(
|
||||
"unrealized_profit_rel_to_own_total_unrealized_pnl",
|
||||
Source::Compute,
|
||||
v1,
|
||||
last()
|
||||
)
|
||||
}),
|
||||
indexes_to_unrealized_loss_rel_to_own_total_unrealized_pnl: (compute_dollars
|
||||
&& extended)
|
||||
.then(|| {
|
||||
computed_di!(
|
||||
"unrealized_loss_rel_to_own_total_unrealized_pnl",
|
||||
Source::Compute,
|
||||
v1,
|
||||
last()
|
||||
)
|
||||
}),
|
||||
indexes_to_neg_unrealized_loss_rel_to_own_total_unrealized_pnl: (compute_dollars
|
||||
&& extended)
|
||||
.then(|| {
|
||||
computed_di!(
|
||||
"neg_unrealized_loss_rel_to_own_total_unrealized_pnl",
|
||||
Source::Compute,
|
||||
v1,
|
||||
last()
|
||||
)
|
||||
}),
|
||||
indexes_to_net_unrealized_pnl_rel_to_own_total_unrealized_pnl: (compute_dollars
|
||||
&& extended)
|
||||
.then(|| {
|
||||
computed_di!(
|
||||
"net_unrealized_pnl_rel_to_own_total_unrealized_pnl",
|
||||
Source::Compute,
|
||||
v1,
|
||||
last()
|
||||
)
|
||||
}),
|
||||
|
||||
// ==================== RELATIVE METRICS: SUPPLY vs CIRCULATING/OWN ====================
|
||||
indexes_to_supply_rel_to_circulating_supply: compute_rel_to_all.then(|| {
|
||||
computed_h!(
|
||||
"supply_rel_to_circulating_supply",
|
||||
Source::Compute,
|
||||
v1,
|
||||
last()
|
||||
)
|
||||
}),
|
||||
height_to_supply_in_profit_rel_to_own_supply: compute_dollars
|
||||
.then(|| eager!(Height, StoredF64, "supply_in_profit_rel_to_own_supply", v1)),
|
||||
height_to_supply_in_loss_rel_to_own_supply: compute_dollars
|
||||
.then(|| eager!(Height, StoredF64, "supply_in_loss_rel_to_own_supply", v1)),
|
||||
indexes_to_supply_in_profit_rel_to_own_supply: compute_dollars.then(|| {
|
||||
computed_di!(
|
||||
"supply_in_profit_rel_to_own_supply",
|
||||
Source::Compute,
|
||||
v1,
|
||||
last()
|
||||
)
|
||||
}),
|
||||
indexes_to_supply_in_loss_rel_to_own_supply: compute_dollars.then(|| {
|
||||
computed_di!(
|
||||
"supply_in_loss_rel_to_own_supply",
|
||||
Source::Compute,
|
||||
v1,
|
||||
last()
|
||||
)
|
||||
}),
|
||||
height_to_supply_in_profit_rel_to_circulating_supply: (compute_rel_to_all
|
||||
&& compute_dollars)
|
||||
.then(|| {
|
||||
eager!(
|
||||
Height,
|
||||
StoredF64,
|
||||
"supply_in_profit_rel_to_circulating_supply",
|
||||
v1
|
||||
)
|
||||
}),
|
||||
height_to_supply_in_loss_rel_to_circulating_supply: (compute_rel_to_all
|
||||
&& compute_dollars)
|
||||
.then(|| {
|
||||
eager!(
|
||||
Height,
|
||||
StoredF64,
|
||||
"supply_in_loss_rel_to_circulating_supply",
|
||||
v1
|
||||
)
|
||||
}),
|
||||
indexes_to_supply_in_profit_rel_to_circulating_supply: (compute_rel_to_all
|
||||
&& compute_dollars)
|
||||
.then(|| {
|
||||
computed_di!(
|
||||
"supply_in_profit_rel_to_circulating_supply",
|
||||
Source::Compute,
|
||||
v1,
|
||||
last()
|
||||
)
|
||||
}),
|
||||
indexes_to_supply_in_loss_rel_to_circulating_supply: (compute_rel_to_all
|
||||
&& compute_dollars)
|
||||
.then(|| {
|
||||
computed_di!(
|
||||
"supply_in_loss_rel_to_circulating_supply",
|
||||
Source::Compute,
|
||||
v1,
|
||||
last()
|
||||
)
|
||||
}),
|
||||
|
||||
// ==================== NET REALIZED PNL DELTAS ====================
|
||||
indexes_to_net_realized_pnl_cumulative_30d_delta: compute_dollars.then(|| {
|
||||
computed_di!(
|
||||
"net_realized_pnl_cumulative_30d_delta",
|
||||
Source::Compute,
|
||||
v3,
|
||||
last()
|
||||
)
|
||||
}),
|
||||
indexes_to_net_realized_pnl_cumulative_30d_delta_rel_to_realized_cap: compute_dollars
|
||||
.then(|| {
|
||||
computed_di!(
|
||||
"net_realized_pnl_cumulative_30d_delta_rel_to_realized_cap",
|
||||
Source::Compute,
|
||||
v3,
|
||||
last()
|
||||
)
|
||||
}),
|
||||
indexes_to_net_realized_pnl_cumulative_30d_delta_rel_to_market_cap: compute_dollars
|
||||
.then(|| {
|
||||
computed_di!(
|
||||
"net_realized_pnl_cumulative_30d_delta_rel_to_market_cap",
|
||||
Source::Compute,
|
||||
v3,
|
||||
last()
|
||||
)
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the minimum length of all height-indexed vectors.
|
||||
/// Used to determine the starting point for processing.
|
||||
pub fn min_height_vecs_len(&self) -> usize {
|
||||
[
|
||||
self.height_to_supply.len(),
|
||||
self.height_to_utxo_count.len(),
|
||||
self.height_to_realized_cap
|
||||
.as_ref()
|
||||
.map_or(usize::MAX, |v| v.len()),
|
||||
self.height_to_realized_profit
|
||||
.as_ref()
|
||||
.map_or(usize::MAX, |v| v.len()),
|
||||
self.height_to_realized_loss
|
||||
.as_ref()
|
||||
.map_or(usize::MAX, |v| v.len()),
|
||||
self.height_to_value_created
|
||||
.as_ref()
|
||||
.map_or(usize::MAX, |v| v.len()),
|
||||
self.height_to_adjusted_value_created
|
||||
.as_ref()
|
||||
.map_or(usize::MAX, |v| v.len()),
|
||||
self.height_to_value_destroyed
|
||||
.as_ref()
|
||||
.map_or(usize::MAX, |v| v.len()),
|
||||
self.height_to_adjusted_value_destroyed
|
||||
.as_ref()
|
||||
.map_or(usize::MAX, |v| v.len()),
|
||||
self.height_to_supply_in_profit
|
||||
.as_ref()
|
||||
.map_or(usize::MAX, |v| v.len()),
|
||||
self.height_to_supply_in_loss
|
||||
.as_ref()
|
||||
.map_or(usize::MAX, |v| v.len()),
|
||||
self.height_to_unrealized_profit
|
||||
.as_ref()
|
||||
.map_or(usize::MAX, |v| v.len()),
|
||||
self.height_to_unrealized_loss
|
||||
.as_ref()
|
||||
.map_or(usize::MAX, |v| v.len()),
|
||||
self.height_to_min_price_paid
|
||||
.as_ref()
|
||||
.map_or(usize::MAX, |v| v.len()),
|
||||
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(),
|
||||
]
|
||||
.into_iter()
|
||||
.min()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Import state from a checkpoint when resuming processing.
|
||||
/// Returns the next height to process from.
|
||||
pub fn import_state(
|
||||
&mut self,
|
||||
starting_height: Height,
|
||||
state: &mut CohortState,
|
||||
) -> Result<Height> {
|
||||
if let Some(mut prev_height) = starting_height.decremented() {
|
||||
if self.height_to_realized_cap.as_mut().is_some() {
|
||||
prev_height = state.import_at_or_before(prev_height)?;
|
||||
}
|
||||
|
||||
state.supply.value = self.height_to_supply.into_iter().get_unwrap(prev_height);
|
||||
state.supply.utxo_count = *self
|
||||
.height_to_utxo_count
|
||||
.into_iter()
|
||||
.get_unwrap(prev_height);
|
||||
|
||||
if let Some(height_to_realized_cap) = self.height_to_realized_cap.as_mut() {
|
||||
state.realized.um().cap =
|
||||
height_to_realized_cap.into_iter().get_unwrap(prev_height);
|
||||
}
|
||||
|
||||
Ok(prev_height.incremented())
|
||||
} else {
|
||||
Err(Error::Str("Unset"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Validate that all computed versions match expected values, resetting if needed.
|
||||
pub fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> {
|
||||
// Always-present vecs
|
||||
self.height_to_supply.validate_computed_version_or_reset(
|
||||
base_version + self.height_to_supply.inner_version(),
|
||||
)?;
|
||||
self.height_to_utxo_count
|
||||
.validate_computed_version_or_reset(
|
||||
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(),
|
||||
)?;
|
||||
self.height_to_satdays_destroyed
|
||||
.validate_computed_version_or_reset(
|
||||
base_version + self.height_to_satdays_destroyed.inner_version(),
|
||||
)?;
|
||||
|
||||
// Dollar-dependent vecs
|
||||
if let Some(height_to_realized_cap) = self.height_to_realized_cap.as_mut().as_mut() {
|
||||
height_to_realized_cap.validate_computed_version_or_reset(
|
||||
base_version + height_to_realized_cap.inner_version(),
|
||||
)?;
|
||||
|
||||
Self::validate_optional_vec_version(&mut self.height_to_realized_profit, base_version)?;
|
||||
Self::validate_optional_vec_version(&mut self.height_to_realized_loss, base_version)?;
|
||||
Self::validate_optional_vec_version(&mut self.height_to_value_created, base_version)?;
|
||||
Self::validate_optional_vec_version(&mut self.height_to_value_destroyed, base_version)?;
|
||||
Self::validate_optional_vec_version(
|
||||
&mut self.height_to_supply_in_profit,
|
||||
base_version,
|
||||
)?;
|
||||
Self::validate_optional_vec_version(&mut self.height_to_supply_in_loss, base_version)?;
|
||||
Self::validate_optional_vec_version(
|
||||
&mut self.height_to_unrealized_profit,
|
||||
base_version,
|
||||
)?;
|
||||
Self::validate_optional_vec_version(&mut self.height_to_unrealized_loss, base_version)?;
|
||||
Self::validate_optional_vec_version(
|
||||
&mut self.dateindex_to_supply_in_profit,
|
||||
base_version,
|
||||
)?;
|
||||
Self::validate_optional_vec_version(
|
||||
&mut self.dateindex_to_supply_in_loss,
|
||||
base_version,
|
||||
)?;
|
||||
Self::validate_optional_vec_version(
|
||||
&mut self.dateindex_to_unrealized_profit,
|
||||
base_version,
|
||||
)?;
|
||||
Self::validate_optional_vec_version(
|
||||
&mut self.dateindex_to_unrealized_loss,
|
||||
base_version,
|
||||
)?;
|
||||
Self::validate_optional_vec_version(&mut self.height_to_min_price_paid, base_version)?;
|
||||
Self::validate_optional_vec_version(&mut self.height_to_max_price_paid, base_version)?;
|
||||
|
||||
if self.height_to_adjusted_value_created.is_some() {
|
||||
Self::validate_optional_vec_version(
|
||||
&mut self.height_to_adjusted_value_created,
|
||||
base_version,
|
||||
)?;
|
||||
Self::validate_optional_vec_version(
|
||||
&mut self.height_to_adjusted_value_destroyed,
|
||||
base_version,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Helper to validate an optional vec's version.
|
||||
fn validate_optional_vec_version<V: StoredVec>(
|
||||
vec: &mut Option<EagerVec<V>>,
|
||||
base_version: Version,
|
||||
) -> Result<()> {
|
||||
if let Some(v) = vec.as_mut() {
|
||||
v.validate_computed_version_or_reset(base_version + v.inner_version())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
//! Common vector structs and logic shared between UTXO and Address cohorts.
|
||||
//!
|
||||
//! This module contains the `Vecs` struct which holds all the computed vectors
|
||||
//! for a single cohort, along with methods for importing, flushing, and computing.
|
||||
//!
|
||||
//! ## Module Organization
|
||||
//!
|
||||
//! The implementation is split across multiple files for maintainability:
|
||||
//! - `vecs.rs`: Struct definition with field documentation
|
||||
//! - `import.rs`: Import, validation, and initialization methods
|
||||
//! - `push.rs`: Per-block push and flush methods
|
||||
//! - `compute.rs`: Post-processing computation methods
|
||||
|
||||
mod compute;
|
||||
mod import;
|
||||
mod push;
|
||||
mod vecs;
|
||||
|
||||
pub use vecs::Vecs;
|
||||
@@ -1,178 +0,0 @@
|
||||
//! Push and flush methods for Vecs.
|
||||
//!
|
||||
//! This module contains methods for:
|
||||
//! - `truncate_push`: Push state values to height-indexed vectors
|
||||
//! - `compute_then_truncate_push_unrealized_states`: Compute and push unrealized states
|
||||
//! - `safe_flush_stateful_vecs`: Safely flush all stateful vectors
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_types::{DateIndex, Dollars, Height, StoredU64};
|
||||
use vecdb::{AnyStoredVec, Exit, GenericStoredVec};
|
||||
|
||||
use crate::{stateful::Flushable, states::CohortState, utils::OptionExt};
|
||||
|
||||
use super::Vecs;
|
||||
|
||||
impl Vecs {
|
||||
pub fn truncate_push(&mut self, height: Height, state: &CohortState) -> Result<()> {
|
||||
self.height_to_supply
|
||||
.truncate_push(height, state.supply.value)?;
|
||||
|
||||
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)?;
|
||||
|
||||
self.height_to_satdays_destroyed
|
||||
.truncate_push(height, state.satdays_destroyed)?;
|
||||
|
||||
if let Some(height_to_realized_cap) = self.height_to_realized_cap.as_mut() {
|
||||
let realized = state.realized.as_ref().unwrap_or_else(|| {
|
||||
dbg!((&state.realized, &state.supply));
|
||||
panic!();
|
||||
});
|
||||
|
||||
height_to_realized_cap.truncate_push(height, realized.cap)?;
|
||||
|
||||
self.height_to_realized_profit
|
||||
.um()
|
||||
.truncate_push(height, realized.profit)?;
|
||||
self.height_to_realized_loss
|
||||
.um()
|
||||
.truncate_push(height, realized.loss)?;
|
||||
self.height_to_value_created
|
||||
.um()
|
||||
.truncate_push(height, realized.value_created)?;
|
||||
self.height_to_value_destroyed
|
||||
.um()
|
||||
.truncate_push(height, realized.value_destroyed)?;
|
||||
|
||||
if self.height_to_adjusted_value_created.is_some() {
|
||||
self.height_to_adjusted_value_created
|
||||
.um()
|
||||
.truncate_push(height, realized.adj_value_created)?;
|
||||
self.height_to_adjusted_value_destroyed
|
||||
.um()
|
||||
.truncate_push(height, realized.adj_value_destroyed)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compute_then_truncate_push_unrealized_states(
|
||||
&mut self,
|
||||
height: Height,
|
||||
height_price: Option<Dollars>,
|
||||
dateindex: Option<DateIndex>,
|
||||
date_price: Option<Option<Dollars>>,
|
||||
state: &CohortState,
|
||||
) -> Result<()> {
|
||||
if let Some(height_price) = height_price {
|
||||
self.height_to_min_price_paid.um().truncate_push(
|
||||
height,
|
||||
state
|
||||
.price_to_amount_first_key_value()
|
||||
.map(|(&dollars, _)| dollars)
|
||||
.unwrap_or(Dollars::NAN),
|
||||
)?;
|
||||
self.height_to_max_price_paid.um().truncate_push(
|
||||
height,
|
||||
state
|
||||
.price_to_amount_last_key_value()
|
||||
.map(|(&dollars, _)| dollars)
|
||||
.unwrap_or(Dollars::NAN),
|
||||
)?;
|
||||
|
||||
let (height_unrealized_state, date_unrealized_state) =
|
||||
state.compute_unrealized_states(height_price, date_price.unwrap());
|
||||
|
||||
self.height_to_supply_in_profit
|
||||
.um()
|
||||
.truncate_push(height, height_unrealized_state.supply_in_profit)?;
|
||||
self.height_to_supply_in_loss
|
||||
.um()
|
||||
.truncate_push(height, height_unrealized_state.supply_in_loss)?;
|
||||
self.height_to_unrealized_profit
|
||||
.um()
|
||||
.truncate_push(height, height_unrealized_state.unrealized_profit)?;
|
||||
self.height_to_unrealized_loss
|
||||
.um()
|
||||
.truncate_push(height, height_unrealized_state.unrealized_loss)?;
|
||||
|
||||
if let Some(date_unrealized_state) = date_unrealized_state {
|
||||
let dateindex = dateindex.unwrap();
|
||||
|
||||
self.dateindex_to_supply_in_profit
|
||||
.um()
|
||||
.truncate_push(dateindex, date_unrealized_state.supply_in_profit)?;
|
||||
self.dateindex_to_supply_in_loss
|
||||
.um()
|
||||
.truncate_push(dateindex, date_unrealized_state.supply_in_loss)?;
|
||||
self.dateindex_to_unrealized_profit
|
||||
.um()
|
||||
.truncate_push(dateindex, date_unrealized_state.unrealized_profit)?;
|
||||
self.dateindex_to_unrealized_loss
|
||||
.um()
|
||||
.truncate_push(dateindex, date_unrealized_state.unrealized_loss)?;
|
||||
}
|
||||
|
||||
// Compute and push price percentiles
|
||||
if let Some(price_percentiles) = self.price_percentiles.as_mut() {
|
||||
let percentile_prices = state.compute_percentile_prices();
|
||||
price_percentiles.truncate_push(height, &percentile_prices)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn safe_flush_stateful_vecs(
|
||||
&mut self,
|
||||
height: Height,
|
||||
exit: &Exit,
|
||||
state: &mut CohortState,
|
||||
) -> Result<()> {
|
||||
self.height_to_supply.safe_write(exit)?;
|
||||
self.height_to_utxo_count.safe_write(exit)?;
|
||||
self.height_to_sent.safe_write(exit)?;
|
||||
self.height_to_satdays_destroyed.safe_write(exit)?;
|
||||
self.height_to_satblocks_destroyed.safe_write(exit)?;
|
||||
|
||||
if let Some(height_to_realized_cap) = self.height_to_realized_cap.as_mut() {
|
||||
height_to_realized_cap.safe_write(exit)?;
|
||||
self.height_to_realized_profit.um().safe_write(exit)?;
|
||||
self.height_to_realized_loss.um().safe_write(exit)?;
|
||||
self.height_to_value_created.um().safe_write(exit)?;
|
||||
self.height_to_value_destroyed.um().safe_write(exit)?;
|
||||
self.height_to_supply_in_profit.um().safe_write(exit)?;
|
||||
self.height_to_supply_in_loss.um().safe_write(exit)?;
|
||||
self.height_to_unrealized_profit.um().safe_write(exit)?;
|
||||
self.height_to_unrealized_loss.um().safe_write(exit)?;
|
||||
self.dateindex_to_supply_in_profit.um().safe_write(exit)?;
|
||||
self.dateindex_to_supply_in_loss.um().safe_write(exit)?;
|
||||
self.dateindex_to_unrealized_profit.um().safe_write(exit)?;
|
||||
self.dateindex_to_unrealized_loss.um().safe_write(exit)?;
|
||||
self.height_to_min_price_paid.um().safe_write(exit)?;
|
||||
self.height_to_max_price_paid.um().safe_write(exit)?;
|
||||
|
||||
if self.height_to_adjusted_value_created.is_some() {
|
||||
self.height_to_adjusted_value_created
|
||||
.um()
|
||||
.safe_write(exit)?;
|
||||
self.height_to_adjusted_value_destroyed
|
||||
.um()
|
||||
.safe_write(exit)?;
|
||||
}
|
||||
|
||||
// Uses Flushable trait - Option<T> impl handles None case
|
||||
self.price_percentiles.safe_write(exit)?;
|
||||
}
|
||||
|
||||
state.commit(height)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,210 +0,0 @@
|
||||
use brk_grouper::Filter;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{DateIndex, Dollars, Height, Sats, StoredF32, StoredF64, StoredU64};
|
||||
use vecdb::{EagerVec, PcoVec};
|
||||
|
||||
use crate::grouped::{
|
||||
ComputedHeightValueVecs, ComputedRatioVecsFromDateIndex, ComputedValueVecsFromDateIndex,
|
||||
ComputedValueVecsFromHeight, ComputedVecsFromDateIndex, ComputedVecsFromHeight,
|
||||
PricePercentiles,
|
||||
};
|
||||
|
||||
/// Common vectors shared between UTXO and Address cohorts.
|
||||
///
|
||||
/// This struct contains all the computed vectors for a single cohort. The fields are
|
||||
/// organized into logical groups matching the initialization order in `forced_import`.
|
||||
///
|
||||
/// ## Field Groups
|
||||
/// - **Supply & UTXO count**: Basic supply metrics (always computed)
|
||||
/// - **Activity**: Sent amounts, satblocks/satdays destroyed
|
||||
/// - **Realized**: Realized cap, profit/loss, value created/destroyed, SOPR
|
||||
/// - **Unrealized**: Unrealized profit/loss, supply in profit/loss
|
||||
/// - **Price**: Min/max price paid, price percentiles
|
||||
/// - **Relative metrics**: Ratios relative to market cap, realized cap, etc.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
#[traversable(skip)]
|
||||
pub filter: Filter,
|
||||
|
||||
// ==================== SUPPLY & UTXO COUNT ====================
|
||||
// Always computed - core supply metrics
|
||||
pub height_to_supply: EagerVec<PcoVec<Height, Sats>>,
|
||||
pub height_to_supply_value: ComputedHeightValueVecs,
|
||||
pub indexes_to_supply: ComputedValueVecsFromDateIndex,
|
||||
pub height_to_utxo_count: EagerVec<PcoVec<Height, StoredU64>>,
|
||||
pub indexes_to_utxo_count: ComputedVecsFromHeight<StoredU64>,
|
||||
pub height_to_supply_half_value: ComputedHeightValueVecs,
|
||||
pub indexes_to_supply_half: ComputedValueVecsFromDateIndex,
|
||||
|
||||
// ==================== ACTIVITY ====================
|
||||
// Always computed - transaction activity metrics
|
||||
pub height_to_sent: EagerVec<PcoVec<Height, Sats>>,
|
||||
pub indexes_to_sent: ComputedValueVecsFromHeight,
|
||||
pub height_to_satblocks_destroyed: EagerVec<PcoVec<Height, Sats>>,
|
||||
pub height_to_satdays_destroyed: EagerVec<PcoVec<Height, Sats>>,
|
||||
pub indexes_to_coinblocks_destroyed: ComputedVecsFromHeight<StoredF64>,
|
||||
pub indexes_to_coindays_destroyed: ComputedVecsFromHeight<StoredF64>,
|
||||
|
||||
// ==================== REALIZED CAP & PRICE ====================
|
||||
// Conditional on compute_dollars
|
||||
pub height_to_realized_cap: Option<EagerVec<PcoVec<Height, Dollars>>>,
|
||||
pub indexes_to_realized_cap: Option<ComputedVecsFromHeight<Dollars>>,
|
||||
pub indexes_to_realized_price: Option<ComputedVecsFromHeight<Dollars>>,
|
||||
pub indexes_to_realized_price_extra: Option<ComputedRatioVecsFromDateIndex>,
|
||||
pub indexes_to_realized_cap_rel_to_own_market_cap: Option<ComputedVecsFromHeight<StoredF32>>,
|
||||
pub indexes_to_realized_cap_30d_delta: Option<ComputedVecsFromDateIndex<Dollars>>,
|
||||
|
||||
// ==================== REALIZED PROFIT & LOSS ====================
|
||||
// Conditional on compute_dollars
|
||||
pub height_to_realized_profit: Option<EagerVec<PcoVec<Height, Dollars>>>,
|
||||
pub indexes_to_realized_profit: Option<ComputedVecsFromHeight<Dollars>>,
|
||||
pub height_to_realized_loss: Option<EagerVec<PcoVec<Height, Dollars>>>,
|
||||
pub indexes_to_realized_loss: Option<ComputedVecsFromHeight<Dollars>>,
|
||||
pub indexes_to_neg_realized_loss: Option<ComputedVecsFromHeight<Dollars>>,
|
||||
pub indexes_to_net_realized_pnl: Option<ComputedVecsFromHeight<Dollars>>,
|
||||
pub indexes_to_realized_value: Option<ComputedVecsFromHeight<Dollars>>,
|
||||
pub indexes_to_realized_profit_rel_to_realized_cap: Option<ComputedVecsFromHeight<StoredF32>>,
|
||||
pub indexes_to_realized_loss_rel_to_realized_cap: Option<ComputedVecsFromHeight<StoredF32>>,
|
||||
pub indexes_to_net_realized_pnl_rel_to_realized_cap: Option<ComputedVecsFromHeight<StoredF32>>,
|
||||
pub height_to_total_realized_pnl: Option<EagerVec<PcoVec<Height, Dollars>>>,
|
||||
pub indexes_to_total_realized_pnl: Option<ComputedVecsFromDateIndex<Dollars>>,
|
||||
pub dateindex_to_realized_profit_to_loss_ratio: Option<EagerVec<PcoVec<DateIndex, StoredF64>>>,
|
||||
|
||||
// ==================== VALUE CREATED & DESTROYED ====================
|
||||
// Conditional on compute_dollars
|
||||
pub height_to_value_created: Option<EagerVec<PcoVec<Height, Dollars>>>,
|
||||
pub indexes_to_value_created: Option<ComputedVecsFromHeight<Dollars>>,
|
||||
pub height_to_value_destroyed: Option<EagerVec<PcoVec<Height, Dollars>>>,
|
||||
pub indexes_to_value_destroyed: Option<ComputedVecsFromHeight<Dollars>>,
|
||||
pub height_to_adjusted_value_created: Option<EagerVec<PcoVec<Height, Dollars>>>,
|
||||
pub indexes_to_adjusted_value_created: Option<ComputedVecsFromHeight<Dollars>>,
|
||||
pub height_to_adjusted_value_destroyed: Option<EagerVec<PcoVec<Height, Dollars>>>,
|
||||
pub indexes_to_adjusted_value_destroyed: Option<ComputedVecsFromHeight<Dollars>>,
|
||||
|
||||
// ==================== SOPR ====================
|
||||
// Spent Output Profit Ratio - conditional on compute_dollars
|
||||
pub dateindex_to_sopr: Option<EagerVec<PcoVec<DateIndex, StoredF64>>>,
|
||||
pub dateindex_to_sopr_7d_ema: Option<EagerVec<PcoVec<DateIndex, StoredF64>>>,
|
||||
pub dateindex_to_sopr_30d_ema: Option<EagerVec<PcoVec<DateIndex, StoredF64>>>,
|
||||
pub dateindex_to_adjusted_sopr: Option<EagerVec<PcoVec<DateIndex, StoredF64>>>,
|
||||
pub dateindex_to_adjusted_sopr_7d_ema: Option<EagerVec<PcoVec<DateIndex, StoredF64>>>,
|
||||
pub dateindex_to_adjusted_sopr_30d_ema: Option<EagerVec<PcoVec<DateIndex, StoredF64>>>,
|
||||
|
||||
// ==================== SELL SIDE RISK ====================
|
||||
// Conditional on compute_dollars
|
||||
pub dateindex_to_sell_side_risk_ratio: Option<EagerVec<PcoVec<DateIndex, StoredF32>>>,
|
||||
pub dateindex_to_sell_side_risk_ratio_7d_ema: Option<EagerVec<PcoVec<DateIndex, StoredF32>>>,
|
||||
pub dateindex_to_sell_side_risk_ratio_30d_ema: Option<EagerVec<PcoVec<DateIndex, StoredF32>>>,
|
||||
|
||||
// ==================== SUPPLY IN PROFIT/LOSS ====================
|
||||
// Conditional on compute_dollars
|
||||
pub height_to_supply_in_profit: Option<EagerVec<PcoVec<Height, Sats>>>,
|
||||
pub indexes_to_supply_in_profit: Option<ComputedValueVecsFromDateIndex>,
|
||||
pub height_to_supply_in_loss: Option<EagerVec<PcoVec<Height, Sats>>>,
|
||||
pub indexes_to_supply_in_loss: Option<ComputedValueVecsFromDateIndex>,
|
||||
pub dateindex_to_supply_in_profit: Option<EagerVec<PcoVec<DateIndex, Sats>>>,
|
||||
pub dateindex_to_supply_in_loss: Option<EagerVec<PcoVec<DateIndex, Sats>>>,
|
||||
pub height_to_supply_in_profit_value: Option<ComputedHeightValueVecs>,
|
||||
pub height_to_supply_in_loss_value: Option<ComputedHeightValueVecs>,
|
||||
|
||||
// ==================== UNREALIZED PROFIT & LOSS ====================
|
||||
// Conditional on compute_dollars
|
||||
pub height_to_unrealized_profit: Option<EagerVec<PcoVec<Height, Dollars>>>,
|
||||
pub indexes_to_unrealized_profit: Option<ComputedVecsFromDateIndex<Dollars>>,
|
||||
pub height_to_unrealized_loss: Option<EagerVec<PcoVec<Height, Dollars>>>,
|
||||
pub indexes_to_unrealized_loss: Option<ComputedVecsFromDateIndex<Dollars>>,
|
||||
pub dateindex_to_unrealized_profit: Option<EagerVec<PcoVec<DateIndex, Dollars>>>,
|
||||
pub dateindex_to_unrealized_loss: Option<EagerVec<PcoVec<DateIndex, Dollars>>>,
|
||||
pub height_to_neg_unrealized_loss: Option<EagerVec<PcoVec<Height, Dollars>>>,
|
||||
pub indexes_to_neg_unrealized_loss: Option<ComputedVecsFromDateIndex<Dollars>>,
|
||||
pub height_to_net_unrealized_pnl: Option<EagerVec<PcoVec<Height, Dollars>>>,
|
||||
pub indexes_to_net_unrealized_pnl: Option<ComputedVecsFromDateIndex<Dollars>>,
|
||||
pub height_to_total_unrealized_pnl: Option<EagerVec<PcoVec<Height, Dollars>>>,
|
||||
pub indexes_to_total_unrealized_pnl: Option<ComputedVecsFromDateIndex<Dollars>>,
|
||||
|
||||
// ==================== PRICE PAID ====================
|
||||
// Conditional on compute_dollars
|
||||
pub height_to_min_price_paid: Option<EagerVec<PcoVec<Height, Dollars>>>,
|
||||
pub indexes_to_min_price_paid: Option<ComputedVecsFromHeight<Dollars>>,
|
||||
pub height_to_max_price_paid: Option<EagerVec<PcoVec<Height, Dollars>>>,
|
||||
pub indexes_to_max_price_paid: Option<ComputedVecsFromHeight<Dollars>>,
|
||||
pub price_percentiles: Option<PricePercentiles>,
|
||||
|
||||
// ==================== RELATIVE METRICS: UNREALIZED vs MARKET CAP ====================
|
||||
// Conditional on compute_dollars
|
||||
pub height_to_unrealized_profit_rel_to_market_cap: Option<EagerVec<PcoVec<Height, StoredF32>>>,
|
||||
pub height_to_unrealized_loss_rel_to_market_cap: Option<EagerVec<PcoVec<Height, StoredF32>>>,
|
||||
pub height_to_neg_unrealized_loss_rel_to_market_cap:
|
||||
Option<EagerVec<PcoVec<Height, StoredF32>>>,
|
||||
pub height_to_net_unrealized_pnl_rel_to_market_cap: Option<EagerVec<PcoVec<Height, StoredF32>>>,
|
||||
pub indexes_to_unrealized_profit_rel_to_market_cap:
|
||||
Option<ComputedVecsFromDateIndex<StoredF32>>,
|
||||
pub indexes_to_unrealized_loss_rel_to_market_cap: Option<ComputedVecsFromDateIndex<StoredF32>>,
|
||||
pub indexes_to_neg_unrealized_loss_rel_to_market_cap:
|
||||
Option<ComputedVecsFromDateIndex<StoredF32>>,
|
||||
pub indexes_to_net_unrealized_pnl_rel_to_market_cap:
|
||||
Option<ComputedVecsFromDateIndex<StoredF32>>,
|
||||
|
||||
// ==================== RELATIVE METRICS: UNREALIZED vs OWN MARKET CAP ====================
|
||||
// Conditional on compute_dollars && extended && compute_rel_to_all
|
||||
pub height_to_unrealized_profit_rel_to_own_market_cap:
|
||||
Option<EagerVec<PcoVec<Height, StoredF32>>>,
|
||||
pub height_to_unrealized_loss_rel_to_own_market_cap:
|
||||
Option<EagerVec<PcoVec<Height, StoredF32>>>,
|
||||
pub height_to_neg_unrealized_loss_rel_to_own_market_cap:
|
||||
Option<EagerVec<PcoVec<Height, StoredF32>>>,
|
||||
pub height_to_net_unrealized_pnl_rel_to_own_market_cap:
|
||||
Option<EagerVec<PcoVec<Height, StoredF32>>>,
|
||||
pub indexes_to_unrealized_profit_rel_to_own_market_cap:
|
||||
Option<ComputedVecsFromDateIndex<StoredF32>>,
|
||||
pub indexes_to_unrealized_loss_rel_to_own_market_cap:
|
||||
Option<ComputedVecsFromDateIndex<StoredF32>>,
|
||||
pub indexes_to_neg_unrealized_loss_rel_to_own_market_cap:
|
||||
Option<ComputedVecsFromDateIndex<StoredF32>>,
|
||||
pub indexes_to_net_unrealized_pnl_rel_to_own_market_cap:
|
||||
Option<ComputedVecsFromDateIndex<StoredF32>>,
|
||||
|
||||
// ==================== RELATIVE METRICS: UNREALIZED vs OWN TOTAL UNREALIZED ====================
|
||||
// Conditional on compute_dollars && extended
|
||||
pub height_to_unrealized_profit_rel_to_own_total_unrealized_pnl:
|
||||
Option<EagerVec<PcoVec<Height, StoredF32>>>,
|
||||
pub height_to_unrealized_loss_rel_to_own_total_unrealized_pnl:
|
||||
Option<EagerVec<PcoVec<Height, StoredF32>>>,
|
||||
pub height_to_neg_unrealized_loss_rel_to_own_total_unrealized_pnl:
|
||||
Option<EagerVec<PcoVec<Height, StoredF32>>>,
|
||||
pub height_to_net_unrealized_pnl_rel_to_own_total_unrealized_pnl:
|
||||
Option<EagerVec<PcoVec<Height, StoredF32>>>,
|
||||
pub indexes_to_unrealized_profit_rel_to_own_total_unrealized_pnl:
|
||||
Option<ComputedVecsFromDateIndex<StoredF32>>,
|
||||
pub indexes_to_unrealized_loss_rel_to_own_total_unrealized_pnl:
|
||||
Option<ComputedVecsFromDateIndex<StoredF32>>,
|
||||
pub indexes_to_neg_unrealized_loss_rel_to_own_total_unrealized_pnl:
|
||||
Option<ComputedVecsFromDateIndex<StoredF32>>,
|
||||
pub indexes_to_net_unrealized_pnl_rel_to_own_total_unrealized_pnl:
|
||||
Option<ComputedVecsFromDateIndex<StoredF32>>,
|
||||
|
||||
// ==================== RELATIVE METRICS: SUPPLY vs CIRCULATING/OWN ====================
|
||||
// Conditional on compute_dollars
|
||||
pub indexes_to_supply_rel_to_circulating_supply: Option<ComputedVecsFromHeight<StoredF64>>,
|
||||
pub height_to_supply_in_profit_rel_to_own_supply: Option<EagerVec<PcoVec<Height, StoredF64>>>,
|
||||
pub height_to_supply_in_loss_rel_to_own_supply: Option<EagerVec<PcoVec<Height, StoredF64>>>,
|
||||
pub indexes_to_supply_in_profit_rel_to_own_supply: Option<ComputedVecsFromDateIndex<StoredF64>>,
|
||||
pub indexes_to_supply_in_loss_rel_to_own_supply: Option<ComputedVecsFromDateIndex<StoredF64>>,
|
||||
pub height_to_supply_in_profit_rel_to_circulating_supply:
|
||||
Option<EagerVec<PcoVec<Height, StoredF64>>>,
|
||||
pub height_to_supply_in_loss_rel_to_circulating_supply:
|
||||
Option<EagerVec<PcoVec<Height, StoredF64>>>,
|
||||
pub indexes_to_supply_in_profit_rel_to_circulating_supply:
|
||||
Option<ComputedVecsFromDateIndex<StoredF64>>,
|
||||
pub indexes_to_supply_in_loss_rel_to_circulating_supply:
|
||||
Option<ComputedVecsFromDateIndex<StoredF64>>,
|
||||
|
||||
// ==================== NET REALIZED PNL DELTAS ====================
|
||||
// Conditional on compute_dollars
|
||||
pub indexes_to_net_realized_pnl_cumulative_30d_delta:
|
||||
Option<ComputedVecsFromDateIndex<Dollars>>,
|
||||
pub indexes_to_net_realized_pnl_cumulative_30d_delta_rel_to_realized_cap:
|
||||
Option<ComputedVecsFromDateIndex<StoredF32>>,
|
||||
pub indexes_to_net_realized_pnl_cumulative_30d_delta_rel_to_market_cap:
|
||||
Option<ComputedVecsFromDateIndex<StoredF32>>,
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
//! Traits for consistent state flushing and importing.
|
||||
//!
|
||||
//! These traits ensure all stateful components follow the same patterns
|
||||
//! for checkpoint/resume operations, preventing bugs where new fields
|
||||
//! are forgotten during flush operations.
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_types::Height;
|
||||
use vecdb::Exit;
|
||||
|
||||
/// Trait for components that can be flushed to disk.
|
||||
///
|
||||
/// This is for simple flush operations that don't require height tracking.
|
||||
pub trait Flushable {
|
||||
/// Safely flush data to disk.
|
||||
fn safe_flush(&mut self, exit: &Exit) -> Result<()>;
|
||||
|
||||
/// Write to mmap without fsync. Data visible to readers immediately but not durable.
|
||||
fn safe_write(&mut self, exit: &Exit) -> Result<()>;
|
||||
}
|
||||
|
||||
/// Trait for stateful components that track data indexed by height.
|
||||
///
|
||||
/// This ensures consistent patterns for:
|
||||
/// - Flushing state at checkpoints
|
||||
/// - Importing state when resuming from a checkpoint
|
||||
/// - Resetting state when starting from scratch
|
||||
pub trait HeightFlushable {
|
||||
/// Flush state to disk at the given height checkpoint.
|
||||
fn flush_at_height(&mut self, height: Height, exit: &Exit) -> Result<()>;
|
||||
|
||||
/// Import state from the most recent checkpoint at or before the given height.
|
||||
/// Returns the actual height that was imported.
|
||||
fn import_at_or_before(&mut self, height: Height) -> Result<Height>;
|
||||
|
||||
/// Reset state for starting from scratch.
|
||||
fn reset(&mut self) -> Result<()>;
|
||||
}
|
||||
|
||||
/// Blanket implementation for Option<T> where T: Flushable
|
||||
impl<T: Flushable> Flushable for Option<T> {
|
||||
fn safe_flush(&mut self, exit: &Exit) -> Result<()> {
|
||||
if let Some(inner) = self.as_mut() {
|
||||
inner.safe_flush(exit)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn safe_write(&mut self, exit: &Exit) -> Result<()> {
|
||||
if let Some(inner) = self.as_mut() {
|
||||
inner.safe_write(exit)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Blanket implementation for Option<T> where T: HeightFlushable
|
||||
impl<T: HeightFlushable> HeightFlushable for Option<T> {
|
||||
fn flush_at_height(&mut self, height: Height, exit: &Exit) -> Result<()> {
|
||||
if let Some(inner) = self.as_mut() {
|
||||
inner.flush_at_height(height, exit)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn import_at_or_before(&mut self, height: Height) -> Result<Height> {
|
||||
if let Some(inner) = self.as_mut() {
|
||||
inner.import_at_or_before(height)
|
||||
} else {
|
||||
Ok(height)
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> Result<()> {
|
||||
if let Some(inner) = self.as_mut() {
|
||||
inner.reset()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,53 +0,0 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use vecdb::{BytesVec, BytesVecValue, PcoVec, PcoVecValue, VecIndex};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RangeMap<I, T>(BTreeMap<I, T>);
|
||||
|
||||
impl<I, T> RangeMap<I, T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: VecIndex,
|
||||
{
|
||||
pub fn get(&self, key: I) -> Option<&T> {
|
||||
self.0.range(..=key).next_back().map(|(&min, value)| {
|
||||
if min > key {
|
||||
unreachable!()
|
||||
}
|
||||
value
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> From<&BytesVec<I, T>> for RangeMap<T, I>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: VecIndex + BytesVecValue,
|
||||
{
|
||||
#[inline]
|
||||
fn from(vec: &BytesVec<I, T>) -> Self {
|
||||
Self(
|
||||
vec.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, v)| (v, I::from(i)))
|
||||
.collect::<BTreeMap<_, _>>(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> From<&PcoVec<I, T>> for RangeMap<T, I>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: VecIndex + PcoVecValue,
|
||||
{
|
||||
#[inline]
|
||||
fn from(vec: &PcoVec<I, T>) -> Self {
|
||||
Self(
|
||||
vec.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, v)| (v, I::from(i)))
|
||||
.collect::<BTreeMap<_, _>>(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
use brk_grouper::{ByAddressType, ByAnyAddress};
|
||||
use brk_indexer::Indexer;
|
||||
use brk_types::{OutputType, StoredU64, TxIndex};
|
||||
use vecdb::{BoxedVecIterator, GenericStoredVec, Reader, VecIndex};
|
||||
|
||||
use super::Vecs;
|
||||
|
||||
pub struct IndexerReaders {
|
||||
pub txinindex_to_outpoint: Reader,
|
||||
pub txindex_to_first_txoutindex: Reader,
|
||||
pub txoutindex_to_value: Reader,
|
||||
pub txoutindex_to_outputtype: Reader,
|
||||
pub txoutindex_to_typeindex: Reader,
|
||||
}
|
||||
|
||||
impl IndexerReaders {
|
||||
pub fn new(indexer: &Indexer) -> Self {
|
||||
Self {
|
||||
txinindex_to_outpoint: indexer.vecs.txinindex_to_outpoint.create_reader(),
|
||||
txindex_to_first_txoutindex: indexer.vecs.txindex_to_first_txoutindex.create_reader(),
|
||||
txoutindex_to_value: indexer.vecs.txoutindex_to_value.create_reader(),
|
||||
txoutindex_to_outputtype: indexer.vecs.txoutindex_to_outputtype.create_reader(),
|
||||
txoutindex_to_typeindex: indexer.vecs.txoutindex_to_typeindex.create_reader(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VecsReaders {
|
||||
pub addresstypeindex_to_anyaddressindex: ByAddressType<Reader>,
|
||||
pub anyaddressindex_to_anyaddressdata: ByAnyAddress<Reader>,
|
||||
}
|
||||
|
||||
impl VecsReaders {
|
||||
pub fn new(vecs: &Vecs) -> Self {
|
||||
Self {
|
||||
addresstypeindex_to_anyaddressindex: ByAddressType {
|
||||
p2pk33: vecs.any_address_indexes.p2pk33.create_reader(),
|
||||
p2pk65: vecs.any_address_indexes.p2pk65.create_reader(),
|
||||
p2pkh: vecs.any_address_indexes.p2pkh.create_reader(),
|
||||
p2sh: vecs.any_address_indexes.p2sh.create_reader(),
|
||||
p2tr: vecs.any_address_indexes.p2tr.create_reader(),
|
||||
p2wpkh: vecs.any_address_indexes.p2wpkh.create_reader(),
|
||||
p2wsh: vecs.any_address_indexes.p2wsh.create_reader(),
|
||||
p2a: vecs.any_address_indexes.p2a.create_reader(),
|
||||
},
|
||||
anyaddressindex_to_anyaddressdata: ByAnyAddress {
|
||||
loaded: vecs.addresses_data.loaded.create_reader(),
|
||||
empty: vecs.addresses_data.empty.create_reader(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_anyaddressindex_reader(&self, address_type: OutputType) -> &Reader {
|
||||
self.addresstypeindex_to_anyaddressindex
|
||||
.get_unwrap(address_type)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_txoutindex_to_txindex<'a>(
|
||||
block_first_txindex: TxIndex,
|
||||
block_tx_count: u64,
|
||||
txindex_to_output_count: &mut BoxedVecIterator<'a, TxIndex, StoredU64>,
|
||||
) -> Vec<TxIndex> {
|
||||
let block_first_txindex = block_first_txindex.to_usize();
|
||||
|
||||
let counts: Vec<_> = (0..block_tx_count as usize)
|
||||
.map(|tx_offset| {
|
||||
let txindex = TxIndex::from(block_first_txindex + tx_offset);
|
||||
u64::from(txindex_to_output_count.get_unwrap(txindex))
|
||||
})
|
||||
.collect();
|
||||
|
||||
let total: u64 = counts.iter().sum();
|
||||
let mut vec = Vec::with_capacity(total as usize);
|
||||
|
||||
for (tx_offset, &output_count) in counts.iter().enumerate() {
|
||||
let txindex = TxIndex::from(block_first_txindex + tx_offset);
|
||||
for _ in 0..output_count {
|
||||
vec.push(txindex);
|
||||
}
|
||||
}
|
||||
|
||||
vec
|
||||
}
|
||||
|
||||
pub fn build_txinindex_to_txindex<'a>(
|
||||
block_first_txindex: TxIndex,
|
||||
block_tx_count: u64,
|
||||
txindex_to_input_count: &mut BoxedVecIterator<'a, TxIndex, StoredU64>,
|
||||
) -> Vec<TxIndex> {
|
||||
let block_first_txindex = block_first_txindex.to_usize();
|
||||
|
||||
let counts: Vec<_> = (0..block_tx_count as usize)
|
||||
.map(|tx_offset| {
|
||||
let txindex = TxIndex::from(block_first_txindex + tx_offset);
|
||||
u64::from(txindex_to_input_count.get_unwrap(txindex))
|
||||
})
|
||||
.collect();
|
||||
|
||||
let total: u64 = counts.iter().sum();
|
||||
let mut vec = Vec::with_capacity(total as usize);
|
||||
|
||||
for (tx_offset, &input_count) in counts.iter().enumerate() {
|
||||
let txindex = TxIndex::from(block_first_txindex + tx_offset);
|
||||
for _ in 0..input_count {
|
||||
vec.push(txindex);
|
||||
}
|
||||
}
|
||||
|
||||
vec
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
use brk_error::Result;
|
||||
use brk_types::{Bitcoin, DateIndex, Dollars, Height, Version};
|
||||
use vecdb::{Exit, IterableVec};
|
||||
|
||||
use crate::{Indexes, indexes, price};
|
||||
|
||||
pub trait DynCohortVecs: Send + Sync {
|
||||
fn min_height_vecs_len(&self) -> usize;
|
||||
fn reset_state_starting_height(&mut self);
|
||||
|
||||
fn import_state(&mut self, starting_height: Height) -> Result<Height>;
|
||||
|
||||
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()>;
|
||||
|
||||
fn truncate_push(&mut self, height: Height) -> Result<()>;
|
||||
|
||||
fn compute_then_truncate_push_unrealized_states(
|
||||
&mut self,
|
||||
height: Height,
|
||||
height_price: Option<Dollars>,
|
||||
dateindex: Option<DateIndex>,
|
||||
date_price: Option<Option<Dollars>>,
|
||||
) -> Result<()>;
|
||||
|
||||
fn safe_flush_stateful_vecs(&mut self, height: Height, exit: &Exit) -> Result<()>;
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compute_rest_part1(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()>;
|
||||
}
|
||||
|
||||
pub trait CohortVecs: DynCohortVecs {
|
||||
fn compute_from_stateful(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()>;
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compute_rest_part2(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
height_to_supply: &impl IterableVec<Height, Bitcoin>,
|
||||
dateindex_to_supply: &impl IterableVec<DateIndex, Bitcoin>,
|
||||
height_to_market_cap: Option<&impl IterableVec<Height, Dollars>>,
|
||||
dateindex_to_market_cap: Option<&impl IterableVec<DateIndex, Dollars>>,
|
||||
height_to_realized_cap: Option<&impl IterableVec<Height, Dollars>>,
|
||||
dateindex_to_realized_cap: Option<&impl IterableVec<DateIndex, Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>;
|
||||
}
|
||||
@@ -1,217 +0,0 @@
|
||||
use brk_error::Result;
|
||||
use brk_grouper::{ByAddressType, Filtered};
|
||||
use brk_types::{
|
||||
CheckedSub, Dollars, EmptyAddressData, Height, LoadedAddressData, Sats, Timestamp, TypeIndex,
|
||||
};
|
||||
use vecdb::VecIndex;
|
||||
|
||||
use crate::utils::OptionExt;
|
||||
|
||||
use super::{
|
||||
address_cohorts,
|
||||
addresstype::{AddressTypeToTypeIndexMap, AddressTypeToVec, HeightToAddressTypeToVec},
|
||||
withaddressdatasource::WithAddressDataSource,
|
||||
};
|
||||
|
||||
impl AddressTypeToVec<(TypeIndex, Sats)> {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn process_received(
|
||||
self,
|
||||
vecs: &mut address_cohorts::Vecs,
|
||||
addresstype_to_typeindex_to_loadedaddressdata: &mut AddressTypeToTypeIndexMap<
|
||||
WithAddressDataSource<LoadedAddressData>,
|
||||
>,
|
||||
addresstype_to_typeindex_to_emptyaddressdata: &mut AddressTypeToTypeIndexMap<
|
||||
WithAddressDataSource<EmptyAddressData>,
|
||||
>,
|
||||
price: Option<Dollars>,
|
||||
addresstype_to_addr_count: &mut ByAddressType<u64>,
|
||||
addresstype_to_empty_addr_count: &mut ByAddressType<u64>,
|
||||
stored_or_new_addresstype_to_typeindex_to_addressdatawithsource: &mut AddressTypeToTypeIndexMap<
|
||||
WithAddressDataSource<LoadedAddressData>,
|
||||
>,
|
||||
) {
|
||||
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;
|
||||
|
||||
let addressdata_withsource = addresstype_to_typeindex_to_loadedaddressdata
|
||||
.get_mut(_type)
|
||||
.unwrap()
|
||||
.entry(type_index)
|
||||
.or_insert_with(|| {
|
||||
addresstype_to_typeindex_to_emptyaddressdata
|
||||
.get_mut(_type)
|
||||
.unwrap()
|
||||
.remove(&type_index)
|
||||
.map(|ad| {
|
||||
from_any_empty = true;
|
||||
ad.into()
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
let addressdata =
|
||||
stored_or_new_addresstype_to_typeindex_to_addressdatawithsource
|
||||
.remove_for_type(_type, &type_index);
|
||||
is_new = addressdata.is_new();
|
||||
from_any_empty = addressdata.is_from_emptyaddressdata();
|
||||
addressdata
|
||||
})
|
||||
});
|
||||
|
||||
if is_new || from_any_empty {
|
||||
(*addresstype_to_addr_count.get_mut(_type).unwrap()) += 1;
|
||||
if from_any_empty {
|
||||
(*addresstype_to_empty_addr_count.get_mut(_type).unwrap()) -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
let addressdata = addressdata_withsource.deref_mut();
|
||||
|
||||
let prev_amount = addressdata.balance();
|
||||
|
||||
let amount = prev_amount + value;
|
||||
|
||||
let filters_differ = vecs.amount_range.get(amount).filter()
|
||||
!= vecs.amount_range.get(prev_amount).filter();
|
||||
|
||||
if is_new || from_any_empty || filters_differ {
|
||||
if !is_new && !from_any_empty {
|
||||
vecs.amount_range
|
||||
.get_mut(prev_amount)
|
||||
.state
|
||||
.um()
|
||||
.subtract(addressdata);
|
||||
}
|
||||
|
||||
addressdata.receive(value, price);
|
||||
|
||||
vecs.amount_range
|
||||
.get_mut(amount)
|
||||
.state
|
||||
.um()
|
||||
.add(addressdata);
|
||||
} else {
|
||||
vecs.amount_range
|
||||
.get_mut(amount)
|
||||
.state
|
||||
.um()
|
||||
.receive(addressdata, value, price);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl HeightToAddressTypeToVec<(TypeIndex, Sats)> {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn process_sent(
|
||||
self,
|
||||
vecs: &mut address_cohorts::Vecs,
|
||||
addresstype_to_typeindex_to_loadedaddressdata: &mut AddressTypeToTypeIndexMap<
|
||||
WithAddressDataSource<LoadedAddressData>,
|
||||
>,
|
||||
addresstype_to_typeindex_to_emptyaddressdata: &mut AddressTypeToTypeIndexMap<
|
||||
WithAddressDataSource<EmptyAddressData>,
|
||||
>,
|
||||
price: Option<Dollars>,
|
||||
addresstype_to_addr_count: &mut ByAddressType<u64>,
|
||||
addresstype_to_empty_addr_count: &mut ByAddressType<u64>,
|
||||
height_to_price_close_vec: Option<&Vec<brk_types::Close<Dollars>>>,
|
||||
height_to_timestamp_fixed_vec: &[Timestamp],
|
||||
height: Height,
|
||||
timestamp: Timestamp,
|
||||
stored_or_new_addresstype_to_typeindex_to_addressdatawithsource: &mut AddressTypeToTypeIndexMap<
|
||||
WithAddressDataSource<LoadedAddressData>,
|
||||
>,
|
||||
) -> Result<()> {
|
||||
self.0.into_iter().try_for_each(|(prev_height, v)| {
|
||||
let prev_price = height_to_price_close_vec
|
||||
.as_ref()
|
||||
.map(|v| **v.get(prev_height.to_usize()).unwrap());
|
||||
|
||||
let prev_timestamp = *height_to_timestamp_fixed_vec
|
||||
.get(prev_height.to_usize())
|
||||
.unwrap();
|
||||
|
||||
let blocks_old = height.to_usize() - prev_height.to_usize();
|
||||
|
||||
let days_old = timestamp.difference_in_days_between_float(prev_timestamp);
|
||||
|
||||
let older_than_hour = timestamp
|
||||
.checked_sub(prev_timestamp)
|
||||
.unwrap()
|
||||
.is_more_than_hour();
|
||||
|
||||
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);
|
||||
|
||||
let addressdata_withsource = typeindex_to_loadedaddressdata
|
||||
.entry(type_index)
|
||||
.or_insert_with(|| {
|
||||
stored_or_new_addresstype_to_typeindex_to_addressdatawithsource
|
||||
.remove_for_type(_type, &type_index)
|
||||
});
|
||||
|
||||
let addressdata = addressdata_withsource.deref_mut();
|
||||
|
||||
let prev_amount = addressdata.balance();
|
||||
|
||||
let amount = prev_amount.checked_sub(value).unwrap();
|
||||
|
||||
let will_be_empty = addressdata.has_1_utxos();
|
||||
|
||||
let filters_differ = vecs.amount_range.get(amount).filter()
|
||||
!= vecs.amount_range.get(prev_amount).filter();
|
||||
|
||||
if will_be_empty || filters_differ {
|
||||
vecs.amount_range
|
||||
.get_mut(prev_amount)
|
||||
.state
|
||||
.um()
|
||||
.subtract(addressdata);
|
||||
|
||||
addressdata.send(value, prev_price)?;
|
||||
|
||||
if will_be_empty {
|
||||
if amount.is_not_zero() {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
(*addresstype_to_addr_count.get_mut(_type).unwrap()) -= 1;
|
||||
(*addresstype_to_empty_addr_count.get_mut(_type).unwrap()) += 1;
|
||||
|
||||
let addressdata =
|
||||
typeindex_to_loadedaddressdata.remove(&type_index).unwrap();
|
||||
|
||||
addresstype_to_typeindex_to_emptyaddressdata
|
||||
.get_mut(_type)
|
||||
.unwrap()
|
||||
.insert(type_index, addressdata.into());
|
||||
} else {
|
||||
vecs.amount_range
|
||||
.get_mut(amount)
|
||||
.state
|
||||
.um()
|
||||
.add(addressdata);
|
||||
}
|
||||
} else {
|
||||
vecs.amount_range.get_mut(amount).state.um().send(
|
||||
addressdata,
|
||||
value,
|
||||
price,
|
||||
prev_price,
|
||||
blocks_old,
|
||||
days_old,
|
||||
older_than_hour,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
use std::{ops::Deref, path::Path};
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_grouper::{CohortContext, Filter, Filtered, StateLevel};
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, DateIndex, Dollars, Height, Sats, Version};
|
||||
use vecdb::{Database, Exit, IterableVec};
|
||||
|
||||
use crate::{
|
||||
Indexes, PriceToAmount, UTXOCohortState,
|
||||
grouped::{PERCENTILES, PERCENTILES_LEN},
|
||||
indexes, price,
|
||||
stateful::{
|
||||
common,
|
||||
r#trait::{CohortVecs, DynCohortVecs},
|
||||
},
|
||||
utils::OptionExt,
|
||||
};
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
state_starting_height: Option<Height>,
|
||||
|
||||
#[traversable(skip)]
|
||||
pub state: Option<UTXOCohortState>,
|
||||
|
||||
/// For aggregate cohorts (all, sth, lth) that only need price_to_amount for percentiles
|
||||
#[traversable(skip)]
|
||||
pub price_to_amount: Option<PriceToAmount>,
|
||||
|
||||
#[traversable(flatten)]
|
||||
pub inner: common::Vecs,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
db: &Database,
|
||||
filter: Filter,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
states_path: &Path,
|
||||
state_level: StateLevel,
|
||||
) -> Result<Self> {
|
||||
let compute_dollars = price.is_some();
|
||||
|
||||
let full_name = filter.to_full_name(CohortContext::Utxo);
|
||||
|
||||
Ok(Self {
|
||||
state_starting_height: None,
|
||||
|
||||
state: if state_level.is_full() {
|
||||
Some(UTXOCohortState::new(
|
||||
states_path,
|
||||
&full_name,
|
||||
compute_dollars,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
|
||||
price_to_amount: if state_level.is_price_only() && compute_dollars {
|
||||
Some(PriceToAmount::create(states_path, &full_name))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
|
||||
inner: common::Vecs::forced_import(
|
||||
db,
|
||||
filter,
|
||||
CohortContext::Utxo,
|
||||
version,
|
||||
indexes,
|
||||
price,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl DynCohortVecs for Vecs {
|
||||
fn min_height_vecs_len(&self) -> usize {
|
||||
self.inner.min_height_vecs_len()
|
||||
}
|
||||
|
||||
fn reset_state_starting_height(&mut self) {
|
||||
self.state_starting_height = Some(Height::ZERO);
|
||||
}
|
||||
|
||||
fn import_state(&mut self, starting_height: Height) -> Result<Height> {
|
||||
let starting_height = self
|
||||
.inner
|
||||
.import_state(starting_height, self.state.um())?;
|
||||
|
||||
self.state_starting_height = Some(starting_height);
|
||||
|
||||
Ok(starting_height)
|
||||
}
|
||||
|
||||
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> {
|
||||
self.inner.validate_computed_versions(base_version)
|
||||
}
|
||||
|
||||
fn truncate_push(&mut self, height: Height) -> Result<()> {
|
||||
if self.state_starting_height.unwrap() > height {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.inner
|
||||
.truncate_push(height, self.state.u())
|
||||
}
|
||||
|
||||
fn compute_then_truncate_push_unrealized_states(
|
||||
&mut self,
|
||||
height: Height,
|
||||
height_price: Option<Dollars>,
|
||||
dateindex: Option<DateIndex>,
|
||||
date_price: Option<Option<Dollars>>,
|
||||
) -> Result<()> {
|
||||
self.inner.compute_then_truncate_push_unrealized_states(
|
||||
height,
|
||||
height_price,
|
||||
dateindex,
|
||||
date_price,
|
||||
self.state.um(),
|
||||
)
|
||||
}
|
||||
|
||||
fn safe_flush_stateful_vecs(&mut self, height: Height, exit: &Exit) -> Result<()> {
|
||||
self.inner
|
||||
.safe_flush_stateful_vecs(height, exit, self.state.um())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compute_rest_part1(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.inner
|
||||
.compute_rest_part1(indexes, price, starting_indexes, exit)
|
||||
}
|
||||
}
|
||||
|
||||
impl CohortVecs for Vecs {
|
||||
fn compute_from_stateful(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.inner.compute_from_stateful(
|
||||
starting_indexes,
|
||||
&others.iter().map(|v| &v.inner).collect::<Vec<_>>(),
|
||||
exit,
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compute_rest_part2(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
height_to_supply: &impl IterableVec<Height, Bitcoin>,
|
||||
dateindex_to_supply: &impl IterableVec<DateIndex, Bitcoin>,
|
||||
height_to_market_cap: Option<&impl IterableVec<Height, Dollars>>,
|
||||
dateindex_to_market_cap: Option<&impl IterableVec<DateIndex, Dollars>>,
|
||||
height_to_realized_cap: Option<&impl IterableVec<Height, Dollars>>,
|
||||
dateindex_to_realized_cap: Option<&impl IterableVec<DateIndex, Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.inner.compute_rest_part2(
|
||||
indexes,
|
||||
price,
|
||||
starting_indexes,
|
||||
height_to_supply,
|
||||
dateindex_to_supply,
|
||||
height_to_market_cap,
|
||||
dateindex_to_market_cap,
|
||||
height_to_realized_cap,
|
||||
dateindex_to_realized_cap,
|
||||
exit,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
/// Compute percentile prices for aggregate cohorts that have standalone price_to_amount.
|
||||
/// Returns NaN array if price_to_amount is None or empty.
|
||||
pub fn compute_percentile_prices_from_standalone(
|
||||
&self,
|
||||
supply: Sats,
|
||||
) -> [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() || supply == Sats::ZERO {
|
||||
return result;
|
||||
}
|
||||
|
||||
let total = supply;
|
||||
let targets = PERCENTILES.map(|p| total * p / 100);
|
||||
|
||||
let mut accumulated = Sats::ZERO;
|
||||
let mut pct_idx = 0;
|
||||
|
||||
for (&price, &sats) in price_to_amount.iter() {
|
||||
accumulated += sats;
|
||||
|
||||
while pct_idx < PERCENTILES_LEN && accumulated >= targets[pct_idx] {
|
||||
result[pct_idx] = price;
|
||||
pct_idx += 1;
|
||||
}
|
||||
|
||||
if pct_idx >= PERCENTILES_LEN {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Vecs {
|
||||
type Target = common::Vecs;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl Filtered for Vecs {
|
||||
fn filter(&self) -> &Filter {
|
||||
&self.inner.filter
|
||||
}
|
||||
}
|
||||
@@ -1,697 +0,0 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_grouper::{
|
||||
AmountFilter, ByAgeRange, ByAmountRange, ByEpoch, ByGreatEqualAmount, ByLowerThanAmount,
|
||||
ByMaxAge, ByMinAge, BySpendableType, ByTerm, Filter, Filtered, StateLevel, Term, TimeFilter,
|
||||
UTXOGroups,
|
||||
};
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Bitcoin, CheckedSub, DateIndex, Dollars, HalvingEpoch, Height, ONE_DAY_IN_SEC, OutputType,
|
||||
Sats, Timestamp, Version,
|
||||
};
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
use rayon::prelude::*;
|
||||
use rustc_hash::FxHashMap;
|
||||
use vecdb::{Database, Exit, IterableVec, VecIndex};
|
||||
|
||||
use crate::{
|
||||
Indexes, indexes, price,
|
||||
stateful::{Flushable, HeightFlushable, r#trait::DynCohortVecs},
|
||||
states::{BlockState, Transacted},
|
||||
utils::OptionExt,
|
||||
};
|
||||
|
||||
use super::{r#trait::CohortVecs, utxo_cohort};
|
||||
|
||||
const VERSION: Version = Version::new(0);
|
||||
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
pub struct Vecs(UTXOGroups<utxo_cohort::Vecs>);
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
states_path: &Path,
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION + Version::ZERO;
|
||||
|
||||
// Helper to create a cohort - booleans are now derived from filter
|
||||
let create = |filter: Filter, state_level: StateLevel| -> Result<utxo_cohort::Vecs> {
|
||||
utxo_cohort::Vecs::forced_import(
|
||||
db,
|
||||
filter,
|
||||
v,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
state_level,
|
||||
)
|
||||
};
|
||||
|
||||
let full = |f: Filter| create(f, StateLevel::Full);
|
||||
let none = |f: Filter| create(f, StateLevel::None);
|
||||
|
||||
Ok(Self(UTXOGroups {
|
||||
// Special case: all uses Version::ONE
|
||||
all: utxo_cohort::Vecs::forced_import(
|
||||
db,
|
||||
Filter::All,
|
||||
version + VERSION + Version::ONE,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
StateLevel::PriceOnly,
|
||||
)?,
|
||||
|
||||
term: ByTerm {
|
||||
short: create(Filter::Term(Term::Sth), StateLevel::PriceOnly)?,
|
||||
long: create(Filter::Term(Term::Lth), StateLevel::PriceOnly)?,
|
||||
},
|
||||
|
||||
epoch: ByEpoch {
|
||||
_0: full(Filter::Epoch(HalvingEpoch::new(0)))?,
|
||||
_1: full(Filter::Epoch(HalvingEpoch::new(1)))?,
|
||||
_2: full(Filter::Epoch(HalvingEpoch::new(2)))?,
|
||||
_3: full(Filter::Epoch(HalvingEpoch::new(3)))?,
|
||||
_4: full(Filter::Epoch(HalvingEpoch::new(4)))?,
|
||||
},
|
||||
|
||||
type_: BySpendableType {
|
||||
p2pk65: full(Filter::Type(OutputType::P2PK65))?,
|
||||
p2pk33: full(Filter::Type(OutputType::P2PK33))?,
|
||||
p2pkh: full(Filter::Type(OutputType::P2PKH))?,
|
||||
p2sh: full(Filter::Type(OutputType::P2SH))?,
|
||||
p2wpkh: full(Filter::Type(OutputType::P2WPKH))?,
|
||||
p2wsh: full(Filter::Type(OutputType::P2WSH))?,
|
||||
p2tr: full(Filter::Type(OutputType::P2TR))?,
|
||||
p2a: full(Filter::Type(OutputType::P2A))?,
|
||||
p2ms: full(Filter::Type(OutputType::P2MS))?,
|
||||
empty: full(Filter::Type(OutputType::Empty))?,
|
||||
unknown: full(Filter::Type(OutputType::Unknown))?,
|
||||
},
|
||||
|
||||
max_age: ByMaxAge {
|
||||
_1w: none(Filter::Time(TimeFilter::LowerThan(7)))?,
|
||||
_1m: none(Filter::Time(TimeFilter::LowerThan(30)))?,
|
||||
_2m: none(Filter::Time(TimeFilter::LowerThan(2 * 30)))?,
|
||||
_3m: none(Filter::Time(TimeFilter::LowerThan(3 * 30)))?,
|
||||
_4m: none(Filter::Time(TimeFilter::LowerThan(4 * 30)))?,
|
||||
_5m: none(Filter::Time(TimeFilter::LowerThan(5 * 30)))?,
|
||||
_6m: none(Filter::Time(TimeFilter::LowerThan(6 * 30)))?,
|
||||
_1y: none(Filter::Time(TimeFilter::LowerThan(365)))?,
|
||||
_2y: none(Filter::Time(TimeFilter::LowerThan(2 * 365)))?,
|
||||
_3y: none(Filter::Time(TimeFilter::LowerThan(3 * 365)))?,
|
||||
_4y: none(Filter::Time(TimeFilter::LowerThan(4 * 365)))?,
|
||||
_5y: none(Filter::Time(TimeFilter::LowerThan(5 * 365)))?,
|
||||
_6y: none(Filter::Time(TimeFilter::LowerThan(6 * 365)))?,
|
||||
_7y: none(Filter::Time(TimeFilter::LowerThan(7 * 365)))?,
|
||||
_8y: none(Filter::Time(TimeFilter::LowerThan(8 * 365)))?,
|
||||
_10y: none(Filter::Time(TimeFilter::LowerThan(10 * 365)))?,
|
||||
_12y: none(Filter::Time(TimeFilter::LowerThan(12 * 365)))?,
|
||||
_15y: none(Filter::Time(TimeFilter::LowerThan(15 * 365)))?,
|
||||
},
|
||||
|
||||
min_age: ByMinAge {
|
||||
_1d: none(Filter::Time(TimeFilter::GreaterOrEqual(1)))?,
|
||||
_1w: none(Filter::Time(TimeFilter::GreaterOrEqual(7)))?,
|
||||
_1m: none(Filter::Time(TimeFilter::GreaterOrEqual(30)))?,
|
||||
_2m: none(Filter::Time(TimeFilter::GreaterOrEqual(2 * 30)))?,
|
||||
_3m: none(Filter::Time(TimeFilter::GreaterOrEqual(3 * 30)))?,
|
||||
_4m: none(Filter::Time(TimeFilter::GreaterOrEqual(4 * 30)))?,
|
||||
_5m: none(Filter::Time(TimeFilter::GreaterOrEqual(5 * 30)))?,
|
||||
_6m: none(Filter::Time(TimeFilter::GreaterOrEqual(6 * 30)))?,
|
||||
_1y: none(Filter::Time(TimeFilter::GreaterOrEqual(365)))?,
|
||||
_2y: none(Filter::Time(TimeFilter::GreaterOrEqual(2 * 365)))?,
|
||||
_3y: none(Filter::Time(TimeFilter::GreaterOrEqual(3 * 365)))?,
|
||||
_4y: none(Filter::Time(TimeFilter::GreaterOrEqual(4 * 365)))?,
|
||||
_5y: none(Filter::Time(TimeFilter::GreaterOrEqual(5 * 365)))?,
|
||||
_6y: none(Filter::Time(TimeFilter::GreaterOrEqual(6 * 365)))?,
|
||||
_7y: none(Filter::Time(TimeFilter::GreaterOrEqual(7 * 365)))?,
|
||||
_8y: none(Filter::Time(TimeFilter::GreaterOrEqual(8 * 365)))?,
|
||||
_10y: none(Filter::Time(TimeFilter::GreaterOrEqual(10 * 365)))?,
|
||||
_12y: none(Filter::Time(TimeFilter::GreaterOrEqual(12 * 365)))?,
|
||||
},
|
||||
|
||||
age_range: ByAgeRange {
|
||||
up_to_1d: full(Filter::Time(TimeFilter::Range(0..1)))?,
|
||||
_1d_to_1w: full(Filter::Time(TimeFilter::Range(1..7)))?,
|
||||
_1w_to_1m: full(Filter::Time(TimeFilter::Range(7..30)))?,
|
||||
_1m_to_2m: full(Filter::Time(TimeFilter::Range(30..60)))?,
|
||||
_2m_to_3m: full(Filter::Time(TimeFilter::Range(60..90)))?,
|
||||
_3m_to_4m: full(Filter::Time(TimeFilter::Range(90..120)))?,
|
||||
_4m_to_5m: full(Filter::Time(TimeFilter::Range(120..150)))?,
|
||||
_5m_to_6m: full(Filter::Time(TimeFilter::Range(150..180)))?,
|
||||
_6m_to_1y: full(Filter::Time(TimeFilter::Range(180..365)))?,
|
||||
_1y_to_2y: full(Filter::Time(TimeFilter::Range(365..730)))?,
|
||||
_2y_to_3y: full(Filter::Time(TimeFilter::Range(730..1095)))?,
|
||||
_3y_to_4y: full(Filter::Time(TimeFilter::Range(1095..1460)))?,
|
||||
_4y_to_5y: full(Filter::Time(TimeFilter::Range(1460..1825)))?,
|
||||
_5y_to_6y: full(Filter::Time(TimeFilter::Range(1825..2190)))?,
|
||||
_6y_to_7y: full(Filter::Time(TimeFilter::Range(2190..2555)))?,
|
||||
_7y_to_8y: full(Filter::Time(TimeFilter::Range(2555..2920)))?,
|
||||
_8y_to_10y: full(Filter::Time(TimeFilter::Range(2920..3650)))?,
|
||||
_10y_to_12y: full(Filter::Time(TimeFilter::Range(3650..4380)))?,
|
||||
_12y_to_15y: full(Filter::Time(TimeFilter::Range(4380..5475)))?,
|
||||
from_15y: full(Filter::Time(TimeFilter::GreaterOrEqual(15 * 365)))?,
|
||||
},
|
||||
|
||||
amount_range: ByAmountRange {
|
||||
_0sats: full(Filter::Amount(AmountFilter::LowerThan(Sats::_1)))?,
|
||||
_1sat_to_10sats: full(Filter::Amount(AmountFilter::Range(Sats::_1..Sats::_10)))?,
|
||||
_10sats_to_100sats: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_10..Sats::_100,
|
||||
)))?,
|
||||
_100sats_to_1k_sats: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_100..Sats::_1K,
|
||||
)))?,
|
||||
_1k_sats_to_10k_sats: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_1K..Sats::_10K,
|
||||
)))?,
|
||||
_10k_sats_to_100k_sats: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_10K..Sats::_100K,
|
||||
)))?,
|
||||
_100k_sats_to_1m_sats: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_100K..Sats::_1M,
|
||||
)))?,
|
||||
_1m_sats_to_10m_sats: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_1M..Sats::_10M,
|
||||
)))?,
|
||||
_10m_sats_to_1btc: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_10M..Sats::_1BTC,
|
||||
)))?,
|
||||
_1btc_to_10btc: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_1BTC..Sats::_10BTC,
|
||||
)))?,
|
||||
_10btc_to_100btc: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_10BTC..Sats::_100BTC,
|
||||
)))?,
|
||||
_100btc_to_1k_btc: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_100BTC..Sats::_1K_BTC,
|
||||
)))?,
|
||||
_1k_btc_to_10k_btc: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_1K_BTC..Sats::_10K_BTC,
|
||||
)))?,
|
||||
_10k_btc_to_100k_btc: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_10K_BTC..Sats::_100K_BTC,
|
||||
)))?,
|
||||
_100k_btc_or_more: full(Filter::Amount(AmountFilter::GreaterOrEqual(
|
||||
Sats::_100K_BTC,
|
||||
)))?,
|
||||
},
|
||||
|
||||
lt_amount: ByLowerThanAmount {
|
||||
_10sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10)))?,
|
||||
_100sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100)))?,
|
||||
_1k_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1K)))?,
|
||||
_10k_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10K)))?,
|
||||
_100k_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100K)))?,
|
||||
_1m_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1M)))?,
|
||||
_10m_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10M)))?,
|
||||
_1btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1BTC)))?,
|
||||
_10btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10BTC)))?,
|
||||
_100btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100BTC)))?,
|
||||
_1k_btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1K_BTC)))?,
|
||||
_10k_btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10K_BTC)))?,
|
||||
_100k_btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100K_BTC)))?,
|
||||
},
|
||||
|
||||
ge_amount: ByGreatEqualAmount {
|
||||
_1sat: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1)))?,
|
||||
_10sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10)))?,
|
||||
_100sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100)))?,
|
||||
_1k_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1K)))?,
|
||||
_10k_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10K)))?,
|
||||
_100k_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100K)))?,
|
||||
_1m_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1M)))?,
|
||||
_10m_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10M)))?,
|
||||
_1btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1BTC)))?,
|
||||
_10btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10BTC)))?,
|
||||
_100btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100BTC)))?,
|
||||
_1k_btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1K_BTC)))?,
|
||||
_10k_btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10K_BTC)))?,
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn tick_tock_next_block(&mut self, chain_state: &[BlockState], timestamp: Timestamp) {
|
||||
if chain_state.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let prev_timestamp = chain_state.last().unwrap().timestamp;
|
||||
|
||||
// Only blocks whose age % ONE_DAY >= threshold can cross a day boundary.
|
||||
// Saves 1 subtraction + 2 divisions per block vs computing days_old directly.
|
||||
let elapsed = (*timestamp).saturating_sub(*prev_timestamp);
|
||||
let threshold = ONE_DAY_IN_SEC.saturating_sub(elapsed);
|
||||
|
||||
// Extract all mutable references upfront to avoid borrow checker issues
|
||||
// Use a single destructuring to get non-overlapping mutable borrows
|
||||
let UTXOGroups {
|
||||
all,
|
||||
term,
|
||||
age_range,
|
||||
..
|
||||
} = &mut self.0;
|
||||
|
||||
let mut vecs = age_range
|
||||
.iter_mut()
|
||||
.map(|v| (v.filter().clone(), &mut v.state))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Collect aggregate cohorts' filter and p2a for age transitions
|
||||
let mut aggregate_p2a: Vec<(Filter, Option<&mut crate::PriceToAmount>)> = vec![
|
||||
(all.filter().clone(), all.price_to_amount.as_mut()),
|
||||
(
|
||||
term.short.filter().clone(),
|
||||
term.short.price_to_amount.as_mut(),
|
||||
),
|
||||
(
|
||||
term.long.filter().clone(),
|
||||
term.long.price_to_amount.as_mut(),
|
||||
),
|
||||
];
|
||||
|
||||
chain_state
|
||||
.iter()
|
||||
.filter(|block_state| {
|
||||
let age = (*prev_timestamp).saturating_sub(*block_state.timestamp);
|
||||
age % ONE_DAY_IN_SEC >= threshold
|
||||
})
|
||||
.for_each(|block_state| {
|
||||
let prev_days_old =
|
||||
prev_timestamp.difference_in_days_between(block_state.timestamp);
|
||||
let days_old = timestamp.difference_in_days_between(block_state.timestamp);
|
||||
|
||||
if prev_days_old == days_old {
|
||||
return;
|
||||
}
|
||||
|
||||
vecs.iter_mut().for_each(|(filter, state)| {
|
||||
let is = filter.contains_time(days_old);
|
||||
let was = filter.contains_time(prev_days_old);
|
||||
|
||||
if is && !was {
|
||||
state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.increment(&block_state.supply, block_state.price);
|
||||
} else if was && !is {
|
||||
state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.decrement(&block_state.supply, block_state.price);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle age transitions for aggregate cohorts' price_to_amount
|
||||
// Check which cohorts the UTXO was in vs is now in, and increment/decrement accordingly
|
||||
// Only process if there's remaining supply (like CohortState::increment/decrement do)
|
||||
if let Some(price) = block_state.price
|
||||
&& block_state.supply.value > Sats::ZERO
|
||||
{
|
||||
aggregate_p2a.iter_mut().for_each(|(filter, p2a)| {
|
||||
let is = filter.contains_time(days_old);
|
||||
let was = filter.contains_time(prev_days_old);
|
||||
|
||||
if is && !was {
|
||||
p2a.um().increment(price, &block_state.supply);
|
||||
} else if was && !is {
|
||||
p2a.um().decrement(price, &block_state.supply);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send(
|
||||
&mut self,
|
||||
height_to_sent: FxHashMap<Height, Transacted>,
|
||||
chain_state: &mut [BlockState],
|
||||
) {
|
||||
// Extract all mutable references upfront to avoid borrow checker issues
|
||||
let UTXOGroups {
|
||||
all,
|
||||
term,
|
||||
age_range,
|
||||
epoch,
|
||||
type_,
|
||||
amount_range,
|
||||
..
|
||||
} = &mut self.0;
|
||||
|
||||
let mut time_based_vecs = age_range
|
||||
.iter_mut()
|
||||
.chain(epoch.iter_mut())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Collect aggregate cohorts' filter and p2a for iteration
|
||||
let mut aggregate_p2a: Vec<(Filter, Option<&mut crate::PriceToAmount>)> = vec![
|
||||
(all.filter().clone(), all.price_to_amount.as_mut()),
|
||||
(
|
||||
term.short.filter().clone(),
|
||||
term.short.price_to_amount.as_mut(),
|
||||
),
|
||||
(
|
||||
term.long.filter().clone(),
|
||||
term.long.price_to_amount.as_mut(),
|
||||
),
|
||||
];
|
||||
|
||||
let last_block = chain_state.last().unwrap();
|
||||
let last_timestamp = last_block.timestamp;
|
||||
let current_price = last_block.price;
|
||||
|
||||
let chain_state_len = chain_state.len();
|
||||
|
||||
height_to_sent.into_iter().for_each(|(height, sent)| {
|
||||
chain_state[height.to_usize()].supply -= &sent.spendable_supply;
|
||||
|
||||
let block_state = chain_state.get(height.to_usize()).unwrap();
|
||||
|
||||
let prev_price = block_state.price;
|
||||
|
||||
let blocks_old = chain_state_len - 1 - height.to_usize();
|
||||
|
||||
let days_old = last_timestamp.difference_in_days_between(block_state.timestamp);
|
||||
let days_old_float =
|
||||
last_timestamp.difference_in_days_between_float(block_state.timestamp);
|
||||
|
||||
let older_than_hour = last_timestamp
|
||||
.checked_sub(block_state.timestamp)
|
||||
.unwrap()
|
||||
.is_more_than_hour();
|
||||
|
||||
time_based_vecs
|
||||
.iter_mut()
|
||||
.filter(|v| match v.filter() {
|
||||
Filter::Time(TimeFilter::GreaterOrEqual(from)) => *from <= days_old,
|
||||
Filter::Time(TimeFilter::LowerThan(to)) => *to > days_old,
|
||||
Filter::Time(TimeFilter::Range(range)) => range.contains(&days_old),
|
||||
Filter::Epoch(epoch) => *epoch == HalvingEpoch::from(height),
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.for_each(|vecs| {
|
||||
vecs.state.um().send(
|
||||
&sent.spendable_supply,
|
||||
current_price,
|
||||
prev_price,
|
||||
blocks_old,
|
||||
days_old_float,
|
||||
older_than_hour,
|
||||
);
|
||||
});
|
||||
|
||||
sent.by_type
|
||||
.spendable
|
||||
.iter_typed()
|
||||
.for_each(|(output_type, supply_state)| {
|
||||
type_.get_mut(output_type).state.um().send(
|
||||
supply_state,
|
||||
current_price,
|
||||
prev_price,
|
||||
blocks_old,
|
||||
days_old_float,
|
||||
older_than_hour,
|
||||
)
|
||||
});
|
||||
|
||||
sent.by_size_group
|
||||
.iter_typed()
|
||||
.for_each(|(group, supply_state)| {
|
||||
amount_range.get_mut(group).state.um().send(
|
||||
supply_state,
|
||||
current_price,
|
||||
prev_price,
|
||||
blocks_old,
|
||||
days_old_float,
|
||||
older_than_hour,
|
||||
);
|
||||
});
|
||||
|
||||
// Update aggregate cohorts' price_to_amount using filter.contains_time()
|
||||
if let Some(prev_price) = prev_price {
|
||||
let supply_state = &sent.spendable_supply;
|
||||
if supply_state.value.is_not_zero() {
|
||||
aggregate_p2a
|
||||
.iter_mut()
|
||||
.filter(|(f, _)| f.contains_time(days_old))
|
||||
.map(|(_, p2a)| p2a)
|
||||
.for_each(|p2a| {
|
||||
p2a.um().decrement(prev_price, supply_state);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn receive(&mut self, received: Transacted, height: Height, price: Option<Dollars>) {
|
||||
let supply_state = received.spendable_supply;
|
||||
|
||||
[
|
||||
&mut self.0.age_range.up_to_1d,
|
||||
self.0.epoch.mut_vec_from_height(height),
|
||||
]
|
||||
.into_iter()
|
||||
.for_each(|v| {
|
||||
v.state.um().receive(&supply_state, price);
|
||||
});
|
||||
|
||||
// Update aggregate cohorts' price_to_amount
|
||||
// New UTXOs have days_old = 0, so use filter.contains_time(0) to check applicability
|
||||
if let Some(price) = price
|
||||
&& supply_state.value.is_not_zero()
|
||||
{
|
||||
self.0
|
||||
.iter_aggregate_mut()
|
||||
.filter(|v| v.filter().contains_time(0))
|
||||
.for_each(|v| {
|
||||
v.price_to_amount
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.increment(price, &supply_state);
|
||||
});
|
||||
}
|
||||
|
||||
self.type_.iter_mut().for_each(|vecs| {
|
||||
let output_type = match vecs.filter() {
|
||||
Filter::Type(output_type) => *output_type,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
vecs.state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.receive(received.by_type.get(output_type), price)
|
||||
});
|
||||
|
||||
received
|
||||
.by_size_group
|
||||
.iter_typed()
|
||||
.for_each(|(group, supply_state)| {
|
||||
self.amount_range
|
||||
.get_mut(group)
|
||||
.state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.receive(supply_state, price);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn compute_overlapping_vecs(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let by_date_range = &self.0.age_range;
|
||||
let by_size_range = &self.0.amount_range;
|
||||
|
||||
[(&mut self.0.all, by_date_range.iter().collect::<Vec<_>>())]
|
||||
.into_par_iter()
|
||||
.chain(self.0.min_age.par_iter_mut().map(|vecs| {
|
||||
let filter = vecs.filter().clone();
|
||||
(
|
||||
vecs,
|
||||
by_date_range
|
||||
.iter()
|
||||
.filter(|other| filter.includes(other.filter()))
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
}))
|
||||
.chain(self.0.max_age.par_iter_mut().map(|vecs| {
|
||||
let filter = vecs.filter().clone();
|
||||
(
|
||||
vecs,
|
||||
by_date_range
|
||||
.iter()
|
||||
.filter(|other| filter.includes(other.filter()))
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
}))
|
||||
.chain(self.0.term.par_iter_mut().map(|vecs| {
|
||||
let filter = vecs.filter().clone();
|
||||
(
|
||||
vecs,
|
||||
by_date_range
|
||||
.iter()
|
||||
.filter(|other| filter.includes(other.filter()))
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
}))
|
||||
.chain(self.0.ge_amount.par_iter_mut().map(|vecs| {
|
||||
let filter = vecs.filter().clone();
|
||||
(
|
||||
vecs,
|
||||
by_size_range
|
||||
.iter()
|
||||
.filter(|other| filter.includes(other.filter()))
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
}))
|
||||
.chain(self.0.lt_amount.par_iter_mut().map(|vecs| {
|
||||
let filter = vecs.filter().clone();
|
||||
(
|
||||
vecs,
|
||||
by_size_range
|
||||
.iter()
|
||||
.filter(|other| filter.includes(other.filter()))
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
}))
|
||||
.try_for_each(|(vecs, stateful)| {
|
||||
vecs.compute_from_stateful(starting_indexes, &stateful, exit)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute_rest_part1(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.par_iter_mut()
|
||||
.try_for_each(|v| v.compute_rest_part1(indexes, price, starting_indexes, exit))
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn compute_rest_part2(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
height_to_supply: &impl IterableVec<Height, Bitcoin>,
|
||||
dateindex_to_supply: &impl IterableVec<DateIndex, Bitcoin>,
|
||||
height_to_market_cap: Option<&impl IterableVec<Height, Dollars>>,
|
||||
dateindex_to_market_cap: Option<&impl IterableVec<DateIndex, Dollars>>,
|
||||
height_to_realized_cap: Option<&impl IterableVec<Height, Dollars>>,
|
||||
dateindex_to_realized_cap: Option<&impl IterableVec<DateIndex, Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.par_iter_mut().try_for_each(|v| {
|
||||
v.compute_rest_part2(
|
||||
indexes,
|
||||
price,
|
||||
starting_indexes,
|
||||
height_to_supply,
|
||||
dateindex_to_supply,
|
||||
height_to_market_cap,
|
||||
dateindex_to_market_cap,
|
||||
height_to_realized_cap,
|
||||
dateindex_to_realized_cap,
|
||||
exit,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn safe_flush_stateful_vecs(&mut self, height: Height, exit: &Exit) -> Result<()> {
|
||||
// Flush stateful cohorts
|
||||
self.par_iter_separate_mut()
|
||||
.try_for_each(|v| v.safe_flush_stateful_vecs(height, exit))?;
|
||||
|
||||
// Flush aggregate cohorts' price_to_amount and price_percentiles
|
||||
// Using traits ensures we can't forget to flush any field
|
||||
self.0.par_iter_aggregate_mut().try_for_each(|v| {
|
||||
v.price_to_amount.flush_at_height(height, exit)?;
|
||||
v.inner.price_percentiles.safe_write(exit)?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// Reset aggregate cohorts' price_to_amount when starting from scratch
|
||||
pub fn reset_aggregate_price_to_amount(&mut self) -> Result<()> {
|
||||
self.0
|
||||
.iter_aggregate_mut()
|
||||
.try_for_each(|v| v.price_to_amount.reset())
|
||||
}
|
||||
|
||||
/// Import aggregate cohorts' price_to_amount from disk when resuming from a checkpoint.
|
||||
/// Returns the height to start processing from (checkpoint_height + 1), matching the
|
||||
/// behavior of `common::import_state` for separate cohorts.
|
||||
///
|
||||
/// Note: We don't check inner.min_height_vecs_len() for aggregate cohorts because their
|
||||
/// inner vecs (height_to_supply, etc.) are computed post-hoc by compute_overlapping_vecs,
|
||||
/// not maintained during the main processing loop.
|
||||
pub fn import_aggregate_price_to_amount(&mut self, height: Height) -> Result<Height> {
|
||||
// Match separate vecs behavior: decrement height to get prev_height
|
||||
let Some(mut prev_height) = height.decremented() else {
|
||||
// height is 0, return ZERO (caller will handle this)
|
||||
return Ok(Height::ZERO);
|
||||
};
|
||||
|
||||
for v in self.0.iter_aggregate_mut() {
|
||||
// Using HeightFlushable trait - if price_to_amount is None, returns height unchanged
|
||||
prev_height = prev_height.min(v.price_to_amount.import_at_or_before(prev_height)?);
|
||||
}
|
||||
// Return prev_height + 1, matching separate vecs behavior
|
||||
Ok(prev_height.incremented())
|
||||
}
|
||||
|
||||
/// Compute and push percentiles for aggregate cohorts (all, sth, lth).
|
||||
/// Must be called after receive()/send() when price_to_amount is up to date.
|
||||
pub fn truncate_push_aggregate_percentiles(&mut self, height: Height) -> Result<()> {
|
||||
let age_range_data: Vec<_> = self
|
||||
.0
|
||||
.age_range
|
||||
.iter()
|
||||
.map(|sub| (sub.filter().clone(), sub.state.u().supply.value))
|
||||
.collect();
|
||||
|
||||
let results: Vec<_> = self
|
||||
.0
|
||||
.par_iter_aggregate()
|
||||
.map(|v| {
|
||||
if v.price_to_amount.is_none() {
|
||||
panic!();
|
||||
}
|
||||
let filter = v.filter().clone();
|
||||
let supply = age_range_data
|
||||
.iter()
|
||||
.filter(|(sub_filter, _)| filter.includes(sub_filter))
|
||||
.map(|(_, value)| *value)
|
||||
.fold(Sats::ZERO, |acc, v| acc + v);
|
||||
let percentiles = v.compute_percentile_prices_from_standalone(supply);
|
||||
(filter, percentiles)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Push results sequentially (requires &mut)
|
||||
for (filter, percentiles) in results {
|
||||
let v = self
|
||||
.0
|
||||
.iter_aggregate_mut()
|
||||
.find(|v| v.filter() == &filter)
|
||||
.unwrap();
|
||||
|
||||
if let Some(pp) = v.inner.price_percentiles.as_mut() {
|
||||
pp.truncate_push(height, &percentiles)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
use brk_types::{EmptyAddressData, EmptyAddressIndex, LoadedAddressData, LoadedAddressIndex};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum WithAddressDataSource<T> {
|
||||
New(T),
|
||||
FromLoadedAddressDataVec((LoadedAddressIndex, T)),
|
||||
FromEmptyAddressDataVec((EmptyAddressIndex, T)),
|
||||
}
|
||||
|
||||
impl<T> WithAddressDataSource<T> {
|
||||
pub fn is_new(&self) -> bool {
|
||||
matches!(self, Self::New(_))
|
||||
}
|
||||
|
||||
pub fn is_from_emptyaddressdata(&self) -> bool {
|
||||
matches!(self, Self::FromEmptyAddressDataVec(_))
|
||||
}
|
||||
|
||||
pub fn deref_mut(&mut self) -> &mut T {
|
||||
match self {
|
||||
Self::New(v) => v,
|
||||
Self::FromLoadedAddressDataVec((_, v)) => v,
|
||||
Self::FromEmptyAddressDataVec((_, v)) => v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WithAddressDataSource<EmptyAddressData>> for WithAddressDataSource<LoadedAddressData> {
|
||||
#[inline]
|
||||
fn from(value: WithAddressDataSource<EmptyAddressData>) -> Self {
|
||||
match value {
|
||||
WithAddressDataSource::New(v) => Self::New(v.into()),
|
||||
WithAddressDataSource::FromLoadedAddressDataVec((i, v)) => {
|
||||
Self::FromLoadedAddressDataVec((i, v.into()))
|
||||
}
|
||||
WithAddressDataSource::FromEmptyAddressDataVec((i, v)) => {
|
||||
Self::FromEmptyAddressDataVec((i, v.into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WithAddressDataSource<LoadedAddressData>> for WithAddressDataSource<EmptyAddressData> {
|
||||
#[inline]
|
||||
fn from(value: WithAddressDataSource<LoadedAddressData>) -> Self {
|
||||
match value {
|
||||
WithAddressDataSource::New(v) => Self::New(v.into()),
|
||||
WithAddressDataSource::FromLoadedAddressDataVec((i, v)) => {
|
||||
Self::FromLoadedAddressDataVec((i, v.into()))
|
||||
}
|
||||
WithAddressDataSource::FromEmptyAddressDataVec((i, v)) => {
|
||||
Self::FromEmptyAddressDataVec((i, v.into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user