global: utxos part 2

This commit is contained in:
nym21
2025-05-17 19:51:52 +02:00
parent 7b38355cd4
commit c8a25934a6
15 changed files with 435 additions and 60 deletions

View File

@@ -4,9 +4,10 @@ use brk_core::{Dollars, Sats, StoredUsize};
// Vecs ? probably // Vecs ? probably
#[derive(Default)] #[derive(Default)]
pub struct CohortStates { pub struct CohortState {
pub realized_cap: Dollars, pub realized_cap: Dollars,
pub supply: Sats, pub supply: Sats,
pub unspendable_supply: Sats,
pub utxo_count: StoredUsize, pub utxo_count: StoredUsize,
// pub price_to_amount: PriceToValue<Amount>, save it not rounded in fjall // pub price_to_amount: PriceToValue<Amount>, save it not rounded in fjall
} }

View File

@@ -10,6 +10,7 @@ pub struct Outputs<T> {
// pub by_epoch: OutputsByEpoch<T>, // pub by_epoch: OutputsByEpoch<T>,
// pub by_size: OutputsBySize<T>, // pub by_size: OutputsBySize<T>,
// pub by_value: OutputsByValue<T>, // pub by_value: OutputsByValue<T>,
// pub by_type: OutputsByType<T>, // all but op-return
} }
#[derive(Default)] #[derive(Default)]
@@ -105,3 +106,6 @@ pub struct OutputsByValue<T> {
pub from_1_000_000_000usd: T, pub from_1_000_000_000usd: T,
// ... // ...
} }
// #[derive(Default)]
// pub struct OutputsByType<T> {}

View File

@@ -136,8 +136,8 @@ impl ComputedValueVecsFromHeight {
)?; )?;
} }
let txindex = self.bitcoin.height.as_ref().unwrap().as_ref(); let height_to_bitcoin = self.bitcoin.height.as_ref().unwrap().as_ref();
let price = &fetched.as_ref().unwrap().chainindexes_to_close.height; let height_to_close = &fetched.as_ref().unwrap().chainindexes_to_close.height;
if let Some(dollars) = self.dollars.as_mut() { if let Some(dollars) = self.dollars.as_mut() {
dollars.compute_all( dollars.compute_all(
@@ -146,7 +146,12 @@ impl ComputedValueVecsFromHeight {
starting_indexes, starting_indexes,
exit, exit,
|v, _, _, starting_indexes, exit| { |v, _, _, starting_indexes, exit| {
v.compute_from_bitcoin(starting_indexes.height, txindex, price, exit) v.compute_from_bitcoin(
starting_indexes.height,
height_to_bitcoin,
height_to_close,
exit,
)
}, },
)?; )?;
} }

View File

@@ -25,7 +25,7 @@ pub struct Vecs {
pub mining: mining::Vecs, pub mining: mining::Vecs,
pub market: market::Vecs, pub market: market::Vecs,
pub transactions: transactions::Vecs, pub transactions: transactions::Vecs,
// pub utxos: utxos::Vecs, pub utxos: utxos::Vecs,
pub fetched: Option<fetched::Vecs>, pub fetched: Option<fetched::Vecs>,
} }
@@ -49,7 +49,7 @@ impl Vecs {
mining: mining::Vecs::forced_import(path, computation, compressed)?, mining: mining::Vecs::forced_import(path, computation, compressed)?,
constants: constants::Vecs::forced_import(path, computation, compressed)?, constants: constants::Vecs::forced_import(path, computation, compressed)?,
market: market::Vecs::forced_import(path, computation, compressed)?, market: market::Vecs::forced_import(path, computation, compressed)?,
// utxos: utxos::Vecs::forced_import(path, computation, compressed)?, utxos: utxos::Vecs::forced_import(path, computation, compressed, fetched.as_ref())?,
transactions: transactions::Vecs::forced_import( transactions: transactions::Vecs::forced_import(
path, path,
indexer, indexer,
@@ -110,6 +110,15 @@ impl Vecs {
)?; )?;
} }
self.utxos.compute(
indexer,
&self.indexes,
&self.transactions,
self.fetched.as_ref(),
&starting_indexes,
exit,
)?;
Ok(()) Ok(())
} }
@@ -121,6 +130,7 @@ impl Vecs {
self.mining.vecs(), self.mining.vecs(),
self.market.vecs(), self.market.vecs(),
self.transactions.vecs(), self.transactions.vecs(),
self.utxos.vecs(),
self.fetched.as_ref().map_or(vec![], |v| v.vecs()), self.fetched.as_ref().map_or(vec![], |v| v.vecs()),
] ]
.concat() .concat()

View File

