git: reset

This commit is contained in:
k
2024-06-23 17:38:53 +02:00
commit a1a576d088
375 changed files with 40952 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
use std::{fmt::Debug, fs, io};
use bincode::{Decode, Encode};
use crate::io::{Binary, OUTPUTS_FOLDER_PATH};
// https://github.com/djkoloski/rust_serialization_benchmark
pub trait AnyState
where
Self: Debug + Encode + Decode,
{
fn name<'a>() -> &'a str;
fn create_dir_all() -> color_eyre::Result<(), io::Error> {
fs::create_dir_all(Self::folder_path())
}
fn folder_path() -> String {
format!("{OUTPUTS_FOLDER_PATH}/states")
}
fn full_path() -> String {
let name = Self::name();
let folder_path = Self::folder_path();
format!("{folder_path}/{name}.bin")
}
fn reset(&mut self) -> color_eyre::Result<(), io::Error> {
self.clear();
fs::remove_file(Self::full_path())
}
fn import() -> color_eyre::Result<Self> {
Self::create_dir_all()?;
Binary::import(&Self::full_path())
}
fn export(&self) -> color_eyre::Result<()> {
Binary::export(&Self::full_path(), self)
}
fn clear(&mut self);
}

View File

@@ -0,0 +1,411 @@
use allocative::Allocative;
use crate::{
states::{DurableStates, OneShotStates, PriceToValue, UnrealizedState},
structs::{LiquiditySplitResult, Price, SplitByLiquidity, WAmount},
};
#[derive(Default, Debug, Allocative)]
pub struct AddressCohortDurableStates {
pub address_count: usize,
pub split_durable_states: SplitByLiquidity<DurableStates>,
pub price_to_split_amount: PriceToValue<SplitByLiquidity<WAmount>>,
}
const ONE_THIRD: f64 = 0.33333333333;
// TODO: Clean that mess, move to a generic liquidity split and somehow support rest for non floats
impl AddressCohortDurableStates {
#[allow(clippy::too_many_arguments)]
pub fn increment(
&mut self,
amount: WAmount,
utxo_count: usize,
realized_cap: Price,
mean_price_paid: Price,
split_sat_amount_result: &LiquiditySplitResult,
split_utxo_count_result: &LiquiditySplitResult,
split_realized_cap_result: &LiquiditySplitResult,
) -> color_eyre::Result<()> {
self.address_count += 1;
self._crement(
amount,
utxo_count,
realized_cap,
mean_price_paid,
split_sat_amount_result,
split_utxo_count_result,
split_realized_cap_result,
true,
)
}
#[allow(clippy::too_many_arguments)]
pub fn decrement(
&mut self,
amount: WAmount,
utxo_count: usize,
realized_cap: Price,
mean_price_paid: Price,
split_sat_amount_result: &LiquiditySplitResult,
split_utxo_count_result: &LiquiditySplitResult,
split_realized_cap_result: &LiquiditySplitResult,
) -> color_eyre::Result<()> {
self.address_count -= 1;
self._crement(
amount,
utxo_count,
realized_cap,
mean_price_paid,
split_sat_amount_result,
split_utxo_count_result,
split_realized_cap_result,
false,
)
}
#[allow(clippy::too_many_arguments)]
pub fn _crement(
&mut self,
amount: WAmount,
utxo_count: usize,
realized_cap: Price,
mean_price_paid: Price,
split_sat_amount_result: &LiquiditySplitResult,
split_utxo_count_result: &LiquiditySplitResult,
split_realized_cap_result: &LiquiditySplitResult,
increment: bool,
) -> color_eyre::Result<()> {
if increment {
self.split_durable_states
.all
.increment(amount, utxo_count, realized_cap)
} else {
self.split_durable_states
.all
.decrement(amount, utxo_count, realized_cap)
}
.inspect_err(|report| {
dbg!(
report,
"split all failed",
split_sat_amount_result,
split_utxo_count_result
);
})?;
let illiquid_amount = split_sat_amount_result.illiquid.trunc();
let illiquid_amount_rest = split_sat_amount_result.illiquid - illiquid_amount;
let mut illiquid_amount = WAmount::from_sat(illiquid_amount as u64);
let mut illiquid_utxo_count = split_utxo_count_result.illiquid.trunc() as usize;
let illiquid_utxo_count_rest = split_utxo_count_result.illiquid.fract();
let mut illiquid_realized_cap =
Price::from_cent(split_realized_cap_result.illiquid.trunc() as u64);
let illiquid_realized_cap_rest = split_realized_cap_result.illiquid.fract();
let liquid_amount = split_sat_amount_result.liquid.trunc();
let liquid_amount_rest = split_sat_amount_result.liquid - liquid_amount;
let mut liquid_amount = WAmount::from_sat(liquid_amount as u64);
let mut liquid_utxo_count = split_utxo_count_result.liquid.trunc() as usize;
let liquid_utxo_count_rest = split_utxo_count_result.liquid.fract();
let mut liquid_realized_cap =
Price::from_cent(split_realized_cap_result.liquid.trunc() as u64);
let liquid_realized_cap_rest = split_realized_cap_result.liquid.fract();
let mut highly_liquid_amount = amount - illiquid_amount - liquid_amount;
let mut highly_liquid_utxo_count = utxo_count - illiquid_utxo_count - liquid_utxo_count;
let mut highly_liquid_realized_cap =
realized_cap - illiquid_realized_cap - liquid_realized_cap;
let amount_diff = amount - illiquid_amount - liquid_amount - highly_liquid_amount;
if amount_diff > WAmount::ZERO {
if illiquid_amount_rest >= ONE_THIRD && illiquid_amount_rest > liquid_amount_rest {
illiquid_amount += amount_diff;
} else if illiquid_amount_rest >= ONE_THIRD {
liquid_amount += amount_diff;
} else {
highly_liquid_amount += amount_diff;
}
}
let utxo_count_diff =
utxo_count - illiquid_utxo_count - liquid_utxo_count - highly_liquid_utxo_count;
if utxo_count_diff > 0 {
if illiquid_utxo_count_rest >= ONE_THIRD
&& illiquid_utxo_count_rest > liquid_utxo_count_rest
{
illiquid_utxo_count += utxo_count_diff;
} else if illiquid_utxo_count_rest >= ONE_THIRD {
liquid_utxo_count += utxo_count_diff;
} else {
highly_liquid_utxo_count += utxo_count_diff;
}
}
let realized_cap_diff =
realized_cap - illiquid_realized_cap - liquid_realized_cap - highly_liquid_realized_cap;
if realized_cap_diff > Price::ZERO {
if illiquid_realized_cap_rest >= ONE_THIRD
&& illiquid_realized_cap_rest > liquid_realized_cap_rest
{
illiquid_realized_cap += realized_cap_diff;
} else if illiquid_realized_cap_rest >= ONE_THIRD {
liquid_realized_cap += realized_cap_diff;
} else {
highly_liquid_realized_cap += realized_cap_diff;
}
}
let split_amount = SplitByLiquidity {
all: amount,
illiquid: illiquid_amount,
liquid: liquid_amount,
highly_liquid: highly_liquid_amount,
};
let split_utxo_count = SplitByLiquidity {
all: utxo_count,
illiquid: illiquid_utxo_count,
liquid: liquid_utxo_count,
highly_liquid: highly_liquid_utxo_count,
};
let split_realized_cap = SplitByLiquidity {
all: realized_cap,
illiquid: illiquid_realized_cap,
liquid: liquid_realized_cap,
highly_liquid: highly_liquid_realized_cap,
};
if increment {
self.price_to_split_amount
.increment(mean_price_paid, split_amount);
} else {
self.price_to_split_amount
.decrement(mean_price_paid, split_amount)
.inspect_err(|report| {
dbg!(
report,
"cents_to_split_amount decrement",
split_sat_amount_result,
split_utxo_count_result,
split_amount,
split_utxo_count,
split_realized_cap,
);
})?;
}
if increment {
self.split_durable_states.illiquid.increment(
illiquid_amount,
illiquid_utxo_count,
illiquid_realized_cap,
)
} else {
self.split_durable_states.illiquid.decrement(
illiquid_amount,
illiquid_utxo_count,
illiquid_realized_cap,
)
}
.inspect_err(|report| {
dbg!(
report,
"split illiquid failed",
split_sat_amount_result,
split_utxo_count_result,
split_amount,
split_utxo_count,
split_realized_cap,
);
})?;
if increment {
self.split_durable_states.liquid.increment(
liquid_amount,
liquid_utxo_count,
liquid_realized_cap,
)
} else {
self.split_durable_states.liquid.decrement(
liquid_amount,
liquid_utxo_count,
liquid_realized_cap,
)
}
.inspect_err(|report| {
dbg!(
report,
"split liquid failed",
split_sat_amount_result,
split_utxo_count_result,
split_amount,
split_utxo_count,
split_realized_cap,
);
})?;
if increment {
self.split_durable_states.highly_liquid.increment(
highly_liquid_amount,
highly_liquid_utxo_count,
highly_liquid_realized_cap,
)
} else {
self.split_durable_states.highly_liquid.decrement(
highly_liquid_amount,
highly_liquid_utxo_count,
highly_liquid_realized_cap,
)
}
.inspect_err(|report| {
dbg!(
report,
"split highly liquid failed",
split_sat_amount_result,
split_utxo_count_result,
split_amount,
split_utxo_count,
split_realized_cap,
);
})?;
Ok(())
}
pub fn compute_one_shot_states(
&self,
block_price: Price,
date_price: Option<Price>,
) -> SplitByLiquidity<OneShotStates> {
let mut one_shot_states: SplitByLiquidity<OneShotStates> = SplitByLiquidity::default();
if date_price.is_some() {
one_shot_states
.all
.unrealized_date_state
.replace(UnrealizedState::default());
one_shot_states
.illiquid
.unrealized_date_state
.replace(UnrealizedState::default());
one_shot_states
.liquid
.unrealized_date_state
.replace(UnrealizedState::default());
one_shot_states
.highly_liquid
.unrealized_date_state
.replace(UnrealizedState::default());
}
let all_supply = self.split_durable_states.all.supply_state.supply;
let illiquid_supply = self.split_durable_states.illiquid.supply_state.supply;
let liquid_supply = self.split_durable_states.liquid.supply_state.supply;
let highly_liquid_supply = self.split_durable_states.highly_liquid.supply_state.supply;
let one_shot_states_ref = &mut one_shot_states;
self.price_to_split_amount.iterate(
SplitByLiquidity {
all: all_supply,
illiquid: illiquid_supply,
liquid: liquid_supply,
highly_liquid: highly_liquid_supply,
},
|price_paid, split_amount| {
one_shot_states_ref.all.price_paid_state.iterate(
price_paid,
split_amount.all,
all_supply,
);
one_shot_states_ref.all.unrealized_block_state.iterate(
price_paid,
block_price,
split_amount.all,
);
if let Some(unrealized_date_state) =
one_shot_states_ref.all.unrealized_date_state.as_mut()
{
unrealized_date_state.iterate(
price_paid,
date_price.unwrap(),
split_amount.all,
);
}
if split_amount.illiquid > WAmount::ZERO {
one_shot_states_ref.illiquid.price_paid_state.iterate(
price_paid,
split_amount.illiquid,
illiquid_supply,
);
one_shot_states_ref.illiquid.unrealized_block_state.iterate(
price_paid,
block_price,
split_amount.illiquid,
);
if let Some(unrealized_date_state) =
one_shot_states_ref.illiquid.unrealized_date_state.as_mut()
{
unrealized_date_state.iterate(
price_paid,
date_price.unwrap(),
split_amount.illiquid,
);
}
}
if split_amount.liquid > WAmount::ZERO {
one_shot_states_ref.liquid.price_paid_state.iterate(
price_paid,
split_amount.liquid,
liquid_supply,
);
one_shot_states_ref.liquid.unrealized_block_state.iterate(
price_paid,
block_price,
split_amount.liquid,
);
if let Some(unrealized_date_state) =
one_shot_states_ref.liquid.unrealized_date_state.as_mut()
{
unrealized_date_state.iterate(
price_paid,
date_price.unwrap(),
split_amount.liquid,
);
}
}
if split_amount.highly_liquid > WAmount::ZERO {
one_shot_states_ref.highly_liquid.price_paid_state.iterate(
price_paid,
split_amount.highly_liquid,
highly_liquid_supply,
);
one_shot_states_ref
.highly_liquid
.unrealized_block_state
.iterate(price_paid, block_price, split_amount.highly_liquid);
if let Some(unrealized_date_state) = one_shot_states_ref
.highly_liquid
.unrealized_date_state
.as_mut()
{
unrealized_date_state.iterate(
price_paid,
date_price.unwrap(),
split_amount.highly_liquid,
);
}
}
},
);
one_shot_states
}
}