@@ -82,7 +82,7 @@ pub struct Vecs {
ComputedVecFrom2<TxIndex, Weight, TxIndex, StoredU32, TxIndex, StoredU32>, ComputedVecFrom2<TxIndex, Weight, TxIndex, StoredU32, TxIndex, StoredU32>,
pub txindex_to_fee: ComputedVecFrom2<TxIndex, Sats, TxIndex, Sats, TxIndex, Sats>, pub txindex_to_fee: ComputedVecFrom2<TxIndex, Sats, TxIndex, Sats, TxIndex, Sats>,
pub txindex_to_feerate: ComputedVecFrom2<TxIndex, Feerate, TxIndex, Sats, TxIndex, StoredUsize>, pub txindex_to_feerate: ComputedVecFrom2<TxIndex, Feerate, TxIndex, Sats, TxIndex, StoredUsize>,
pub indexes_to_utxo_count: ComputedVecsFromHeight<StoredUsize>, pub indexes_to_exact_utxo_count: ComputedVecsFromHeight<StoredUsize>,
} }
impl Vecs { impl Vecs {
@@ -632,9 +632,9 @@ impl Vecs {
.add_sum() .add_sum()
.add_total(), .add_total(),
)?, )?,
indexes_to_utxo_count: ComputedVecsFromHeight::forced_import( indexes_to_exact_utxo_count: ComputedVecsFromHeight::forced_import(
path, path,
"utxo_count_bis", "exact_utxo_count",
true, true,
Version::TWO, Version::TWO,
compressed, compressed,
@@ -1039,7 +1039,7 @@ impl Vecs {
}, },
)?; )?;
self.indexes_to_utxo_count.compute_all( self.indexes_to_exact_utxo_count.compute_all(
indexer, indexer,
indexes, indexes,
starting_indexes, starting_indexes,
@@ -1130,7 +1130,7 @@ impl Vecs {
self.indexes_to_tx_vsize.vecs(), self.indexes_to_tx_vsize.vecs(),
self.indexes_to_tx_weight.vecs(), self.indexes_to_tx_weight.vecs(),
self.indexes_to_unknownoutput_count.vecs(), self.indexes_to_unknownoutput_count.vecs(),
self.indexes_to_utxo_count.vecs(), self.indexes_to_exact_utxo_count.vecs(),
] ]
.concat() .concat()
} }

View File