View File

@@ -0,0 +1,68 @@
use crate::structs::{AddressSize, AddressSplit, AddressType};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub enum AddressCohortId {
All,
Plankton,
Shrimp,
Crab,
Fish,
Shark,
Whale,
Humpback,
Megalodon,
P2PK,
P2PKH,
P2SH,
P2WPKH,
P2WSH,
P2TR,
}
impl AddressCohortId {
pub fn as_name(&self) -> Option<&str> {
match self {
Self::All => None,
Self::Plankton => Some("plankton"),
Self::Shrimp => Some("shrimp"),
Self::Crab => Some("crab"),
Self::Fish => Some("fish"),
Self::Shark => Some("shark"),
Self::Whale => Some("whale"),
Self::Humpback => Some("humpback"),
Self::Megalodon => Some("megalodon"),
Self::P2PK => Some("p2pk"),
Self::P2PKH => Some("p2pkh"),
Self::P2SH => Some("p2sh"),
Self::P2WPKH => Some("p2wpkh"),
Self::P2WSH => Some("p2wsh"),
Self::P2TR => Some("p2tr"),
}
}
pub fn as_split(&self) -> AddressSplit {
match self {
Self::All => AddressSplit::All,
Self::Plankton => AddressSplit::Size(AddressSize::Plankton),
Self::Shrimp => AddressSplit::Size(AddressSize::Shrimp),
Self::Crab => AddressSplit::Size(AddressSize::Crab),
Self::Fish => AddressSplit::Size(AddressSize::Fish),
Self::Shark => AddressSplit::Size(AddressSize::Shark),
Self::Whale => AddressSplit::Size(AddressSize::Whale),
Self::Humpback => AddressSplit::Size(AddressSize::Humpback),
Self::Megalodon => AddressSplit::Size(AddressSize::Megalodon),
Self::P2PK => AddressSplit::Type(AddressType::P2PK),
Self::P2PKH => AddressSplit::Type(AddressType::P2PKH),
Self::P2SH => AddressSplit::Type(AddressType::P2SH),
Self::P2WPKH => AddressSplit::Type(AddressType::P2WPKH),
Self::P2WSH => AddressSplit::Type(AddressType::P2WSH),
Self::P2TR => AddressSplit::Type(AddressType::P2TR),
}
}
}

View File

@@ -0,0 +1,143 @@
use allocative::Allocative;
use color_eyre::eyre::eyre;
use derive_deref::{Deref, DerefMut};
use rayon::prelude::*;
use crate::{
databases::AddressIndexToAddressData,
structs::{AddressData, AddressRealizedData, Price},
};
use super::{AddressCohortDurableStates, AddressCohortsOneShotStates, SplitByAddressCohort};
#[derive(Default, Deref, DerefMut, Allocative)]
pub struct AddressCohortsDurableStates(SplitByAddressCohort<AddressCohortDurableStates>);
impl AddressCohortsDurableStates {
pub fn init(address_index_to_address_data: &mut AddressIndexToAddressData) -> Self {
let mut s = Self::default();
// Paralize that, different s could be added together
address_index_to_address_data
.iter(&mut |(_, address_data)| s.increment(address_data).unwrap());
s
}
pub fn iterate(
&mut self,
address_realized_data: &AddressRealizedData,
current_address_data: &AddressData,
) -> color_eyre::Result<()> {
self.decrement(&address_realized_data.initial_address_data)
.inspect_err(|report| {
dbg!(report);
dbg!(address_realized_data, current_address_data);
dbg!("decrement initial address_data");
})?;
self.increment(current_address_data).inspect_err(|report| {
dbg!(report);
dbg!(address_realized_data, current_address_data);
dbg!("increment address_data");
})?;
Ok(())
}
/// Should always increment using current address data state
fn increment(&mut self, address_data: &AddressData) -> color_eyre::Result<()> {
self._crement(address_data, true)
}
/// Should always decrement using initial address data state
fn decrement(&mut self, address_data: &AddressData) -> color_eyre::Result<()> {
self._crement(address_data, false)
}
fn _crement(&mut self, address_data: &AddressData, increment: bool) -> color_eyre::Result<()> {
// No need to either insert or remove if empty
if address_data.is_empty() {
return Ok(());
}
let amount = address_data.amount;
let utxo_count = address_data.outputs_len as usize;
let realized_cap = address_data.realized_cap;
let mean_price_paid = address_data.realized_cap / amount;
let liquidity_classification = address_data.compute_liquidity_classification();
let split_sat_amount = liquidity_classification.split(amount.to_sat() as f64);
let split_utxo_count = liquidity_classification.split(utxo_count as f64);
let split_realized_cap = liquidity_classification.split(utxo_count as f64);
self.0
.iterate(address_data, |state: &mut AddressCohortDurableStates| {
if increment {
if let Err(report) = state.increment(
amount,
utxo_count,
realized_cap,
mean_price_paid,
&split_sat_amount,
&split_utxo_count,
&split_realized_cap,
) {
dbg!(
report.to_string(),
&state,
&address_data,
&liquidity_classification
);
return Err(eyre!("increment error"));
}
} else if let Err(report) = state.decrement(
amount,
utxo_count,
realized_cap,
mean_price_paid,
&split_sat_amount,
&split_utxo_count,
&split_realized_cap,
) {
dbg!(
report.to_string(),
&state,
&address_data,
&liquidity_classification
);
return Err(eyre!("decrement error"));
}
Ok(())
})?;
Ok(())
}
pub fn compute_one_shot_states(
&mut self,
block_price: Price,
date_price: Option<Price>,
) -> AddressCohortsOneShotStates {
let mut one_shot_states = AddressCohortsOneShotStates::default();
self.as_vec()
.into_par_iter()
.map(|(states, address_cohort_id)| {
(
address_cohort_id,
states.compute_one_shot_states(block_price, date_price),
)
})
.collect::<Vec<_>>()
.into_iter()
.for_each(|(address_cohort_id, states)| {
*one_shot_states.get_mut_from_id(&address_cohort_id) = states;
});
one_shot_states
}
}

View File

@@ -0,0 +1,48 @@
use derive_deref::{Deref, DerefMut};
use crate::{
states::InputState,
structs::{AddressRealizedData, LiquidityClassification, SplitByLiquidity, WAmount},
};
use super::SplitByAddressCohort;
#[derive(Deref, DerefMut, Default)]
pub struct AddressCohortsInputStates(SplitByAddressCohort<SplitByLiquidity<InputState>>);
impl AddressCohortsInputStates {
pub fn iterate_input(
&mut self,
realized_data: &AddressRealizedData,
liquidity_classification: &LiquidityClassification,
) -> color_eyre::Result<()> {
let count = realized_data.utxos_destroyed as f64;
let sent = realized_data.sent;
let split_count = liquidity_classification.split(count);
let split_volume = liquidity_classification.split(sent.to_sat() as f64);
let iterate = move |state: &mut SplitByLiquidity<InputState>| -> color_eyre::Result<()> {
state.all.iterate(count, sent);
state.illiquid.iterate(
split_count.illiquid,
WAmount::from_sat(split_volume.illiquid.round() as u64),
);
state.liquid.iterate(
split_count.liquid,
WAmount::from_sat(split_volume.liquid.round() as u64),
);
state.highly_liquid.iterate(
split_count.highly_liquid,
WAmount::from_sat(split_volume.highly_liquid.round() as u64),
);
Ok(())
};
self.iterate(&realized_data.initial_address_data, iterate)
}
}

View File

@@ -0,0 +1,8 @@
use derive_deref::{Deref, DerefMut};
use crate::{states::OneShotStates, structs::SplitByLiquidity};
use super::SplitByAddressCohort;
#[derive(Deref, DerefMut, Default)]
pub struct AddressCohortsOneShotStates(pub SplitByAddressCohort<SplitByLiquidity<OneShotStates>>);

View File

@@ -0,0 +1,48 @@
use derive_deref::{Deref, DerefMut};
use crate::{
states::OutputState,
structs::{AddressRealizedData, LiquidityClassification, SplitByLiquidity, WAmount},
};
use super::SplitByAddressCohort;
#[derive(Deref, DerefMut, Default)]
pub struct AddressCohortsOutputStates(SplitByAddressCohort<SplitByLiquidity<OutputState>>);
impl AddressCohortsOutputStates {
pub fn iterate_output(
&mut self,
realized_data: &AddressRealizedData,
liquidity_classification: &LiquidityClassification,
) -> color_eyre::Result<()> {
let count = realized_data.utxos_created as f64;
let volume = realized_data.received;
let split_count = liquidity_classification.split(count);
let split_volume = liquidity_classification.split(volume.to_sat() as f64);
let iterate = move |state: &mut SplitByLiquidity<OutputState>| -> color_eyre::Result<()> {
state.all.iterate(count, volume);
state.illiquid.iterate(
split_count.illiquid,
WAmount::from_sat(split_volume.illiquid.round() as u64),
);
state.liquid.iterate(
split_count.liquid,
WAmount::from_sat(split_volume.liquid.round() as u64),
);
state.highly_liquid.iterate(
split_count.highly_liquid,
WAmount::from_sat(split_volume.highly_liquid.round() as u64),
);
Ok(())
};
self.iterate(&realized_data.initial_address_data, iterate)
}
}

View File

@@ -0,0 +1,48 @@
use derive_deref::{Deref, DerefMut};
use crate::{
states::RealizedState,
structs::{AddressRealizedData, LiquidityClassification, Price, SplitByLiquidity},
};
use super::SplitByAddressCohort;
#[derive(Deref, DerefMut, Default)]
pub struct AddressCohortsRealizedStates(SplitByAddressCohort<SplitByLiquidity<RealizedState>>);
impl AddressCohortsRealizedStates {
pub fn iterate_realized(
&mut self,
realized_data: &AddressRealizedData,
liquidity_classification: &LiquidityClassification,
) -> color_eyre::Result<()> {
let profit = realized_data.profit;
let loss = realized_data.loss;
let split_profit = liquidity_classification.split(profit.to_cent() as f64);
let split_loss = liquidity_classification.split(loss.to_cent() as f64);
let iterate = move |state: &mut SplitByLiquidity<RealizedState>| -> color_eyre::Result<()> {
state.all.iterate(profit, loss);
state.illiquid.iterate(
Price::from_cent(split_profit.illiquid as u64),
Price::from_cent(split_loss.illiquid as u64),
);
state.liquid.iterate(
Price::from_cent(split_profit.liquid as u64),
Price::from_cent(split_loss.liquid as u64),
);
state.highly_liquid.iterate(
Price::from_cent(split_profit.highly_liquid as u64),
Price::from_cent(split_loss.highly_liquid as u64),
);
Ok(())
};
self.iterate(&realized_data.initial_address_data, iterate)
}
}

View File

@@ -0,0 +1,17 @@
mod cohort_durable_states;
mod cohort_id;
mod cohorts_durable_states;
mod cohorts_input_states;
mod cohorts_one_shot_states;
mod cohorts_output_states;
mod cohorts_realized_states;
mod split_by_address_cohort;
pub use cohort_durable_states::*;
pub use cohort_id::*;
pub use cohorts_durable_states::*;
pub use cohorts_input_states::*;
pub use cohorts_one_shot_states::*;
pub use cohorts_output_states::*;
pub use cohorts_realized_states::*;
pub use split_by_address_cohort::*;

View File

@@ -0,0 +1,177 @@
use allocative::Allocative;
use crate::structs::{AddressData, AddressSize, AddressSplit, AddressType};
use super::AddressCohortId;
#[derive(Default, Allocative)]
pub struct SplitByAddressCohort<T> {
pub all: T,
pub plankton: T,
pub shrimp: T,
pub crab: T,
pub fish: T,
pub shark: T,
pub whale: T,
pub humpback: T,
pub megalodon: T,
pub p2pk: T,
pub p2pkh: T,
pub p2sh: T,
pub p2wpkh: T,
pub p2wsh: T,
pub p2tr: T,
}
impl<T> SplitByAddressCohort<T> {
pub fn get(&self, split: &AddressSplit) -> Option<&T> {
match &split {
AddressSplit::All => Some(&self.all),
AddressSplit::Type(address_type) => match address_type {
AddressType::P2PK => Some(&self.p2pk),
AddressType::P2PKH => Some(&self.p2pkh),
AddressType::P2SH => Some(&self.p2sh),
AddressType::P2WPKH => Some(&self.p2wpkh),
AddressType::P2WSH => Some(&self.p2wsh),
AddressType::P2TR => Some(&self.p2tr),
AddressType::MultiSig => None,
AddressType::Unknown => None,
AddressType::OpReturn => None,
AddressType::PushOnly => None,
AddressType::Empty => None,
},
AddressSplit::Size(address_size) => match address_size {
AddressSize::Plankton => Some(&self.plankton),
AddressSize::Shrimp => Some(&self.shrimp),
AddressSize::Crab => Some(&self.crab),
AddressSize::Fish => Some(&self.fish),
AddressSize::Shark => Some(&self.shark),
AddressSize::Whale => Some(&self.whale),
AddressSize::Humpback => Some(&self.humpback),
AddressSize::Megalodon => Some(&self.megalodon),
AddressSize::Empty => None,
},
}
}
pub fn iterate(
&mut self,
address_data: &AddressData,
iterate: impl Fn(&mut T) -> color_eyre::Result<()>,
) -> color_eyre::Result<()> {
if let Some(state) = self.get_mut_from_split(&AddressSplit::All) {
iterate(state)?;
}
if let Some(state) = self.get_mut_from_split(&AddressSplit::Type(address_data.address_type))
{
iterate(state)?;
}
if let Some(state) = self.get_mut_from_split(&AddressSplit::Size(AddressSize::from_amount(
address_data.amount,
))) {
iterate(state)?;
}
Ok(())
}
fn get_mut_from_split(&mut self, split: &AddressSplit) -> Option<&mut T> {
match &split {
AddressSplit::All => Some(&mut self.all),
AddressSplit::Type(address_type) => match address_type {
AddressType::P2PK => Some(&mut self.p2pk),
AddressType::P2PKH => Some(&mut self.p2pkh),
AddressType::P2SH => Some(&mut self.p2sh),
AddressType::P2WPKH => Some(&mut self.p2wpkh),
AddressType::P2WSH => Some(&mut self.p2wsh),
AddressType::P2TR => Some(&mut self.p2tr),
AddressType::MultiSig => None,
AddressType::Unknown => None,
AddressType::OpReturn => None,
AddressType::PushOnly => None,
AddressType::Empty => None,
},
AddressSplit::Size(address_size) => match address_size {
AddressSize::Plankton => Some(&mut self.plankton),
AddressSize::Shrimp => Some(&mut self.shrimp),
AddressSize::Crab => Some(&mut self.crab),
AddressSize::Fish => Some(&mut self.fish),
AddressSize::Shark => Some(&mut self.shark),
AddressSize::Whale => Some(&mut self.whale),
AddressSize::Humpback => Some(&mut self.humpback),
AddressSize::Megalodon => Some(&mut self.megalodon),
AddressSize::Empty => None,
},
}
}
pub fn get_mut_from_id(&mut self, id: &AddressCohortId) -> &mut T {
match id {
AddressCohortId::All => &mut self.all,
AddressCohortId::Plankton => &mut self.plankton,
AddressCohortId::Shrimp => &mut self.shrimp,
AddressCohortId::Crab => &mut self.crab,
AddressCohortId::Fish => &mut self.fish,
AddressCohortId::Shark => &mut self.shark,
AddressCohortId::Whale => &mut self.whale,
AddressCohortId::Humpback => &mut self.humpback,
AddressCohortId::Megalodon => &mut self.megalodon,
AddressCohortId::P2PK => &mut self.p2pk,
AddressCohortId::P2PKH => &mut self.p2pkh,
AddressCohortId::P2SH => &mut self.p2sh,
AddressCohortId::P2WPKH => &mut self.p2wpkh,
AddressCohortId::P2WSH => &mut self.p2wsh,
AddressCohortId::P2TR => &mut self.p2tr,
}
}
pub fn as_vec(&self) -> Vec<(&T, AddressCohortId)> {
vec![
(&self.all, AddressCohortId::All),
(&self.plankton, AddressCohortId::Plankton),
(&self.shrimp, AddressCohortId::Shrimp),
(&self.crab, AddressCohortId::Crab),
(&self.fish, AddressCohortId::Fish),
(&self.shark, AddressCohortId::Shark),
(&self.whale, AddressCohortId::Whale),
(&self.humpback, AddressCohortId::Humpback),
(&self.megalodon, AddressCohortId::Megalodon),
(&self.p2pk, AddressCohortId::P2PK),
(&self.p2pkh, AddressCohortId::P2PKH),
(&self.p2sh, AddressCohortId::P2SH),
(&self.p2wpkh, AddressCohortId::P2WPKH),
(&self.p2wsh, AddressCohortId::P2WSH),
(&self.p2tr, AddressCohortId::P2TR),
]
}
pub fn as_mut_vec(&mut self) -> Vec<(&mut T, AddressCohortId)> {
vec![
(&mut self.all, AddressCohortId::All),
(&mut self.plankton, AddressCohortId::Plankton),
(&mut self.shrimp, AddressCohortId::Shrimp),
(&mut self.crab, AddressCohortId::Crab),
(&mut self.fish, AddressCohortId::Fish),
(&mut self.shark, AddressCohortId::Shark),
(&mut self.whale, AddressCohortId::Whale),
(&mut self.humpback, AddressCohortId::Humpback),
(&mut self.megalodon, AddressCohortId::Megalodon),
(&mut self.p2pk, AddressCohortId::P2PK),
(&mut self.p2pkh, AddressCohortId::P2PKH),
(&mut self.p2sh, AddressCohortId::P2SH),
(&mut self.p2wpkh, AddressCohortId::P2WPKH),
(&mut self.p2wsh, AddressCohortId::P2WSH),
(&mut self.p2tr, AddressCohortId::P2TR),
]
}
}

View File

@@ -0,0 +1,18 @@
use allocative::Allocative;
use crate::structs::Price;
#[derive(Debug, Default, Allocative)]
pub struct CapitalizationState {
pub realized_cap: Price,
}
impl CapitalizationState {
pub fn increment(&mut self, realized_cap: Price) {
self.realized_cap += realized_cap;
}
pub fn decrement(&mut self, realized_cap: Price) {
self.realized_cap -= realized_cap;
}
}

View File

@@ -0,0 +1,55 @@
use allocative::Allocative;
use color_eyre::eyre::eyre;
use crate::structs::{Price, WAmount};
use super::{CapitalizationState, SupplyState, UTXOState};
#[derive(Default, Debug, Allocative)]
pub struct DurableStates {
pub capitalization_state: CapitalizationState,
pub supply_state: SupplyState,
pub utxo_state: UTXOState,
}
impl DurableStates {
pub fn increment(
&mut self,
amount: WAmount,
utxo_count: usize,
realized_cap: Price,
) -> color_eyre::Result<()> {
if amount == WAmount::ZERO {
if utxo_count != 0 {
dbg!(amount, utxo_count);
return Err(eyre!("Shouldn't be possible"));
}
} else {
self.capitalization_state.increment(realized_cap);
self.supply_state.increment(amount);
self.utxo_state.increment(utxo_count);
}
Ok(())
}
pub fn decrement(
&mut self,
amount: WAmount,
utxo_count: usize,
realized_cap: Price,
) -> color_eyre::Result<()> {
if amount == WAmount::ZERO {
if utxo_count != 0 {
dbg!(amount, utxo_count);
unreachable!("Shouldn't be possible")
}
} else {
self.capitalization_state.decrement(realized_cap);
self.supply_state.decrement(amount)?;
self.utxo_state.decrement(utxo_count)?;
}
Ok(())
}
}

View File

@@ -0,0 +1,14 @@
use crate::structs::WAmount;
#[derive(Debug, Default)]
pub struct InputState {
pub count: f64,
pub volume: WAmount,
}
impl InputState {
pub fn iterate(&mut self, count: f64, volume: WAmount) {
self.count += count;
self.volume += volume;
}
}

View File

@@ -0,0 +1,23 @@
mod capitalization_state;
mod durable_states;
mod input_state;
mod one_shot_states;
mod output_state;
mod price_paid_state;
mod price_to_value;
mod realized_state;
mod supply_state;
mod unrealized_state;
mod utxo_state;
pub use capitalization_state::*;
pub use durable_states::*;
pub use input_state::*;
pub use one_shot_states::*;
pub use output_state::*;
pub use price_paid_state::*;
pub use price_to_value::*;
pub use realized_state::*;
pub use supply_state::*;
pub use unrealized_state::*;
pub use utxo_state::*;

View File

@@ -0,0 +1,9 @@
use super::{PricePaidState, UnrealizedState};
#[derive(Default)]
pub struct OneShotStates {
pub price_paid_state: PricePaidState,
pub unrealized_block_state: UnrealizedState,
pub unrealized_date_state: Option<UnrealizedState>,
}

View File

@@ -0,0 +1,14 @@
use crate::structs::WAmount;
#[derive(Debug, Default)]
pub struct OutputState {
pub count: f64,
pub volume: WAmount,
}
impl OutputState {
pub fn iterate(&mut self, count: f64, volume: WAmount) {
self.count += count;
self.volume += volume;
}
}

View File

@@ -0,0 +1,210 @@
use crate::structs::{Price, WAmount};
#[derive(Default, Debug)]
pub struct PricePaidState {
pub pp_05p: Option<Price>,
pub pp_10p: Option<Price>,
pub pp_15p: Option<Price>,
pub pp_20p: Option<Price>,
pub pp_25p: Option<Price>,
pub pp_30p: Option<Price>,
pub pp_35p: Option<Price>,
pub pp_40p: Option<Price>,
pub pp_45p: Option<Price>,
pub pp_median: Option<Price>,
pub pp_55p: Option<Price>,
pub pp_60p: Option<Price>,
pub pp_65p: Option<Price>,
pub pp_70p: Option<Price>,
pub pp_75p: Option<Price>,
pub pp_80p: Option<Price>,
pub pp_85p: Option<Price>,
pub pp_90p: Option<Price>,
pub pp_95p: Option<Price>,
pub processed_amount: WAmount,
}
impl PricePaidState {
pub fn iterate(&mut self, price: Price, amount: WAmount, total_supply: WAmount) {
let PricePaidState {
processed_amount,
pp_05p,
pp_10p,
pp_15p,
pp_20p,
pp_25p,
pp_30p,
pp_35p,
pp_40p,
pp_45p,
pp_median,
pp_55p,
pp_60p,
pp_65p,
pp_70p,
pp_75p,
pp_80p,
pp_85p,
pp_90p,
pp_95p,
} = self;
*processed_amount += amount;
if pp_95p.is_some() {
return;
}
let processed_sat_amount = processed_amount.to_sat();
let total_sat_supply = total_supply.to_sat();
if processed_sat_amount >= total_sat_supply * 95 / 100 {
pp_95p.replace(price);
}
if pp_90p.is_some() {
return;
}
if processed_sat_amount >= total_sat_supply * 90 / 100 {
pp_90p.replace(price);
}
if pp_85p.is_some() {
return;
}
if processed_sat_amount >= total_sat_supply * 85 / 100 {
pp_85p.replace(price);
}
if pp_80p.is_some() {
return;
}
if processed_sat_amount >= total_sat_supply * 80 / 100 {
pp_80p.replace(price);
}
if pp_75p.is_some() {
return;
}
if processed_sat_amount >= total_sat_supply * 75 / 100 {
pp_75p.replace(price);
}
if pp_70p.is_some() {
return;
}
if processed_sat_amount >= total_sat_supply * 70 / 100 {
pp_70p.replace(price);
}
if pp_65p.is_some() {
return;
}
if processed_sat_amount >= total_sat_supply * 65 / 100 {
pp_65p.replace(price);
}
if pp_60p.is_some() {
return;
}
if processed_sat_amount >= total_sat_supply * 60 / 100 {
pp_60p.replace(price);
}
if pp_55p.is_some() {
return;
}
if processed_sat_amount >= total_sat_supply * 55 / 100 {
pp_55p.replace(price);
}
if pp_median.is_some() {
return;
}
if processed_sat_amount >= total_sat_supply / 2 {
pp_median.replace(price);
}
if pp_45p.is_some() {
return;
}
if processed_sat_amount >= total_sat_supply * 45 / 100 {
pp_45p.replace(price);
}
if pp_40p.is_some() {
return;
}
if processed_sat_amount >= total_sat_supply * 40 / 100 {
pp_40p.replace(price);
}
if pp_35p.is_some() {
return;
}
if processed_sat_amount >= total_sat_supply * 35 / 100 {
pp_35p.replace(price);
}
if pp_30p.is_some() {
return;
}
if processed_sat_amount >= total_sat_supply * 30 / 100 {
pp_30p.replace(price);
}
if pp_25p.is_some() {
return;
}
if processed_sat_amount >= total_sat_supply / 4 {
pp_25p.replace(price);
}
if pp_20p.is_some() {
return;
}
if processed_sat_amount >= total_sat_supply / 5 {
pp_20p.replace(price);
}
if pp_15p.is_some() {
return;
}
if processed_sat_amount >= total_sat_supply * 15 / 100 {
pp_15p.replace(price);
}
if pp_10p.is_some() {
return;
}
if processed_sat_amount >= total_sat_supply / 10 {
pp_10p.replace(price);
}
if pp_05p.is_some() {
return;
}
if processed_sat_amount >= total_sat_supply / 20 {
pp_05p.replace(price);
}
}
}