@@ -1,19 +1,23 @@
use std::{fs, path::Path}; use std::{fs, path::Path, thread};
use brk_core::{CheckedSub, Dollars, Height, Sats, StoredUsize}; use brk_core::{
Bitcoin, CheckedSub, Dollars, Height, InputIndex, OutputIndex, OutputType, Sats, StoredUsize,
};
use brk_exit::Exit; use brk_exit::Exit;
use brk_indexer::Indexer; use brk_indexer::Indexer;
use brk_vec::{ use brk_vec::{
AnyCollectableVec, AnyVec, BaseVecIterator, Compressed, Computation, EagerVec, StoredIndex, AnyCollectableVec, AnyVec, BaseVecIterator, Compressed, Computation, EagerVec, StoredIndex,
VecIterator, Version, VecIterator, Version,
}; };
use color_eyre::eyre::ContextCompat;
use derive_deref::{Deref, DerefMut}; use derive_deref::{Deref, DerefMut};
use rayon::prelude::*;
use crate::states::{CohortStates, Outputs}; use crate::states::{CohortState, Outputs};
use super::{ use super::{
Indexes, Indexes, fetched,
grouped::{ComputedVecsFromHeight, StorableVecGeneatorOptions}, grouped::{ComputedValueVecsFromHeight, ComputedVecsFromHeight, StorableVecGeneatorOptions},
indexes, transactions, indexes, transactions,
}; };
@@ -22,51 +26,77 @@ pub struct Vecs(Outputs<Vecs_>);
#[derive(Clone)] #[derive(Clone)]
pub struct Vecs_ { pub struct Vecs_ {
pub height_to_realized_cap: EagerVec<Height, Dollars>, pub height_to_realized_cap: Option<EagerVec<Height, Dollars>>,
pub indexes_to_realized_cap: ComputedVecsFromHeight<Dollars>, pub indexes_to_realized_cap: Option<ComputedVecsFromHeight<Dollars>>,
pub height_to_supply: EagerVec<Height, Sats>, pub height_to_supply: EagerVec<Height, Sats>,
pub indexes_to_supply: ComputedVecsFromHeight<Sats>, pub indexes_to_supply: ComputedValueVecsFromHeight,
pub height_to_unspendable_supply: EagerVec<Height, Sats>,
pub indexes_to_unspendable_supply: ComputedValueVecsFromHeight,
pub height_to_utxo_count: EagerVec<Height, StoredUsize>, pub height_to_utxo_count: EagerVec<Height, StoredUsize>,
pub indexes_to_utxo_count: ComputedVecsFromHeight<StoredUsize>, pub indexes_to_utxo_count: ComputedVecsFromHeight<StoredUsize>,
} }
const VERSION: Version = Version::ZERO; const VERSION: Version = Version::new(3);
impl Vecs { impl Vecs {
pub fn forced_import( pub fn forced_import(
path: &Path, path: &Path,
_computation: Computation, _computation: Computation,
compressed: Compressed, compressed: Compressed,
fetched: Option<&fetched::Vecs>,
) -> color_eyre::Result<Self> { ) -> color_eyre::Result<Self> {
let compute_dollars = fetched.is_some();
fs::create_dir_all(path)?; fs::create_dir_all(path)?;
Ok(Self(Outputs { Ok(Self(Outputs {
all: Vecs_ { all: Vecs_ {
height_to_realized_cap: EagerVec::forced_import( height_to_realized_cap: compute_dollars.then(|| {
&path.join("height_to_realized_cap"), EagerVec::forced_import(
VERSION + Version::ZERO, &path.join("height_to_realized_cap"),
compressed, VERSION + Version::ZERO,
)?, compressed,
indexes_to_realized_cap: ComputedVecsFromHeight::forced_import( )
path, .unwrap()
"realized_cap", }),
false, indexes_to_realized_cap: compute_dollars.then(|| {
VERSION + Version::ZERO, ComputedVecsFromHeight::forced_import(
compressed, path,
StorableVecGeneatorOptions::default().add_last(), "realized_cap",
)?, false,
VERSION + Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_last(),
)
.unwrap()
}),
height_to_supply: EagerVec::forced_import( height_to_supply: EagerVec::forced_import(
&path.join("height_to_supply"), &path.join("height_to_supply"),
VERSION + Version::ZERO, VERSION + Version::ZERO,
compressed, compressed,
)?, )?,
indexes_to_supply: ComputedVecsFromHeight::forced_import( indexes_to_supply: ComputedValueVecsFromHeight::forced_import(
path, path,
"supply", "supply",
false, false,
VERSION + Version::ZERO, VERSION + Version::ZERO,
compressed, compressed,
StorableVecGeneatorOptions::default().add_last(), StorableVecGeneatorOptions::default().add_last(),
compute_dollars,
)?,
height_to_unspendable_supply: EagerVec::forced_import(
&path.join("height_to_unspendable_supply"),
VERSION + Version::ZERO,
compressed,
)?,
indexes_to_unspendable_supply: ComputedValueVecsFromHeight::forced_import(
path,
"unspendable_supply",
false,
VERSION + Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_last(),
compute_dollars,
)?, )?,
height_to_utxo_count: EagerVec::forced_import( height_to_utxo_count: EagerVec::forced_import(
&path.join("height_to_utxo_count"), &path.join("height_to_utxo_count"),
@@ -90,6 +120,7 @@ impl Vecs {
indexer: &Indexer, indexer: &Indexer,
indexes: &indexes::Vecs, indexes: &indexes::Vecs,
transactions: &transactions::Vecs, transactions: &transactions::Vecs,
fetched: Option<&fetched::Vecs>,
starting_indexes: &Indexes, starting_indexes: &Indexes,
exit: &Exit, exit: &Exit,
) -> color_eyre::Result<()> { ) -> color_eyre::Result<()> {
@@ -97,12 +128,22 @@ impl Vecs {
let height_to_first_outputindex = &indexer_vecs.height_to_first_outputindex; let height_to_first_outputindex = &indexer_vecs.height_to_first_outputindex;
let height_to_first_inputindex = &indexer_vecs.height_to_first_inputindex; let height_to_first_inputindex = &indexer_vecs.height_to_first_inputindex;
let height_to_output_count = transactions.indexes_to_output_count.height.unwrap_last(); let height_to_output_count = transactions.indexes_to_output_count.height.unwrap_sum();
let height_to_input_count = transactions.indexes_to_input_count.height.unwrap_last(); let height_to_input_count = transactions.indexes_to_input_count.height.unwrap_sum();
let inputindex_to_outputindex = &indexer_vecs.inputindex_to_outputindex; let inputindex_to_outputindex = &indexer_vecs.inputindex_to_outputindex;
let outputindex_to_value = &indexer_vecs.outputindex_to_value; let outputindex_to_value = &indexer_vecs.outputindex_to_value;
let txindex_to_height = &indexes.txindex_to_height; let txindex_to_height = &indexes.txindex_to_height;
let outputindex_to_txindex = &indexes.outputindex_to_txindex; let outputindex_to_txindex = &indexes.outputindex_to_txindex;
let outputindex_to_outputtype = &indexer_vecs.outputindex_to_outputtype;
let height_to_close = &fetched
.as_ref()
.map(|fetched| &fetched.chainindexes_to_close.height);
let height_to_opreturn_count = &transactions
.indexes_to_opreturn_count
.height
.as_ref()
.unwrap()
.as_ref();
let mut height_to_first_outputindex_iter = height_to_first_outputindex.into_iter(); let mut height_to_first_outputindex_iter = height_to_first_outputindex.into_iter();
let mut height_to_first_inputindex_iter = height_to_first_inputindex.into_iter(); let mut height_to_first_inputindex_iter = height_to_first_inputindex.into_iter();
@@ -110,8 +151,12 @@ impl Vecs {
let mut height_to_input_count_iter = height_to_input_count.into_iter(); let mut height_to_input_count_iter = height_to_input_count.into_iter();
let mut inputindex_to_outputindex_iter = inputindex_to_outputindex.into_iter(); let mut inputindex_to_outputindex_iter = inputindex_to_outputindex.into_iter();
let mut outputindex_to_value_iter = outputindex_to_value.into_iter(); let mut outputindex_to_value_iter = outputindex_to_value.into_iter();
let mut outputindex_to_value_iter_2 = outputindex_to_value.into_iter();
let mut txindex_to_height_iter = txindex_to_height.into_iter(); let mut txindex_to_height_iter = txindex_to_height.into_iter();
let mut outputindex_to_txindex_iter = outputindex_to_txindex.into_iter(); let mut outputindex_to_txindex_iter = outputindex_to_txindex.into_iter();
let mut height_to_close_iter = height_to_close.as_ref().map(|v| v.into_iter());
let mut height_to_opreturn_count_iter = height_to_opreturn_count.into_iter();
let mut outputindex_to_outputtype_iter = outputindex_to_outputtype.into_iter();
let base_version = Version::ZERO let base_version = Version::ZERO
+ height_to_first_outputindex.version() + height_to_first_outputindex.version()
@@ -121,26 +166,40 @@ impl Vecs {
+ inputindex_to_outputindex.version() + inputindex_to_outputindex.version()
+ outputindex_to_value.version() + outputindex_to_value.version()
+ txindex_to_height.version() + txindex_to_height.version()
+ outputindex_to_txindex.version(); + outputindex_to_txindex.version()
+ height_to_opreturn_count.version()
+ outputindex_to_outputtype.version()
+ height_to_close
.as_ref()
.map_or(Version::ZERO, |v| v.version());
let height_to_realized_cap = &mut self.0.all.height_to_realized_cap; let mut height_to_realized_cap = self.0.all.height_to_realized_cap.as_mut();
let height_to_supply = &mut self.0.all.height_to_supply; let height_to_supply = &mut self.0.all.height_to_supply;
let height_to_unspendable_supply = &mut self.0.all.height_to_unspendable_supply;
let height_to_utxo_count = &mut self.0.all.height_to_utxo_count; let height_to_utxo_count = &mut self.0.all.height_to_utxo_count;
height_to_realized_cap.validate_computed_version_or_reset_file(
base_version + height_to_realized_cap.inner_version(),
)?;
height_to_supply.validate_computed_version_or_reset_file( height_to_supply.validate_computed_version_or_reset_file(
base_version + height_to_supply.inner_version(), base_version + height_to_supply.inner_version(),
)?; )?;
height_to_unspendable_supply.validate_computed_version_or_reset_file(
base_version + height_to_unspendable_supply.inner_version(),
)?;
height_to_utxo_count.validate_computed_version_or_reset_file( height_to_utxo_count.validate_computed_version_or_reset_file(
base_version + height_to_utxo_count.inner_version(), base_version + height_to_utxo_count.inner_version(),
)?; )?;
if let Some(height_to_realized_cap) = height_to_realized_cap.as_mut() {
height_to_realized_cap.validate_computed_version_or_reset_file(
base_version + height_to_realized_cap.inner_version(),
)?;
}
let starting_height = [ let starting_height = [
height_to_realized_cap.len(),
height_to_supply.len(), height_to_supply.len(),
height_to_unspendable_supply.len(),
height_to_utxo_count.len(), height_to_utxo_count.len(),
height_to_realized_cap
.as_ref()
.map_or(usize::MAX, |v| v.len()),
] ]
.into_iter() .into_iter()
.map(Height::from) .map(Height::from)
@@ -148,34 +207,212 @@ impl Vecs {
.unwrap() .unwrap()
.min(starting_indexes.height); .min(starting_indexes.height);
let mut states = CohortStates::default(); let mut state = CohortState::default();
if let Some(prev_height) = starting_height.checked_sub(Height::new(1)) { if let Some(prev_height) = starting_height.checked_sub(Height::new(1)) {
states.realized_cap = height_to_realized_cap state.supply = height_to_supply.into_iter().unwrap_get_inner(prev_height);
state.unspendable_supply = height_to_unspendable_supply
.into_iter() .into_iter()
.unwrap_get_inner(prev_height); .unwrap_get_inner(prev_height);
states.supply = height_to_supply.into_iter().unwrap_get_inner(prev_height); state.utxo_count = height_to_utxo_count
states.utxo_count = height_to_utxo_count
.into_iter() .into_iter()
.unwrap_get_inner(prev_height); .unwrap_get_inner(prev_height);
if let Some(height_to_realized_cap) = height_to_realized_cap.as_mut() {
state.realized_cap = height_to_realized_cap
.into_iter()
.unwrap_get_inner(prev_height);
}
} }
(starting_height.unwrap_to_usize()..height_to_first_outputindex_iter.len()) (starting_height.unwrap_to_usize()..height_to_first_outputindex_iter.len())
.map(Height::from) .map(Height::from)
.try_for_each(|height| -> color_eyre::Result<()> { .try_for_each(|height| -> color_eyre::Result<()> {
let first_outputindex = height_to_first_outputindex_iter.unwrap_get_inner(height); let first_outputindex = height_to_first_outputindex_iter
let first_inputindex = height_to_first_inputindex_iter.unwrap_get_inner(height); .unwrap_get_inner(height)
.unwrap_to_usize();
let first_inputindex = height_to_first_inputindex_iter
.unwrap_get_inner(height)
.unwrap_to_usize();
let output_count = height_to_output_count_iter.unwrap_get_inner(height); let output_count = height_to_output_count_iter.unwrap_get_inner(height);
let input_count = height_to_input_count_iter.unwrap_get_inner(height); let input_count = height_to_input_count_iter.unwrap_get_inner(height);
let opreturn_count = height_to_opreturn_count_iter.unwrap_get_inner(height);
let (sent_sats_dollars, (mut received_spendable, mut received_unspendable)) =
thread::scope(|s| {
// Skip coinbase
let sent_sats_dollars = s.spawn(|| {
(first_inputindex + 1..first_inputindex + *input_count)
.map(InputIndex::from)
.map(|inputindex| {
inputindex_to_outputindex_iter.unwrap_get_inner(inputindex)
})
.map(|outputindex| {
let value =
outputindex_to_value_iter.unwrap_get_inner(outputindex);
if let Some(height_to_close_iter) =
height_to_close_iter.as_mut()
{
let txindex = outputindex_to_txindex_iter
.unwrap_get_inner(outputindex);
let height =
txindex_to_height_iter.unwrap_get_inner(txindex);
let dollars =
*height_to_close_iter.unwrap_get_inner(height);
(value, dollars)
} else {
(value, Dollars::ZERO)
}
})
.collect::<Vec<_>>()
});
let received = s.spawn(|| {
let mut spendable = Sats::ZERO;
let mut unspendable = Sats::ZERO;
(first_outputindex..first_outputindex + *output_count)
.map(OutputIndex::from)
.for_each(|outputindex| {
let value =
outputindex_to_value_iter_2.unwrap_get_inner(outputindex);
if outputindex_to_outputtype_iter.unwrap_get_inner(outputindex)
== OutputType::OpReturn
{
unspendable += value
} else {
spendable += value
}
});
(spendable, unspendable)
});
(sent_sats_dollars.join().unwrap(), received.join().unwrap())
});
let (sent, realized_cap_destroyed) = sent_sats_dollars
.into_par_iter()
.map(|(sats, dollars)| (sats, dollars * Bitcoin::from(sats)))
.reduce(
|| (Sats::ZERO, Dollars::ZERO),
|acc, (sats, dollars)| (acc.0 + sats, acc.1 + dollars),
);
let utxos_created = *output_count - *opreturn_count;
// Three invalid coinbases which all have 1 output
let utxos_destroyed = if height == Height::new(0)
|| height == Height::new(91_842)
|| height == Height::new(91_880)
{
received_spendable -= Sats::FIFTY_BTC;
received_unspendable += Sats::FIFTY_BTC;
*input_count
} else {
*input_count - 1
};
state.supply -= sent;
state.supply += received_spendable;
state.unspendable_supply += received_unspendable;
*state.utxo_count += utxos_created;
*state.utxo_count -= utxos_destroyed;
if let Some(height_to_close_iter) = height_to_close_iter.as_mut() {
let received = received_spendable + received_unspendable;
let price = *height_to_close_iter.unwrap_get_inner(height);
let realized_cap_created = price * Bitcoin::from(received);
state.realized_cap = (state.realized_cap + realized_cap_created)
.checked_sub(realized_cap_destroyed)
.context("to work")
.inspect_err(|_| {
dbg!((
height,
state.realized_cap,
realized_cap_created,
realized_cap_destroyed
));
})
.unwrap();
}
height_to_supply.forced_push_at(height, state.supply, exit)?;
height_to_unspendable_supply.forced_push_at(
height,
state.unspendable_supply,
exit,
)?;
height_to_utxo_count.forced_push_at(height, state.utxo_count, exit)?;
if let Some(height_to_realized_cap) = height_to_realized_cap.as_mut() {
height_to_realized_cap.forced_push_at(height, state.realized_cap, exit)?;
}
Ok(()) Ok(())
})?; })?;
height_to_supply.safe_flush(exit)?;
height_to_unspendable_supply.safe_flush(exit)?;
height_to_utxo_count.safe_flush(exit)?;
if let Some(height_to_realized_cap) = height_to_realized_cap.as_mut() {
height_to_realized_cap.safe_flush(exit)?;
}
self.0.all.indexes_to_supply.compute_rest(
indexer,
indexes,
fetched,
starting_indexes,
exit,
Some(&self.0.all.height_to_supply),
)?;
self.0.all.indexes_to_unspendable_supply.compute_rest(
indexer,
indexes,
fetched,
starting_indexes,
exit,
Some(&self.0.all.height_to_unspendable_supply),
)?;
self.0.all.indexes_to_utxo_count.compute_rest(
indexes,
starting_indexes,
exit,
Some(&self.0.all.height_to_utxo_count),
)?;
if let Some(indexes_to_realized_cap) = self.0.all.indexes_to_realized_cap.as_mut() {
indexes_to_realized_cap.compute_rest(
indexes,
starting_indexes,
exit,
Some(self.0.all.height_to_realized_cap.as_ref().unwrap()),
)?;
}
Ok(()) Ok(())
} }
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> { pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
// [].concat() [
vec![] vec![
&self.all.height_to_supply as &dyn AnyCollectableVec,
&self.all.height_to_unspendable_supply,
&self.all.height_to_utxo_count,
],
self.all
.height_to_realized_cap
.as_ref()
.map_or(vec![], |v| vec![v as &dyn AnyCollectableVec]),
self.all.indexes_to_supply.vecs(),
self.all.indexes_to_unspendable_supply.vecs(),
self.all.indexes_to_utxo_count.vecs(),
self.all
.indexes_to_realized_cap
.as_ref()
.map_or(vec![], |v| v.vecs()),
]
.concat()
} }
} }

View File

@@ -3,6 +3,8 @@ use std::ops::{Add, Div, Mul};
use serde::Serialize; use serde::Serialize;
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout}; use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::CheckedSub;
use super::Dollars; use super::Dollars;
#[derive( #[derive(
@@ -88,3 +90,9 @@ impl Mul<usize> for Cents {
Self(self.0 * rhs as u64) Self(self.0 * rhs as u64)
} }
} }
impl CheckedSub for Cents {
fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.checked_sub(rhs.0).map(Cents::from)
}
}

View File

@@ -1,12 +1,14 @@
use std::{ use std::{
f64, f64,
ops::{Add, Div, Mul}, ops::{Add, AddAssign, Div, Mul},
}; };
use derive_deref::Deref; use derive_deref::Deref;
use serde::Serialize; use serde::Serialize;
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout}; use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::CheckedSub;
use super::{Bitcoin, Cents, Close, Sats, StoredF32, StoredF64}; use super::{Bitcoin, Cents, Close, Sats, StoredF32, StoredF64};
#[derive( #[derive(
@@ -170,3 +172,17 @@ impl From<Dollars> for u128 {
u128::from(Cents::from(value)) u128::from(Cents::from(value))
} }
} }
impl AddAssign for Dollars {
fn add_assign(&mut self, rhs: Self) {
*self = Dollars::from(Cents::from(*self) + Cents::from(rhs));
}
}
impl CheckedSub for Dollars {
fn checked_sub(self, rhs: Self) -> Option<Self> {
Cents::from(self)
.checked_sub(Cents::from(rhs))
.map(Dollars::from)
}
}

View File

@@ -28,10 +28,12 @@ use super::{Bitcoin, Cents, Dollars, Height};
)] )]
pub struct Sats(u64); pub struct Sats(u64);
#[allow(clippy::inconsistent_digit_grouping)]
impl Sats { impl Sats {
pub const ZERO: Self = Self(0); pub const ZERO: Self = Self(0);
pub const MAX: Self = Self(u64::MAX); pub const MAX: Self = Self(u64::MAX);
pub const ONE_BTC: Self = Self(100_000_000); pub const ONE_BTC: Self = Self(1_00_000_000);
pub const FIFTY_BTC: Self = Self(50_00_000_000);
pub fn is_zero(&self) -> bool { pub fn is_zero(&self) -> bool {
*self == Self::ZERO *self == Self::ZERO

View File

@@ -1,6 +1,6 @@
use std::ops::{Add, Div}; use std::ops::{Add, Div};
use derive_deref::Deref; use derive_deref::{Deref, DerefMut};
use serde::Serialize; use serde::Serialize;
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout}; use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
@@ -15,6 +15,7 @@ use super::{
#[derive( #[derive(
Debug, Debug,
Deref, Deref,
DerefMut,
Clone, Clone,
Default, Default,
Copy, Copy,

View File

@@ -3,7 +3,7 @@
#![doc = include_str!("../examples/main.rs")] #![doc = include_str!("../examples/main.rs")]
#![doc = "```"] #![doc = "```"]
use std::{collections::BTreeMap, fs, path::Path}; use std::{collections::BTreeMap, fs, path::Path, thread::sleep, time::Duration};
use brk_core::{Cents, Close, Date, Dollars, Height, High, Low, OHLCCents, Open, Timestamp}; use brk_core::{Cents, Close, Date, Dollars, Height, High, Low, OHLCCents, Open, Timestamp};
use color_eyre::eyre::Error; use color_eyre::eyre::Error;
@@ -50,6 +50,16 @@ impl Fetcher {
height: Height, height: Height,
timestamp: Timestamp, timestamp: Timestamp,
previous_timestamp: Option<Timestamp>, previous_timestamp: Option<Timestamp>,
) -> color_eyre::Result<OHLCCents> {
self.get_height_(height, timestamp, previous_timestamp, 0)
}
fn get_height_(
&mut self,
height: Height,
timestamp: Timestamp,
previous_timestamp: Option<Timestamp>,
tries: usize,
) -> color_eyre::Result<OHLCCents> { ) -> color_eyre::Result<OHLCCents> {
let timestamp = timestamp.floor_seconds(); let timestamp = timestamp.floor_seconds();
@@ -69,6 +79,14 @@ impl Fetcher {
.unwrap_or_else(|e| { .unwrap_or_else(|e| {
eprintln!("{e}"); eprintln!("{e}");
self.kibo.get_from_height(height).unwrap_or_else(|e| { self.kibo.get_from_height(height).unwrap_or_else(|e| {
sleep(Duration::from_secs(30));
if tries < 8 * 60 * 2 {
return self
.get_height_(height, timestamp, previous_timestamp, tries + 1)
.unwrap();
}
let date = Date::from(timestamp); let date = Date::from(timestamp);
eprintln!("{e}"); eprintln!("{e}");
panic!( panic!(

View File

@@ -270,8 +270,10 @@ export function init({
?.timeScale() ?.timeScale()
.subscribeVisibleLogicalRangeChange( .subscribeVisibleLogicalRangeChange(
utils.debounce((t) => { utils.debounce((t) => {
from.set(t.from); if (t) {
to.set(t.to); from.set(t.from);
to.set(t.to);
}
}), }),
); );

View File

@@ -739,7 +739,8 @@ function createUtils() {
id.includes("fee") || id.includes("fee") ||
id.includes("coinbase") || id.includes("coinbase") ||
id.includes("subsidy") || id.includes("subsidy") ||
id.endsWith("stack") id.endsWith("stack") ||
id.includes("supply")
) { ) {
unit = "Sats"; unit = "Sats";
} else if ( } else if (
@@ -752,7 +753,8 @@ function createUtils() {
id.includes("ath") || id.includes("ath") ||
id.includes("-sma") || id.includes("-sma") ||
id.endsWith("-price") || id.endsWith("-price") ||
id.startsWith("price-") id.startsWith("price-") ||
id.startsWith("realized-")
) { ) {
unit = "USD"; unit = "USD";
} else if (id.includes("count") || id.match(/v[1-3]/g)) { } else if (id.includes("count") || id.match(/v[1-3]/g)) {

View File

@@ -1103,7 +1103,7 @@ function createPartialOptions(colors) {
title: "Unspent Transaction Output Count", title: "Unspent Transaction Output Count",
bottom: [ bottom: [
createBaseSeries({ createBaseSeries({
key: "utxo-count-bis", key: "exact-utxo-count",
name: "total", name: "total",
}), }),
], ],
@@ -1335,6 +1335,67 @@ function createPartialOptions(colors) {
}, },
], ],
}, },
{
name: "UTXOs",
tree: [
{
name: "supply",
title: "Supply",
bottom: [
createBaseSeries({
key: "supply",
name: "Supply",
}),
createBaseSeries({
key: "supply-in-btc",
name: "Supply",
}),
createBaseSeries({
key: "supply-in-usd",
name: "Supply",
}),
],
},
{
name: "unspendable supply",
title: "Unspendable Supply",
bottom: [
createBaseSeries({
key: "unspendable-supply",
name: "Supply",
}),
createBaseSeries({
key: "unspendable-supply-in-btc",
name: "Supply",
}),
createBaseSeries({
key: "unspendable-supply-in-usd",
name: "Supply",
}),
],
},
{
name: "count",
title: "UTXO Count",
bottom: [
createBaseSeries({
key: "utxo-count",
name: "Count",
}),
],
},
{
name: "realized cap",
title: "Realized Capitalization",
bottom: [
createBaseSeries({
key: "realized-cap",
name: "Realized Cap",
}),
],
},
],
},
], ],
}, },
{ {

View File

@@ -654,6 +654,7 @@ export function createVecIdToIndexes() {
"emptyoutput-count-min": [DateIndex, DecadeIndex, DifficultyEpoch, MonthIndex, QuarterIndex, WeekIndex, YearIndex], "emptyoutput-count-min": [DateIndex, DecadeIndex, DifficultyEpoch, MonthIndex, QuarterIndex, WeekIndex, YearIndex],
"emptyoutput-count-sum": [DateIndex, DecadeIndex, DifficultyEpoch, MonthIndex, QuarterIndex, WeekIndex, YearIndex], "emptyoutput-count-sum": [DateIndex, DecadeIndex, DifficultyEpoch, MonthIndex, QuarterIndex, WeekIndex, YearIndex],
"emptyoutputindex": [EmptyOutputIndex], "emptyoutputindex": [EmptyOutputIndex],
"exact-utxo-count": [DateIndex, DecadeIndex, DifficultyEpoch, Height, MonthIndex, QuarterIndex, WeekIndex, YearIndex],
"fee": [TxIndex], "fee": [TxIndex],
"fee-10p": [Height], "fee-10p": [Height],
"fee-25p": [Height], "fee-25p": [Height],
@@ -892,6 +893,7 @@ export function createVecIdToIndexes() {
"price-8y-ago": [DateIndex, DecadeIndex, MonthIndex, QuarterIndex, WeekIndex, YearIndex], "price-8y-ago": [DateIndex, DecadeIndex, MonthIndex, QuarterIndex, WeekIndex, YearIndex],
"quarterindex": [MonthIndex, QuarterIndex], "quarterindex": [MonthIndex, QuarterIndex],
"rawlocktime": [TxIndex], "rawlocktime": [TxIndex],
"realized-cap": [DateIndex, DecadeIndex, DifficultyEpoch, Height, MonthIndex, QuarterIndex, WeekIndex, YearIndex],
"subsidy": [Height], "subsidy": [Height],
"subsidy-10p": [DateIndex], "subsidy-10p": [DateIndex],
"subsidy-25p": [DateIndex], "subsidy-25p": [DateIndex],
@@ -922,6 +924,9 @@ export function createVecIdToIndexes() {
"subsidy-median": [DateIndex], "subsidy-median": [DateIndex],
"subsidy-min": [DateIndex, DecadeIndex, DifficultyEpoch, MonthIndex, QuarterIndex, WeekIndex, YearIndex], "subsidy-min": [DateIndex, DecadeIndex, DifficultyEpoch, MonthIndex, QuarterIndex, WeekIndex, YearIndex],
"subsidy-sum": [DateIndex, DecadeIndex, DifficultyEpoch, MonthIndex, QuarterIndex, WeekIndex, YearIndex], "subsidy-sum": [DateIndex, DecadeIndex, DifficultyEpoch, MonthIndex, QuarterIndex, WeekIndex, YearIndex],
"supply": [DateIndex, DecadeIndex, DifficultyEpoch, Height, MonthIndex, QuarterIndex, WeekIndex, YearIndex],
"supply-in-btc": [DateIndex, DecadeIndex, DifficultyEpoch, Height, MonthIndex, QuarterIndex, WeekIndex, YearIndex],
"supply-in-usd": [DateIndex, DecadeIndex, DifficultyEpoch, Height, MonthIndex, QuarterIndex, WeekIndex, YearIndex],
"timestamp": [DateIndex, DecadeIndex, DifficultyEpoch, HalvingEpoch, Height, MonthIndex, QuarterIndex, WeekIndex, YearIndex], "timestamp": [DateIndex, DecadeIndex, DifficultyEpoch, HalvingEpoch, Height, MonthIndex, QuarterIndex, WeekIndex, YearIndex],
"timestamp-fixed": [Height], "timestamp-fixed": [Height],
"total-block-count": [DateIndex, DecadeIndex, DifficultyEpoch, Height, MonthIndex, QuarterIndex, WeekIndex, YearIndex], "total-block-count": [DateIndex, DecadeIndex, DifficultyEpoch, Height, MonthIndex, QuarterIndex, WeekIndex, YearIndex],
@@ -1003,7 +1008,10 @@ export function createVecIdToIndexes() {
"unknownoutput-count-min": [DateIndex, DecadeIndex, DifficultyEpoch, MonthIndex, QuarterIndex, WeekIndex, YearIndex], "unknownoutput-count-min": [DateIndex, DecadeIndex, DifficultyEpoch, MonthIndex, QuarterIndex, WeekIndex, YearIndex],
"unknownoutput-count-sum": [DateIndex, DecadeIndex, DifficultyEpoch, MonthIndex, QuarterIndex, WeekIndex, YearIndex], "unknownoutput-count-sum": [DateIndex, DecadeIndex, DifficultyEpoch, MonthIndex, QuarterIndex, WeekIndex, YearIndex],
"unknownoutputindex": [UnknownOutputIndex], "unknownoutputindex": [UnknownOutputIndex],
"utxo-count-bis": [DateIndex, DecadeIndex, DifficultyEpoch, Height, MonthIndex, QuarterIndex, WeekIndex, YearIndex], "unspendable-supply": [DateIndex, DecadeIndex, DifficultyEpoch, Height, MonthIndex, QuarterIndex, WeekIndex, YearIndex],
"unspendable-supply-in-btc": [DateIndex, DecadeIndex, DifficultyEpoch, Height, MonthIndex, QuarterIndex, WeekIndex, YearIndex],
"unspendable-supply-in-usd": [DateIndex, DecadeIndex, DifficultyEpoch, Height, MonthIndex, QuarterIndex, WeekIndex, YearIndex],
"utxo-count": [DateIndex, DecadeIndex, DifficultyEpoch, Height, MonthIndex, QuarterIndex, WeekIndex, YearIndex],
"value": [InputIndex, OutputIndex], "value": [InputIndex, OutputIndex],
"vbytes": [Height], "vbytes": [Height],
"vsize": [TxIndex], "vsize": [TxIndex],