View File

@@ -0,0 +1,123 @@
use std::{
collections::BTreeMap,
fmt::Debug,
ops::{AddAssign, SubAssign},
};
use allocative::Allocative;
use color_eyre::eyre::eyre;
use derive_deref::{Deref, DerefMut};
use crate::structs::{Price, SplitByLiquidity, WAmount};
#[derive(Deref, DerefMut, Default, Debug, Allocative)]
pub struct PriceToValue<T>(BTreeMap<u32, T>);
impl<T> PriceToValue<T>
where
T: Default
+ Debug
+ AddAssign
+ SubAssign
+ CanSubtract
+ Default
+ Copy
+ Clone
+ PartialEq
+ IsZero,
{
pub fn increment(&mut self, price: Price, value: T) {
*self.entry(price.to_cent() as u32).or_default() += value;
}
pub fn decrement(&mut self, price: Price, value: T) -> color_eyre::Result<()> {
let cent = price.to_cent() as u32;
let delete = {
let self_value = self.get_mut(&cent);
if self_value.is_none() {
dbg!(&self.0, price, value);
return Err(eyre!("self_value is none"));
}
let self_value = self_value.unwrap();
if !self_value.can_subtract(&value) {
dbg!(*self_value, &self.0, price, value);
return Err(eyre!("self value < value"));
}
*self_value -= value;
self_value.is_zero()?
};
if delete {
self.remove(&cent).unwrap();
}
Ok(())
}
pub fn iterate(&self, supply: T, mut iterate: impl FnMut(Price, T)) {
let mut processed = T::default();
self.iter().for_each(|(cent, value)| {
let value = *value;
processed += value;
iterate(Price::from_cent(*cent as u64), value)
});
if processed != supply {
dbg!(processed, supply);
panic!("processed_amount isn't equal to supply")
}
}
}
pub trait CanSubtract {
fn can_subtract(&self, other: &Self) -> bool;
}
impl CanSubtract for WAmount {
fn can_subtract(&self, other: &Self) -> bool {
self >= other
}
}
impl CanSubtract for SplitByLiquidity<WAmount> {
fn can_subtract(&self, other: &Self) -> bool {
self.all >= other.all
&& self.illiquid >= other.illiquid
&& self.liquid >= other.liquid
&& self.highly_liquid >= other.highly_liquid
}
}
pub trait IsZero {
fn is_zero(&self) -> color_eyre::Result<bool>;
}
impl IsZero for WAmount {
fn is_zero(&self) -> color_eyre::Result<bool> {
Ok(*self == WAmount::ZERO)
}
}
impl IsZero for SplitByLiquidity<WAmount> {
fn is_zero(&self) -> color_eyre::Result<bool> {
if self.all == WAmount::ZERO
&& (self.illiquid != WAmount::ZERO
|| self.liquid != WAmount::ZERO
|| self.highly_liquid != WAmount::ZERO)
{
dbg!(&self);
Err(eyre!("Bad split"))
} else {
Ok(self.all == WAmount::ZERO)
}
}
}

View File

@@ -0,0 +1,14 @@
use crate::structs::Price;
#[derive(Debug, Default)]
pub struct RealizedState {
pub realized_profit: Price,
pub realized_loss: Price,
}
impl RealizedState {
pub fn iterate(&mut self, realized_profit: Price, realized_loss: Price) {
self.realized_profit += realized_profit;
self.realized_loss += realized_loss;
}
}

View File

@@ -0,0 +1,27 @@
use allocative::Allocative;
use color_eyre::eyre::eyre;
use crate::structs::WAmount;
#[derive(Debug, Default, Allocative)]
pub struct SupplyState {
pub supply: WAmount,
}
impl SupplyState {
pub fn increment(&mut self, amount: WAmount) {
self.supply += amount;
}
pub fn decrement(&mut self, amount: WAmount) -> color_eyre::Result<()> {
if self.supply < amount {
dbg!(self.supply, amount);
return Err(eyre!("supply smaller than supply"));
}
self.supply -= amount;
Ok(())
}
}

View File

@@ -0,0 +1,38 @@
use std::{cmp::Ordering, ops::Add};
use crate::structs::{Price, WAmount};
#[derive(Debug, Default)]
pub struct UnrealizedState {
pub supply_in_profit: WAmount,
pub unrealized_profit: Price,
pub unrealized_loss: Price,
}
impl UnrealizedState {
#[inline]
pub fn iterate(&mut self, price_then: Price, price_now: Price, amount: WAmount) {
match price_then.cmp(&price_now) {
Ordering::Less => {
self.unrealized_profit += (price_now - price_then) * amount;
self.supply_in_profit += amount;
}
Ordering::Greater => {
self.unrealized_loss += (price_then - price_now) * amount;
}
Ordering::Equal => {}
}
}
}
impl Add<UnrealizedState> for UnrealizedState {
type Output = UnrealizedState;
fn add(self, other: UnrealizedState) -> UnrealizedState {
UnrealizedState {
supply_in_profit: self.supply_in_profit + other.supply_in_profit,
unrealized_profit: self.unrealized_profit + other.unrealized_profit,
unrealized_loss: self.unrealized_loss + other.unrealized_loss,
}
}
}

View File

@@ -0,0 +1,25 @@
use allocative::Allocative;
use color_eyre::eyre::eyre;
#[derive(Debug, Default, Allocative)]
pub struct UTXOState {
pub count: usize,
}
impl UTXOState {
pub fn increment(&mut self, utxo_count: usize) {
self.count += utxo_count;
}
pub fn decrement(&mut self, utxo_count: usize) -> color_eyre::Result<()> {
if self.count < utxo_count {
dbg!(self.count, utxo_count);
return Err(eyre!("self.count smaller than utxo_count"));
}
self.count -= utxo_count;
Ok(())
}
}

View File

@@ -0,0 +1,7 @@
mod address;
mod any;
mod utxo;
pub use address::*;
pub use any::*;
pub use utxo::*;

View File

@@ -0,0 +1,107 @@
use allocative::Allocative;
use crate::{
states::{DurableStates, OneShotStates, PriceToValue, UnrealizedState},
structs::{Price, WAmount},
};
#[derive(Default, Debug, Allocative)]
pub struct UTXOCohortDurableStates {
pub durable_states: DurableStates,
pub price_to_amount: PriceToValue<WAmount>,
}
impl UTXOCohortDurableStates {
pub fn increment(
&mut self,
amount: WAmount,
utxo_count: usize,
price: Price,
) -> color_eyre::Result<()> {
self._crement(amount, utxo_count, price, true)
}
pub fn decrement(
&mut self,
amount: WAmount,
utxo_count: usize,
price: Price,
) -> color_eyre::Result<()> {
self._crement(amount, utxo_count, price, false)
}
pub fn _crement(
&mut self,
amount: WAmount,
utxo_count: usize,
price: Price,
increment: bool,
) -> color_eyre::Result<()> {
let realized_cap = price * amount;
if increment {
self.durable_states
.increment(amount, utxo_count, realized_cap)
} else {
self.durable_states
.decrement(amount, utxo_count, realized_cap)
}
.inspect_err(|report| {
dbg!(report, "split all failed", amount, utxo_count);
})?;
let rounded_price = price.to_significant();
if increment {
self.price_to_amount.increment(rounded_price, amount);
} else {
self.price_to_amount
.decrement(rounded_price, amount)
.inspect_err(|report| {
dbg!(
report,
"cents_to_amount decrement failed",
amount,
utxo_count
);
})?;
}
Ok(())
}
pub fn compute_one_shot_states(
&self,
block_price: Price,
date_price: Option<Price>,
) -> OneShotStates {
let mut one_shot_states = OneShotStates::default();
if date_price.is_some() {
one_shot_states
.unrealized_date_state
.replace(UnrealizedState::default());
}
let supply = self.durable_states.supply_state.supply;
let one_shot_states_ref = &mut one_shot_states;
self.price_to_amount.iterate(supply, |price_paid, amount| {
one_shot_states_ref
.price_paid_state
.iterate(price_paid, amount, supply);
one_shot_states_ref
.unrealized_block_state
.iterate(price_paid, block_price, amount);
if let Some(unrealized_date_state) = one_shot_states_ref.unrealized_date_state.as_mut()
{
unrealized_date_state.iterate(price_paid, date_price.unwrap(), amount);
}
});
one_shot_states
}
}

View File

@@ -0,0 +1,32 @@
pub enum UTXOFilter {
To(u32),
FromTo { from: u32, to: u32 },
From(u32),
Year(u32),
}
impl UTXOCheck for UTXOFilter {
fn check(&self, days_old: &u32, year: &u32) -> bool {
match self {
UTXOFilter::From(from) => from <= days_old,
UTXOFilter::To(to) => to > days_old,
UTXOFilter::FromTo { from, to } => from <= days_old && to > days_old,
UTXOFilter::Year(_year) => _year == year,
}
}
fn check_days_old(&self, days_old: &u32) -> bool {
match self {
UTXOFilter::From(from) => from <= days_old,
UTXOFilter::To(to) => to > days_old,
UTXOFilter::FromTo { from, to } => from <= days_old && to > days_old,
UTXOFilter::Year(_) => unreachable!(),
}
}
}
pub trait UTXOCheck {
fn check(&self, days_old: &u32, year: &u32) -> bool;
fn check_days_old(&self, days_old: &u32) -> bool;
}

View File

@@ -0,0 +1,84 @@
use super::{SplitByUTXOCohort, UTXOFilter};
pub const UTXO_FILTERS: SplitByUTXOCohort<UTXOFilter> = SplitByUTXOCohort {
up_to_1d: UTXOFilter::To(1),
up_to_1w: UTXOFilter::To(7),
up_to_1m: UTXOFilter::To(30),
up_to_2m: UTXOFilter::To(2 * 30),
up_to_3m: UTXOFilter::To(3 * 30),
up_to_4m: UTXOFilter::To(4 * 30),
up_to_5m: UTXOFilter::To(5 * 30),
up_to_6m: UTXOFilter::To(6 * 30),
up_to_1y: UTXOFilter::To(365),
up_to_2y: UTXOFilter::To(2 * 365),
up_to_3y: UTXOFilter::To(3 * 365),
up_to_5y: UTXOFilter::To(5 * 365),
up_to_7y: UTXOFilter::To(7 * 365),
up_to_10y: UTXOFilter::To(10 * 365),
up_to_15y: UTXOFilter::To(15 * 365),
from_1d_to_1w: UTXOFilter::FromTo { from: 1, to: 7 },
from_1w_to_1m: UTXOFilter::FromTo { from: 7, to: 30 },
from_1m_to_3m: UTXOFilter::FromTo {
from: 30,
to: 3 * 30,
},
from_3m_to_6m: UTXOFilter::FromTo {
from: 3 * 30,
to: 6 * 30,
},
from_6m_to_1y: UTXOFilter::FromTo {
from: 6 * 30,
to: 365,
},
from_1y_to_2y: UTXOFilter::FromTo {
from: 365,
to: 2 * 365,
},
from_2y_to_3y: UTXOFilter::FromTo {
from: 2 * 365,
to: 3 * 365,
},
from_3y_to_5y: UTXOFilter::FromTo {
from: 3 * 365,
to: 5 * 365,
},
from_5y_to_7y: UTXOFilter::FromTo {
from: 5 * 365,
to: 7 * 365,
},
from_7y_to_10y: UTXOFilter::FromTo {
from: 7 * 365,
to: 10 * 365,
},
from_10y_to_15y: UTXOFilter::FromTo {
from: 10 * 365,
to: 15 * 365,
},
from_1y: UTXOFilter::From(365),
from_2y: UTXOFilter::From(2 * 365),
from_4y: UTXOFilter::From(4 * 365),
from_10y: UTXOFilter::From(10 * 365),
from_15y: UTXOFilter::From(15 * 365),
year_2009: UTXOFilter::Year(2009),
year_2010: UTXOFilter::Year(2010),
year_2011: UTXOFilter::Year(2011),
year_2012: UTXOFilter::Year(2012),
year_2013: UTXOFilter::Year(2013),
year_2014: UTXOFilter::Year(2014),
year_2015: UTXOFilter::Year(2015),
year_2016: UTXOFilter::Year(2016),
year_2017: UTXOFilter::Year(2017),
year_2018: UTXOFilter::Year(2018),
year_2019: UTXOFilter::Year(2019),
year_2020: UTXOFilter::Year(2020),
year_2021: UTXOFilter::Year(2021),
year_2022: UTXOFilter::Year(2022),
year_2023: UTXOFilter::Year(2023),
year_2024: UTXOFilter::Year(2024),
sth: UTXOFilter::To(155),
lth: UTXOFilter::From(155),
};

View File

@@ -0,0 +1,119 @@
use allocative::Allocative;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default, Allocative)]
pub enum UTXOCohortId {
#[default]
UpTo1d,
UpTo1w,
UpTo1m,
UpTo2m,
UpTo3m,
UpTo4m,
UpTo5m,
UpTo6m,
UpTo1y,
UpTo2y,
UpTo3y,
UpTo5y,
UpTo7y,
UpTo10y,
UpTo15y,
From1dTo1w,
From1wTo1m,
From1mTo3m,
From3mTo6m,
From6mTo1y,
From1yTo2y,
From2yTo3y,
From3yTo5y,
From5yTo7y,
From7yTo10y,
From10yTo15y,
From1y,
From2y,
From4y,
From10y,
From15y,
Year2009,
Year2010,
Year2011,
Year2012,
Year2013,
Year2014,
Year2015,
Year2016,
Year2017,
Year2018,
Year2019,
Year2020,
Year2021,
Year2022,
Year2023,
Year2024,
ShortTermHolders,
LongTermHolders,
}
impl UTXOCohortId {
pub fn name(&self) -> &str {
match self {
Self::UpTo1d => "up_to_1d",
Self::UpTo1w => "up_to_1w",
Self::UpTo1m => "up_to_1m",
Self::UpTo2m => "up_to_2m",
Self::UpTo3m => "up_to_3m",
Self::UpTo4m => "up_to_4m",
Self::UpTo5m => "up_to_5m",
Self::UpTo6m => "up_to_6m",
Self::UpTo1y => "up_to_1y",
Self::UpTo2y => "up_to_2y",
Self::UpTo3y => "up_to_3y",
Self::UpTo5y => "up_to_5y",
Self::UpTo7y => "up_to_7y",
Self::UpTo10y => "up_to_10y",
Self::UpTo15y => "up_to_15y",
Self::From1dTo1w => "from_1d_to_1w",
Self::From1wTo1m => "from_1w_to_1m",
Self::From1mTo3m => "from_1m_to_3m",
Self::From3mTo6m => "from_3m_to_6m",
Self::From6mTo1y => "from_6m_to_1y",
Self::From1yTo2y => "from_1y_to_2y",
Self::From2yTo3y => "from_2y_to_3y",
Self::From3yTo5y => "from_3y_to_5y",
Self::From5yTo7y => "from_5y_to_7y",
Self::From7yTo10y => "from_7y_to_10y",
Self::From10yTo15y => "from_10y_to_15y",
Self::From1y => "from_1y",
Self::From2y => "from_2y",
Self::From4y => "from_4y",
Self::From10y => "from_10y",
Self::From15y => "from_15y",
Self::Year2009 => "year_2009",
Self::Year2010 => "year_2010",
Self::Year2011 => "year_2011",
Self::Year2012 => "year_2012",
Self::Year2013 => "year_2013",
Self::Year2014 => "year_2014",
Self::Year2015 => "year_2015",
Self::Year2016 => "year_2016",
Self::Year2017 => "year_2017",
Self::Year2018 => "year_2018",
Self::Year2019 => "year_2019",
Self::Year2020 => "year_2020",
Self::Year2021 => "year_2021",
Self::Year2022 => "year_2022",
Self::Year2023 => "year_2023",
Self::Year2024 => "year_2024",
Self::ShortTermHolders => "sth",
Self::LongTermHolders => "lth",
}
}
}

View File

@@ -0,0 +1,154 @@
use allocative::Allocative;
use chrono::Datelike;
use derive_deref::{Deref, DerefMut};
use rayon::prelude::*;
use crate::{
states::DateDataVec,
structs::{BlockData, Price, SentData, WAmount},
utils::difference_in_days_between_timestamps,
WNaiveDate,
};
use super::{SplitByUTXOCohort, UTXOCohortDurableStates, UTXOCohortsOneShotStates};
#[derive(Default, Deref, DerefMut, Allocative)]
pub struct UTXOCohortsDurableStates(SplitByUTXOCohort<UTXOCohortDurableStates>);
impl UTXOCohortsDurableStates {
pub fn init(date_data_vec: &DateDataVec) -> Self {
let mut s = Self::default();
if let Some(last_date_data) = date_data_vec.last() {
let last_block_data = last_date_data.blocks.last().unwrap();
date_data_vec.iter().for_each(|date_data| {
let year = date_data.date.year() as u32;
date_data.blocks.iter().for_each(|block_data| {
let amount = block_data.amount;
let utxo_count = block_data.utxos as usize;
// No need to either insert or remove if 0
if amount == WAmount::ZERO {
return;
}
let increment_days_old = difference_in_days_between_timestamps(
block_data.timestamp,
last_block_data.timestamp,
);
s.initial_filtered_apply(&increment_days_old, &year, |state| {
state
.increment(amount, utxo_count, block_data.price)
.unwrap();
});
})
});
}
s
}
pub fn udpate_age_if_needed(
&mut self,
block_data: &BlockData,
last_block_data: &BlockData,
previous_last_block_data: Option<&BlockData>,
) {
let amount = block_data.amount;
let utxo_count = block_data.utxos as usize;
let price = block_data.price;
// No need to either insert or remove if 0
if amount == WAmount::ZERO {
return;
}
if block_data.height == last_block_data.height {
let year = WNaiveDate::from_timestamp(block_data.timestamp).year() as u32;
self.initial_filtered_apply(&0, &year, |state| {
state.increment(amount, utxo_count, price).unwrap();
})
} else {
let increment_days_old = difference_in_days_between_timestamps(
block_data.timestamp,
last_block_data.timestamp,
);
let decrement_days_old = difference_in_days_between_timestamps(
block_data.timestamp,
previous_last_block_data
.unwrap_or_else(|| {
dbg!(block_data, last_block_data, previous_last_block_data);
panic!()
})
.timestamp,
);
if increment_days_old == decrement_days_old {
return;
}
self.duo_filtered_apply(
&increment_days_old,
&decrement_days_old,
|state| {
state.increment(amount, utxo_count, price).unwrap();
},
|state| {
state.decrement(amount, utxo_count, price).unwrap();
},
);
}
}
pub fn subtract_moved(
&mut self,
block_data: &BlockData,
sent_data: &SentData,
previous_last_block_data: &BlockData,
) {
let amount = sent_data.volume;
let utxo_count = sent_data.count as usize;
// No need to either insert or remove if 0
if amount == WAmount::ZERO {
return;
}
let days_old = difference_in_days_between_timestamps(
block_data.timestamp,
previous_last_block_data.timestamp,
);
let year = WNaiveDate::from_timestamp(block_data.timestamp).year() as u32;
self.initial_filtered_apply(&days_old, &year, |state| {
state
.decrement(amount, utxo_count, block_data.price)
.unwrap();
})
}
pub fn compute_one_shot_states(
&mut self,
block_price: Price,
date_price: Option<Price>,
) -> UTXOCohortsOneShotStates {
let mut one_shot_states = UTXOCohortsOneShotStates::default();
self.as_vec()
.into_par_iter()
.map(|(states, id)| (states.compute_one_shot_states(block_price, date_price), id))
.collect::<Vec<_>>()
.into_iter()
.for_each(|(states, id)| {
*one_shot_states.get_mut(&id) = states;
});
one_shot_states
}
}

View File

@@ -0,0 +1,8 @@
use derive_deref::{Deref, DerefMut};
use crate::states::OneShotStates;
use super::SplitByUTXOCohort;
#[derive(Deref, DerefMut, Default)]
pub struct UTXOCohortsOneShotStates(pub SplitByUTXOCohort<OneShotStates>);

View File

@@ -0,0 +1,68 @@
use std::{cmp::Ordering, collections::BTreeMap};
use chrono::Datelike;
use derive_deref::{Deref, DerefMut};
use crate::{
states::{DateDataVec, InputState, RealizedState},
structs::{BlockPath, Price, SentData},
utils::difference_in_days_between_timestamps,
};
use super::SplitByUTXOCohort;
#[derive(Default, Debug)]
pub struct SentState {
pub input: InputState,
pub realized: RealizedState,
}
#[derive(Deref, DerefMut, Default)]
pub struct UTXOCohortsSentStates(SplitByUTXOCohort<SentState>);
impl UTXOCohortsSentStates {
pub fn compute(
&mut self,
date_data_vec: &DateDataVec,
block_path_to_sent_data: &BTreeMap<BlockPath, SentData>,
current_price: Price,
) {
if let Some(last_block_data) = date_data_vec.last_block() {
block_path_to_sent_data
.iter()
.for_each(|(block_path, sent_data)| {
let date_data = date_data_vec.get_date_data(block_path).unwrap();
let year = date_data.date.year() as u32;
let block_data = date_data.get_block_data(block_path).unwrap();
let days_old = difference_in_days_between_timestamps(
block_data.timestamp,
last_block_data.timestamp,
);
let previous_price = block_data.price;
let amount_sent = sent_data.volume;
self.initial_filtered_apply(&days_old, &year, |state| {
state.input.iterate(sent_data.count as f64, amount_sent);
let previous_value = previous_price * amount_sent;
let current_value = current_price * amount_sent;
match previous_value.cmp(&current_value) {
Ordering::Less => {
state.realized.realized_profit += current_value - previous_value;
}
Ordering::Greater => {
state.realized.realized_loss += previous_value - current_value;
}
Ordering::Equal => {}
}
})
})
}
}
}

View File

@@ -0,0 +1,17 @@
mod cohort_durable_states;
mod cohort_filter;
mod cohort_filters;
mod cohort_id;
mod cohorts_durable_states;
mod cohorts_one_shot_states;
mod cohorts_sent_states;
mod split_by_utxo_cohort;
pub use cohort_durable_states::*;
pub use cohort_filter::*;
pub use cohort_filters::*;
pub use cohort_id::*;
pub use cohorts_durable_states::*;
pub use cohorts_one_shot_states::*;
pub use cohorts_sent_states::*;
pub use split_by_utxo_cohort::*;

View File

@@ -0,0 +1,740 @@
use allocative::Allocative;
use super::{UTXOCheck, UTXOCohortId, UTXO_FILTERS};
#[derive(Default, Allocative)]
pub struct SplitByUTXOCohort<T> {
pub sth: T,
pub lth: T,
pub up_to_1d: T,
pub up_to_1w: T,
pub up_to_1m: T,
pub up_to_2m: T,
pub up_to_3m: T,
pub up_to_4m: T,
pub up_to_5m: T,
pub up_to_6m: T,
pub up_to_1y: T,
pub up_to_2y: T,
pub up_to_3y: T,
pub up_to_5y: T,
pub up_to_7y: T,
pub up_to_10y: T,
pub up_to_15y: T,
pub from_1d_to_1w: T,
pub from_1w_to_1m: T,
pub from_1m_to_3m: T,
pub from_3m_to_6m: T,
pub from_6m_to_1y: T,
pub from_1y_to_2y: T,
pub from_2y_to_3y: T,
pub from_3y_to_5y: T,
pub from_5y_to_7y: T,
pub from_7y_to_10y: T,
pub from_10y_to_15y: T,
pub from_1y: T,
pub from_2y: T,
pub from_4y: T,
pub from_10y: T,
pub from_15y: T,
pub year_2009: T,
pub year_2010: T,
pub year_2011: T,
pub year_2012: T,
pub year_2013: T,
pub year_2014: T,
pub year_2015: T,
pub year_2016: T,
pub year_2017: T,
pub year_2018: T,
pub year_2019: T,
pub year_2020: T,
pub year_2021: T,
pub year_2022: T,
pub year_2023: T,
pub year_2024: T,
}
impl<T> SplitByUTXOCohort<T> {
pub fn get(&self, id: &UTXOCohortId) -> &T {
match id {
UTXOCohortId::UpTo1d => &self.up_to_1d,
UTXOCohortId::UpTo1w => &self.up_to_1w,
UTXOCohortId::UpTo1m => &self.up_to_1m,
UTXOCohortId::UpTo2m => &self.up_to_2m,
UTXOCohortId::UpTo3m => &self.up_to_3m,
UTXOCohortId::UpTo4m => &self.up_to_4m,
UTXOCohortId::UpTo5m => &self.up_to_5m,
UTXOCohortId::UpTo6m => &self.up_to_6m,
UTXOCohortId::UpTo1y => &self.up_to_1y,
UTXOCohortId::UpTo2y => &self.up_to_2y,
UTXOCohortId::UpTo3y => &self.up_to_3y,
UTXOCohortId::UpTo5y => &self.up_to_5y,
UTXOCohortId::UpTo7y => &self.up_to_7y,
UTXOCohortId::UpTo10y => &self.up_to_10y,
UTXOCohortId::UpTo15y => &self.up_to_15y,
UTXOCohortId::From1dTo1w => &self.from_1d_to_1w,
UTXOCohortId::From1wTo1m => &self.from_1w_to_1m,
UTXOCohortId::From1mTo3m => &self.from_1m_to_3m,
UTXOCohortId::From3mTo6m => &self.from_3m_to_6m,
UTXOCohortId::From6mTo1y => &self.from_6m_to_1y,
UTXOCohortId::From1yTo2y => &self.from_1y_to_2y,
UTXOCohortId::From2yTo3y => &self.from_2y_to_3y,
UTXOCohortId::From3yTo5y => &self.from_3y_to_5y,
UTXOCohortId::From5yTo7y => &self.from_5y_to_7y,
UTXOCohortId::From7yTo10y => &self.from_7y_to_10y,
UTXOCohortId::From10yTo15y => &self.from_10y_to_15y,
UTXOCohortId::From1y => &self.from_1y,
UTXOCohortId::From2y => &self.from_2y,
UTXOCohortId::From4y => &self.from_4y,
UTXOCohortId::From10y => &self.from_10y,
UTXOCohortId::From15y => &self.from_15y,
UTXOCohortId::Year2009 => &self.year_2009,
UTXOCohortId::Year2010 => &self.year_2010,
UTXOCohortId::Year2011 => &self.year_2011,
UTXOCohortId::Year2012 => &self.year_2012,
UTXOCohortId::Year2013 => &self.year_2013,
UTXOCohortId::Year2014 => &self.year_2014,
UTXOCohortId::Year2015 => &self.year_2015,
UTXOCohortId::Year2016 => &self.year_2016,
UTXOCohortId::Year2017 => &self.year_2017,
UTXOCohortId::Year2018 => &self.year_2018,
UTXOCohortId::Year2019 => &self.year_2019,
UTXOCohortId::Year2020 => &self.year_2020,
UTXOCohortId::Year2021 => &self.year_2021,
UTXOCohortId::Year2022 => &self.year_2022,
UTXOCohortId::Year2023 => &self.year_2023,
UTXOCohortId::Year2024 => &self.year_2024,
UTXOCohortId::ShortTermHolders => &self.sth,
UTXOCohortId::LongTermHolders => &self.lth,
}
}
pub fn get_mut(&mut self, id: &UTXOCohortId) -> &mut T {
match id {
UTXOCohortId::UpTo1d => &mut self.up_to_1d,
UTXOCohortId::UpTo1w => &mut self.up_to_1w,
UTXOCohortId::UpTo1m => &mut self.up_to_1m,
UTXOCohortId::UpTo2m => &mut self.up_to_2m,
UTXOCohortId::UpTo3m => &mut self.up_to_3m,
UTXOCohortId::UpTo4m => &mut self.up_to_4m,
UTXOCohortId::UpTo5m => &mut self.up_to_5m,
UTXOCohortId::UpTo6m => &mut self.up_to_6m,
UTXOCohortId::UpTo1y => &mut self.up_to_1y,
UTXOCohortId::UpTo2y => &mut self.up_to_2y,
UTXOCohortId::UpTo3y => &mut self.up_to_3y,
UTXOCohortId::UpTo5y => &mut self.up_to_5y,
UTXOCohortId::UpTo7y => &mut self.up_to_7y,
UTXOCohortId::UpTo10y => &mut self.up_to_10y,
UTXOCohortId::UpTo15y => &mut self.up_to_15y,
UTXOCohortId::From1dTo1w => &mut self.from_1d_to_1w,
UTXOCohortId::From1wTo1m => &mut self.from_1w_to_1m,
UTXOCohortId::From1mTo3m => &mut self.from_1m_to_3m,
UTXOCohortId::From3mTo6m => &mut self.from_3m_to_6m,
UTXOCohortId::From6mTo1y => &mut self.from_6m_to_1y,
UTXOCohortId::From1yTo2y => &mut self.from_1y_to_2y,
UTXOCohortId::From2yTo3y => &mut self.from_2y_to_3y,
UTXOCohortId::From3yTo5y => &mut self.from_3y_to_5y,
UTXOCohortId::From5yTo7y => &mut self.from_5y_to_7y,
UTXOCohortId::From7yTo10y => &mut self.from_7y_to_10y,
UTXOCohortId::From10yTo15y => &mut self.from_10y_to_15y,
UTXOCohortId::From1y => &mut self.from_1y,
UTXOCohortId::From2y => &mut self.from_2y,
UTXOCohortId::From4y => &mut self.from_4y,
UTXOCohortId::From10y => &mut self.from_10y,
UTXOCohortId::From15y => &mut self.from_15y,
UTXOCohortId::Year2009 => &mut self.year_2009,
UTXOCohortId::Year2010 => &mut self.year_2010,
UTXOCohortId::Year2011 => &mut self.year_2011,
UTXOCohortId::Year2012 => &mut self.year_2012,
UTXOCohortId::Year2013 => &mut self.year_2013,
UTXOCohortId::Year2014 => &mut self.year_2014,
UTXOCohortId::Year2015 => &mut self.year_2015,
UTXOCohortId::Year2016 => &mut self.year_2016,
UTXOCohortId::Year2017 => &mut self.year_2017,
UTXOCohortId::Year2018 => &mut self.year_2018,
UTXOCohortId::Year2019 => &mut self.year_2019,
UTXOCohortId::Year2020 => &mut self.year_2020,
UTXOCohortId::Year2021 => &mut self.year_2021,
UTXOCohortId::Year2022 => &mut self.year_2022,
UTXOCohortId::Year2023 => &mut self.year_2023,
UTXOCohortId::Year2024 => &mut self.year_2024,
UTXOCohortId::ShortTermHolders => &mut self.sth,
UTXOCohortId::LongTermHolders => &mut self.lth,
}
}
/// Excluding years since they're static
pub fn duo_filtered_apply(
&mut self,
current_days_old: &u32,
previous_days_old: &u32,
apply_if_current_only: impl Fn(&mut T),
apply_if_previous_only: impl Fn(&mut T),
) {
let is_up_to_1d = UTXO_FILTERS.up_to_1d.check_days_old(current_days_old);
let was_up_to_1d = UTXO_FILTERS.up_to_1d.check_days_old(previous_days_old);
if is_up_to_1d && !was_up_to_1d {
apply_if_current_only(&mut self.up_to_1d);
} else if was_up_to_1d && !is_up_to_1d {
apply_if_previous_only(&mut self.up_to_1d);
}
let is_up_to_1w = UTXO_FILTERS.up_to_1w.check_days_old(current_days_old);
let was_up_to_1w = UTXO_FILTERS.up_to_1w.check_days_old(previous_days_old);
if is_up_to_1w && !was_up_to_1w {
apply_if_current_only(&mut self.up_to_1w);
} else if was_up_to_1w && !is_up_to_1w {
apply_if_previous_only(&mut self.up_to_1w);
}
let is_up_to_1m = UTXO_FILTERS.up_to_1m.check_days_old(current_days_old);
let was_up_to_1m = UTXO_FILTERS.up_to_1m.check_days_old(previous_days_old);
if is_up_to_1m && !was_up_to_1m {
apply_if_current_only(&mut self.up_to_1m);
} else if was_up_to_1m && !is_up_to_1m {
apply_if_previous_only(&mut self.up_to_1m);
}
let is_up_to_2m = UTXO_FILTERS.up_to_2m.check_days_old(current_days_old);
let was_up_to_2m = UTXO_FILTERS.up_to_2m.check_days_old(previous_days_old);
if is_up_to_2m && !was_up_to_2m {
apply_if_current_only(&mut self.up_to_2m);
} else if was_up_to_2m && !is_up_to_2m {
apply_if_previous_only(&mut self.up_to_2m);
}
let is_up_to_3m = UTXO_FILTERS.up_to_3m.check_days_old(current_days_old);
let was_up_to_3m = UTXO_FILTERS.up_to_3m.check_days_old(previous_days_old);
if is_up_to_3m && !was_up_to_3m {
apply_if_current_only(&mut self.up_to_3m);
} else if was_up_to_3m && !is_up_to_3m {
apply_if_previous_only(&mut self.up_to_3m);
}
let is_up_to_4m = UTXO_FILTERS.up_to_4m.check_days_old(current_days_old);
let was_up_to_4m = UTXO_FILTERS.up_to_4m.check_days_old(previous_days_old);
if is_up_to_4m && !was_up_to_4m {
apply_if_current_only(&mut self.up_to_4m);
} else if was_up_to_4m && !is_up_to_4m {
apply_if_previous_only(&mut self.up_to_4m);
}
let is_up_to_5m = UTXO_FILTERS.up_to_5m.check_days_old(current_days_old);
let was_up_to_5m = UTXO_FILTERS.up_to_5m.check_days_old(previous_days_old);
if is_up_to_5m && !was_up_to_5m {
apply_if_current_only(&mut self.up_to_5m);
} else if was_up_to_5m && !is_up_to_5m {
apply_if_previous_only(&mut self.up_to_5m);
}
let is_up_to_6m = UTXO_FILTERS.up_to_6m.check_days_old(current_days_old);
let was_up_to_6m = UTXO_FILTERS.up_to_6m.check_days_old(previous_days_old);
if is_up_to_6m && !was_up_to_6m {
apply_if_current_only(&mut self.up_to_6m);
} else if was_up_to_6m && !is_up_to_6m {
apply_if_previous_only(&mut self.up_to_6m);
}
let is_up_to_1y = UTXO_FILTERS.up_to_1y.check_days_old(current_days_old);
let was_up_to_1y = UTXO_FILTERS.up_to_1y.check_days_old(previous_days_old);
if is_up_to_1y && !was_up_to_1y {
apply_if_current_only(&mut self.up_to_1y);
} else if was_up_to_1y && !is_up_to_1y {
apply_if_previous_only(&mut self.up_to_1y);
}
let is_up_to_2y = UTXO_FILTERS.up_to_2y.check_days_old(current_days_old);
let was_up_to_2y = UTXO_FILTERS.up_to_2y.check_days_old(previous_days_old);
if is_up_to_2y && !was_up_to_2y {
apply_if_current_only(&mut self.up_to_2y);
} else if was_up_to_2y && !is_up_to_2y {
apply_if_previous_only(&mut self.up_to_2y);
}
let is_up_to_3y = UTXO_FILTERS.up_to_3y.check_days_old(current_days_old);
let was_up_to_3y = UTXO_FILTERS.up_to_3y.check_days_old(previous_days_old);
if is_up_to_3y && !was_up_to_3y {
apply_if_current_only(&mut self.up_to_3y);
} else if was_up_to_3y && !is_up_to_3y {
apply_if_previous_only(&mut self.up_to_3y);
}
let is_up_to_5y = UTXO_FILTERS.up_to_5y.check_days_old(current_days_old);
let was_up_to_5y = UTXO_FILTERS.up_to_5y.check_days_old(previous_days_old);
if is_up_to_5y && !was_up_to_5y {
apply_if_current_only(&mut self.up_to_5y);
} else if was_up_to_5y && !is_up_to_5y {
apply_if_previous_only(&mut self.up_to_5y);
}
let is_up_to_7y = UTXO_FILTERS.up_to_7y.check_days_old(current_days_old);
let was_up_to_7y = UTXO_FILTERS.up_to_7y.check_days_old(previous_days_old);
if is_up_to_7y && !was_up_to_7y {
apply_if_current_only(&mut self.up_to_7y);
} else if was_up_to_7y && !is_up_to_7y {
apply_if_previous_only(&mut self.up_to_7y);
}
let is_up_to_10y = UTXO_FILTERS.up_to_10y.check_days_old(current_days_old);
let was_up_to_10y = UTXO_FILTERS.up_to_10y.check_days_old(previous_days_old);
if is_up_to_10y && !was_up_to_10y {
apply_if_current_only(&mut self.up_to_10y);
} else if was_up_to_10y && !is_up_to_10y {
apply_if_previous_only(&mut self.up_to_10y);
}
let is_up_to_15y = UTXO_FILTERS.up_to_15y.check_days_old(current_days_old);
let was_up_to_15y = UTXO_FILTERS.up_to_15y.check_days_old(previous_days_old);
if is_up_to_15y && !was_up_to_15y {
apply_if_current_only(&mut self.up_to_15y);
} else if was_up_to_15y && !is_up_to_15y {
apply_if_previous_only(&mut self.up_to_15y);
}
let is_from_1d_to_1w = UTXO_FILTERS.from_1d_to_1w.check_days_old(current_days_old);
let was_from_1d_to_1w = UTXO_FILTERS.from_1d_to_1w.check_days_old(previous_days_old);
if is_from_1d_to_1w && !was_from_1d_to_1w {
apply_if_current_only(&mut self.from_1d_to_1w);
} else if was_from_1d_to_1w && !is_from_1d_to_1w {
apply_if_previous_only(&mut self.from_1d_to_1w);
}
let is_from_1w_to_1m = UTXO_FILTERS.from_1w_to_1m.check_days_old(current_days_old);
let was_from_1w_to_1m = UTXO_FILTERS.from_1w_to_1m.check_days_old(previous_days_old);
if is_from_1w_to_1m && !was_from_1w_to_1m {
apply_if_current_only(&mut self.from_1w_to_1m);
} else if was_from_1w_to_1m && !is_from_1w_to_1m {
apply_if_previous_only(&mut self.from_1w_to_1m);
}
let is_from_1m_to_3m = UTXO_FILTERS.from_1m_to_3m.check_days_old(current_days_old);
let was_from_1m_to_3m = UTXO_FILTERS.from_1m_to_3m.check_days_old(previous_days_old);
if is_from_1m_to_3m && !was_from_1m_to_3m {
apply_if_current_only(&mut self.from_1m_to_3m);
} else if was_from_1m_to_3m && !is_from_1m_to_3m {
apply_if_previous_only(&mut self.from_1m_to_3m);
}
let is_from_3m_to_6m = UTXO_FILTERS.from_3m_to_6m.check_days_old(current_days_old);
let was_from_3m_to_6m = UTXO_FILTERS.from_3m_to_6m.check_days_old(previous_days_old);
if is_from_3m_to_6m && !was_from_3m_to_6m {
apply_if_current_only(&mut self.from_3m_to_6m);
} else if was_from_3m_to_6m && !is_from_3m_to_6m {
apply_if_previous_only(&mut self.from_3m_to_6m);
}
let is_from_6m_to_1y = UTXO_FILTERS.from_6m_to_1y.check_days_old(current_days_old);
let was_from_6m_to_1y = UTXO_FILTERS.from_6m_to_1y.check_days_old(previous_days_old);
if is_from_6m_to_1y && !was_from_6m_to_1y {
apply_if_current_only(&mut self.from_6m_to_1y);
} else if was_from_6m_to_1y && !is_from_6m_to_1y {
apply_if_previous_only(&mut self.from_6m_to_1y);
}
let is_from_1y_to_2y = UTXO_FILTERS.from_1y_to_2y.check_days_old(current_days_old);
let was_from_1y_to_2y = UTXO_FILTERS.from_1y_to_2y.check_days_old(previous_days_old);
if is_from_1y_to_2y && !was_from_1y_to_2y {
apply_if_current_only(&mut self.from_1y_to_2y);
} else if was_from_1y_to_2y && !is_from_1y_to_2y {
apply_if_previous_only(&mut self.from_1y_to_2y);
}
let is_from_2y_to_3y = UTXO_FILTERS.from_2y_to_3y.check_days_old(current_days_old);
let was_from_2y_to_3y = UTXO_FILTERS.from_2y_to_3y.check_days_old(previous_days_old);
if is_from_2y_to_3y && !was_from_2y_to_3y {
apply_if_current_only(&mut self.from_2y_to_3y);
} else if was_from_2y_to_3y && !is_from_2y_to_3y {
apply_if_previous_only(&mut self.from_2y_to_3y);
}
let is_from_3y_to_5y = UTXO_FILTERS.from_3y_to_5y.check_days_old(current_days_old);
let was_from_3y_to_5y = UTXO_FILTERS.from_3y_to_5y.check_days_old(previous_days_old);
if is_from_3y_to_5y && !was_from_3y_to_5y {
apply_if_current_only(&mut self.from_3y_to_5y);
} else if was_from_3y_to_5y && !is_from_3y_to_5y {
apply_if_previous_only(&mut self.from_3y_to_5y);
}
let is_from_5y_to_7y = UTXO_FILTERS.from_5y_to_7y.check_days_old(current_days_old);
let was_from_5y_to_7y = UTXO_FILTERS.from_5y_to_7y.check_days_old(previous_days_old);
if is_from_5y_to_7y && !was_from_5y_to_7y {
apply_if_current_only(&mut self.from_5y_to_7y);
} else if was_from_5y_to_7y && !is_from_5y_to_7y {
apply_if_previous_only(&mut self.from_5y_to_7y);
}
let is_from_7y_to_10y = UTXO_FILTERS.from_7y_to_10y.check_days_old(current_days_old);
let was_from_7y_to_10y = UTXO_FILTERS
.from_7y_to_10y
.check_days_old(previous_days_old);
if is_from_7y_to_10y && !was_from_7y_to_10y {
apply_if_current_only(&mut self.from_7y_to_10y);
} else if was_from_7y_to_10y && !is_from_7y_to_10y {
apply_if_previous_only(&mut self.from_7y_to_10y);
}
let is_from_10y_to_15y = UTXO_FILTERS
.from_10y_to_15y
.check_days_old(current_days_old);
let was_from_10y_to_15y = UTXO_FILTERS
.from_10y_to_15y
.check_days_old(previous_days_old);
if is_from_10y_to_15y && !was_from_10y_to_15y {
apply_if_current_only(&mut self.from_10y_to_15y);
} else if was_from_10y_to_15y && !is_from_10y_to_15y {
apply_if_previous_only(&mut self.from_10y_to_15y);
}
let is_from_1y = UTXO_FILTERS.from_1y.check_days_old(current_days_old);
let was_from_1y = UTXO_FILTERS.from_1y.check_days_old(previous_days_old);
if is_from_1y && !was_from_1y {
apply_if_current_only(&mut self.from_1y);
} else if was_from_1y && !is_from_1y {
apply_if_previous_only(&mut self.from_1y);
}
let is_from_2y = UTXO_FILTERS.from_2y.check_days_old(current_days_old);
let was_from_2y = UTXO_FILTERS.from_2y.check_days_old(previous_days_old);
if is_from_2y && !was_from_2y {
apply_if_current_only(&mut self.from_2y);
} else if was_from_2y && !is_from_2y {
apply_if_previous_only(&mut self.from_2y);
}
let is_from_4y = UTXO_FILTERS.from_4y.check_days_old(current_days_old);
let was_from_4y = UTXO_FILTERS.from_4y.check_days_old(previous_days_old);
if is_from_4y && !was_from_4y {
apply_if_current_only(&mut self.from_4y);
} else if was_from_4y && !is_from_4y {
apply_if_previous_only(&mut self.from_4y);
}
let is_from_10y = UTXO_FILTERS.from_10y.check_days_old(current_days_old);
let was_from_10y = UTXO_FILTERS.from_10y.check_days_old(previous_days_old);
if is_from_10y && !was_from_10y {
apply_if_current_only(&mut self.from_10y);
} else if was_from_10y && !is_from_10y {
apply_if_previous_only(&mut self.from_10y);
}
let is_from_15y = UTXO_FILTERS.from_15y.check_days_old(current_days_old);
let was_from_15y = UTXO_FILTERS.from_15y.check_days_old(previous_days_old);
if is_from_15y && !was_from_15y {
apply_if_current_only(&mut self.from_15y);
} else if was_from_15y && !is_from_15y {
apply_if_previous_only(&mut self.from_15y);
}
let is_sth = UTXO_FILTERS.sth.check_days_old(current_days_old);
let was_sth = UTXO_FILTERS.sth.check_days_old(previous_days_old);
if is_sth && !was_sth {
apply_if_current_only(&mut self.sth);
} else if was_sth && !is_sth {
apply_if_previous_only(&mut self.sth);
}
let is_lth = UTXO_FILTERS.lth.check_days_old(current_days_old);
let was_lth = UTXO_FILTERS.lth.check_days_old(previous_days_old);
if is_lth && !was_lth {
if is_sth {
unreachable!()
}
apply_if_current_only(&mut self.lth);
} else if was_lth && !is_lth {
if was_sth {
unreachable!()
}
// unreachable!();
apply_if_previous_only(&mut self.lth);
}
}
/// Includes years since it's the initial apply
pub fn initial_filtered_apply(&mut self, days_old: &u32, year: &u32, apply: impl Fn(&mut T)) {
if UTXO_FILTERS.up_to_1d.check(days_old, year) {
apply(&mut self.up_to_1d);
} else if UTXO_FILTERS.from_1d_to_1w.check(days_old, year) {
apply(&mut self.from_1d_to_1w);
} else if UTXO_FILTERS.from_1w_to_1m.check(days_old, year) {
apply(&mut self.from_1w_to_1m);
} else if UTXO_FILTERS.from_1m_to_3m.check(days_old, year) {
apply(&mut self.from_1m_to_3m);
} else if UTXO_FILTERS.from_3m_to_6m.check(days_old, year) {
apply(&mut self.from_3m_to_6m);
} else if UTXO_FILTERS.from_6m_to_1y.check(days_old, year) {
apply(&mut self.from_6m_to_1y);
} else if UTXO_FILTERS.from_1y_to_2y.check(days_old, year) {
apply(&mut self.from_1y_to_2y);
} else if UTXO_FILTERS.from_2y_to_3y.check(days_old, year) {
apply(&mut self.from_2y_to_3y);
} else if UTXO_FILTERS.from_3y_to_5y.check(days_old, year) {
apply(&mut self.from_3y_to_5y);
} else if UTXO_FILTERS.from_5y_to_7y.check(days_old, year) {
apply(&mut self.from_5y_to_7y);
} else if UTXO_FILTERS.from_7y_to_10y.check(days_old, year) {
apply(&mut self.from_7y_to_10y);
} else if UTXO_FILTERS.from_10y_to_15y.check(days_old, year) {
apply(&mut self.from_10y_to_15y);
}
if UTXO_FILTERS.year_2009.check(days_old, year) {
apply(&mut self.year_2009);
} else if UTXO_FILTERS.year_2010.check(days_old, year) {
apply(&mut self.year_2010);
} else if UTXO_FILTERS.year_2011.check(days_old, year) {
apply(&mut self.year_2011);
} else if UTXO_FILTERS.year_2012.check(days_old, year) {
apply(&mut self.year_2012);
} else if UTXO_FILTERS.year_2013.check(days_old, year) {
apply(&mut self.year_2013);
} else if UTXO_FILTERS.year_2014.check(days_old, year) {
apply(&mut self.year_2014);
} else if UTXO_FILTERS.year_2015.check(days_old, year) {
apply(&mut self.year_2015);
} else if UTXO_FILTERS.year_2016.check(days_old, year) {
apply(&mut self.year_2016);
} else if UTXO_FILTERS.year_2017.check(days_old, year) {
apply(&mut self.year_2017);
} else if UTXO_FILTERS.year_2018.check(days_old, year) {
apply(&mut self.year_2018);
} else if UTXO_FILTERS.year_2019.check(days_old, year) {
apply(&mut self.year_2019);
} else if UTXO_FILTERS.year_2020.check(days_old, year) {
apply(&mut self.year_2020);
} else if UTXO_FILTERS.year_2021.check(days_old, year) {
apply(&mut self.year_2021);
} else if UTXO_FILTERS.year_2022.check(days_old, year) {
apply(&mut self.year_2022);
} else if UTXO_FILTERS.year_2023.check(days_old, year) {
apply(&mut self.year_2023);
} else if UTXO_FILTERS.year_2024.check(days_old, year) {
apply(&mut self.year_2024);
}
if UTXO_FILTERS.sth.check(days_old, year) {
apply(&mut self.sth);
} else if UTXO_FILTERS.lth.check(days_old, year) {
apply(&mut self.lth);
} else {
unreachable!()
}
if UTXO_FILTERS.from_1y.check(days_old, year) {
apply(&mut self.from_1y);
}
if UTXO_FILTERS.from_2y.check(days_old, year) {
apply(&mut self.from_2y);
}
if UTXO_FILTERS.from_4y.check(days_old, year) {
apply(&mut self.from_4y);
}
if UTXO_FILTERS.from_10y.check(days_old, year) {
apply(&mut self.from_10y);
}
if UTXO_FILTERS.from_15y.check(days_old, year) {
apply(&mut self.from_15y);
}
if UTXO_FILTERS.up_to_15y.check(days_old, year) {
apply(&mut self.up_to_15y);
} else {
return;
}
if UTXO_FILTERS.up_to_10y.check(days_old, year) {
apply(&mut self.up_to_10y);
} else {
return;
}
if UTXO_FILTERS.up_to_7y.check(days_old, year) {
apply(&mut self.up_to_7y);
} else {
return;
}
if UTXO_FILTERS.up_to_5y.check(days_old, year) {
apply(&mut self.up_to_5y);
} else {
return;
}
if UTXO_FILTERS.up_to_3y.check(days_old, year) {
apply(&mut self.up_to_3y);
} else {
return;
}
if UTXO_FILTERS.up_to_2y.check(days_old, year) {
apply(&mut self.up_to_2y);
} else {
return;
}
if UTXO_FILTERS.up_to_1y.check(days_old, year) {
apply(&mut self.up_to_1y);
} else {
return;
}
if UTXO_FILTERS.up_to_6m.check(days_old, year) {
apply(&mut self.up_to_6m);
} else {
return;
}
if UTXO_FILTERS.up_to_5m.check(days_old, year) {
apply(&mut self.up_to_5m);
} else {
return;
}
if UTXO_FILTERS.up_to_4m.check(days_old, year) {
apply(&mut self.up_to_4m);
} else {
return;
}
if UTXO_FILTERS.up_to_3m.check(days_old, year) {
apply(&mut self.up_to_3m);
} else {
return;
}
if UTXO_FILTERS.up_to_2m.check(days_old, year) {
apply(&mut self.up_to_2m);
} else {
return;
}
if UTXO_FILTERS.up_to_1m.check(days_old, year) {
apply(&mut self.up_to_1m);
} else {
return;
}
if UTXO_FILTERS.up_to_1w.check(days_old, year) {
apply(&mut self.up_to_1w);
}
}
#[inline(always)]
pub fn as_vec(&self) -> Vec<(&T, UTXOCohortId)> {
vec![
(&self.up_to_1d, UTXOCohortId::UpTo1d),
(&self.up_to_1w, UTXOCohortId::UpTo1w),
(&self.up_to_1m, UTXOCohortId::UpTo1m),
(&self.up_to_2m, UTXOCohortId::UpTo2m),
(&self.up_to_3m, UTXOCohortId::UpTo3m),
(&self.up_to_4m, UTXOCohortId::UpTo4m),
(&self.up_to_5m, UTXOCohortId::UpTo5m),
(&self.up_to_6m, UTXOCohortId::UpTo6m),
(&self.up_to_1y, UTXOCohortId::UpTo1y),
(&self.up_to_2y, UTXOCohortId::UpTo2y),
(&self.up_to_3y, UTXOCohortId::UpTo3y),
(&self.up_to_5y, UTXOCohortId::UpTo5y),
(&self.up_to_7y, UTXOCohortId::UpTo7y),
(&self.up_to_10y, UTXOCohortId::UpTo10y),
(&self.up_to_15y, UTXOCohortId::UpTo15y),
(&self.from_1d_to_1w, UTXOCohortId::From1dTo1w),
(&self.from_1w_to_1m, UTXOCohortId::From1wTo1m),
(&self.from_1m_to_3m, UTXOCohortId::From1mTo3m),
(&self.from_3m_to_6m, UTXOCohortId::From3mTo6m),
(&self.from_6m_to_1y, UTXOCohortId::From6mTo1y),
(&self.from_1y_to_2y, UTXOCohortId::From1yTo2y),
(&self.from_2y_to_3y, UTXOCohortId::From2yTo3y),
(&self.from_3y_to_5y, UTXOCohortId::From3yTo5y),
(&self.from_5y_to_7y, UTXOCohortId::From5yTo7y),
(&self.from_7y_to_10y, UTXOCohortId::From7yTo10y),
(&self.from_10y_to_15y, UTXOCohortId::From10yTo15y),
(&self.from_1y, UTXOCohortId::From1y),
(&self.from_2y, UTXOCohortId::From2y),
(&self.from_4y, UTXOCohortId::From4y),
(&self.from_10y, UTXOCohortId::From10y),
(&self.from_15y, UTXOCohortId::From15y),
(&self.year_2009, UTXOCohortId::Year2009),
(&self.year_2010, UTXOCohortId::Year2010),
(&self.year_2011, UTXOCohortId::Year2011),
(&self.year_2012, UTXOCohortId::Year2012),
(&self.year_2013, UTXOCohortId::Year2013),
(&self.year_2014, UTXOCohortId::Year2014),
(&self.year_2015, UTXOCohortId::Year2015),
(&self.year_2016, UTXOCohortId::Year2016),
(&self.year_2017, UTXOCohortId::Year2017),
(&self.year_2018, UTXOCohortId::Year2018),
(&self.year_2019, UTXOCohortId::Year2019),
(&self.year_2020, UTXOCohortId::Year2020),
(&self.year_2021, UTXOCohortId::Year2021),
(&self.year_2022, UTXOCohortId::Year2022),
(&self.year_2023, UTXOCohortId::Year2023),
(&self.year_2024, UTXOCohortId::Year2024),
(&self.sth, UTXOCohortId::ShortTermHolders),
(&self.lth, UTXOCohortId::LongTermHolders),
]
}
#[inline(always)]
pub fn as_mut_vec(&mut self) -> Vec<(&mut T, UTXOCohortId)> {
vec![
(&mut self.up_to_1d, UTXOCohortId::UpTo1d),
(&mut self.up_to_1w, UTXOCohortId::UpTo1w),
(&mut self.up_to_1m, UTXOCohortId::UpTo1m),
(&mut self.up_to_2m, UTXOCohortId::UpTo2m),
(&mut self.up_to_3m, UTXOCohortId::UpTo3m),
(&mut self.up_to_4m, UTXOCohortId::UpTo4m),
(&mut self.up_to_5m, UTXOCohortId::UpTo5m),
(&mut self.up_to_6m, UTXOCohortId::UpTo6m),
(&mut self.up_to_1y, UTXOCohortId::UpTo1y),
(&mut self.up_to_2y, UTXOCohortId::UpTo2y),
(&mut self.up_to_3y, UTXOCohortId::UpTo3y),
(&mut self.up_to_5y, UTXOCohortId::UpTo5y),
(&mut self.up_to_7y, UTXOCohortId::UpTo7y),
(&mut self.up_to_10y, UTXOCohortId::UpTo10y),
(&mut self.up_to_15y, UTXOCohortId::UpTo15y),
(&mut self.from_1d_to_1w, UTXOCohortId::From1dTo1w),
(&mut self.from_1w_to_1m, UTXOCohortId::From1wTo1m),
(&mut self.from_1m_to_3m, UTXOCohortId::From1mTo3m),
(&mut self.from_3m_to_6m, UTXOCohortId::From3mTo6m),
(&mut self.from_6m_to_1y, UTXOCohortId::From6mTo1y),
(&mut self.from_1y_to_2y, UTXOCohortId::From1yTo2y),
(&mut self.from_2y_to_3y, UTXOCohortId::From2yTo3y),
(&mut self.from_3y_to_5y, UTXOCohortId::From3yTo5y),
(&mut self.from_5y_to_7y, UTXOCohortId::From5yTo7y),
(&mut self.from_7y_to_10y, UTXOCohortId::From7yTo10y),
(&mut self.from_10y_to_15y, UTXOCohortId::From10yTo15y),
(&mut self.from_1y, UTXOCohortId::From1y),
(&mut self.from_2y, UTXOCohortId::From2y),
(&mut self.from_4y, UTXOCohortId::From4y),
(&mut self.from_10y, UTXOCohortId::From10y),
(&mut self.from_15y, UTXOCohortId::From15y),
(&mut self.year_2009, UTXOCohortId::Year2009),
(&mut self.year_2010, UTXOCohortId::Year2010),
(&mut self.year_2011, UTXOCohortId::Year2011),
(&mut self.year_2012, UTXOCohortId::Year2012),
(&mut self.year_2013, UTXOCohortId::Year2013),
(&mut self.year_2014, UTXOCohortId::Year2014),
(&mut self.year_2015, UTXOCohortId::Year2015),
(&mut self.year_2016, UTXOCohortId::Year2016),
(&mut self.year_2017, UTXOCohortId::Year2017),
(&mut self.year_2018, UTXOCohortId::Year2018),
(&mut self.year_2019, UTXOCohortId::Year2019),
(&mut self.year_2020, UTXOCohortId::Year2020),
(&mut self.year_2021, UTXOCohortId::Year2021),
(&mut self.year_2022, UTXOCohortId::Year2022),
(&mut self.year_2023, UTXOCohortId::Year2023),
(&mut self.year_2024, UTXOCohortId::Year2024),
(&mut self.sth, UTXOCohortId::ShortTermHolders),
(&mut self.lth, UTXOCohortId::LongTermHolders),
]
}
}

View File

@@ -0,0 +1,29 @@
use allocative::Allocative;
use bincode::{Decode, Encode};
use crate::structs::Counter;
use super::AnyState;
#[derive(Default, Debug, Encode, Decode, Allocative)]
pub struct Counters {
pub op_return_addresses: Counter,
pub push_only_addresses: Counter,
pub unknown_addresses: Counter,
pub empty_addresses: Counter,
}
impl Counters {}
impl AnyState for Counters {
fn name<'a>() -> &'a str {
"counters"
}
fn clear(&mut self) {
self.op_return_addresses.reset();
self.push_only_addresses.reset();
self.unknown_addresses.reset();
self.empty_addresses.reset();
}
}

View File

@@ -0,0 +1,48 @@
use allocative::Allocative;
use bincode::{Decode, Encode};
use derive_deref::{Deref, DerefMut};
use crate::structs::{BlockData, BlockPath, DateData};
use super::AnyState;
#[derive(Default, Deref, DerefMut, Debug, Encode, Decode, Allocative)]
pub struct DateDataVec(Vec<DateData>);
impl DateDataVec {
pub fn last_block(&self) -> Option<&BlockData> {
self.last().and_then(|date_data| date_data.blocks.last())
}
pub fn last_mut_block(&mut self) -> Option<&mut BlockData> {
self.last_mut()
.and_then(|date_data| date_data.blocks.last_mut())
}
pub fn second_last_block(&self) -> Option<&BlockData> {
self.iter()
.flat_map(|date_data| &date_data.blocks)
.rev()
.nth(1)
}
pub fn get_date_data(&self, block_path: &BlockPath) -> Option<&DateData> {
self.0.get(block_path.date_index as usize)
}
pub fn get_block_data(&self, block_path: &BlockPath) -> Option<&BlockData> {
self.0
.get(block_path.date_index as usize)
.and_then(|date_data| date_data.blocks.get(block_path.block_index as usize))
}
}
impl AnyState for DateDataVec {
fn name<'a>() -> &'a str {
"date_data_vec"
}
fn clear(&mut self) {
self.0.clear();
}
}

95
parser/src/states/mod.rs Normal file
View File

@@ -0,0 +1,95 @@
use std::thread;
mod _trait;
mod cohorts_states;
mod counters;
mod date_data_vec;
pub use _trait::*;
use allocative::Allocative;
pub use cohorts_states::*;
use counters::*;
use date_data_vec::*;
use crate::{databases::AddressIndexToAddressData, datasets::AllDatasets, utils::log};
#[derive(Default, Allocative)]
pub struct States {
pub address_counters: Counters,
pub date_data_vec: DateDataVec,
pub address_cohorts_durable_states: AddressCohortsDurableStates,
pub utxo_cohorts_durable_states: UTXOCohortsDurableStates,
}
impl States {
pub fn import(
address_index_to_address_data: &mut AddressIndexToAddressData,
datasets: &AllDatasets,
) -> color_eyre::Result<Self> {
let date_data_vec_handle = thread::spawn(DateDataVec::import);
let address_counters = Counters::import()?;
let date_data_vec = date_data_vec_handle.join().unwrap()?;
// TODO:
// For both address and utxo check if any of these datasets have a None min
// If so use default state otherwise init
// unrealized
// price_paid
// capitalization
// supply
// utxo
let mut address_cohorts_durable_states = AddressCohortsDurableStates::default();
let mut utxo_cohorts_durable_states = UTXOCohortsDurableStates::default();
if let Some(first_date_data) = date_data_vec.first() {
if let Some(first_block_data) = first_date_data.blocks.first() {
let first_height = first_block_data.height as usize;
let first_date = first_date_data.date;
// TODO: Do the same for addresses
address_cohorts_durable_states =
AddressCohortsDurableStates::init(address_index_to_address_data);
if !datasets.utxo.needs_durable_states(first_height, first_date) {
utxo_cohorts_durable_states = UTXOCohortsDurableStates::init(&date_data_vec);
}
}
}
Ok(Self {
address_cohorts_durable_states,
address_counters,
date_data_vec,
utxo_cohorts_durable_states,
})
}
pub fn reset(&mut self, include_addresses: bool) {
log("Reseting all states...");
let _ = self.date_data_vec.reset();
self.utxo_cohorts_durable_states = UTXOCohortsDurableStates::default();
// TODO: Check that they are ONLY computed in an `if include_addresses`
if include_addresses {
let _ = self.address_counters.reset();
self.address_cohorts_durable_states = AddressCohortsDurableStates::default();
}
}
pub fn export(&self) -> color_eyre::Result<()> {
thread::scope(|s| {
s.spawn(|| self.address_counters.export().unwrap());
s.spawn(|| self.date_data_vec.export().unwrap());
});
Ok(())
}
}