brk: first commit

This commit is contained in:
nym21
2025-02-23 01:25:15 +01:00
parent 8c3f519016
commit 19cf34f9d4
266 changed files with 225 additions and 1268 deletions
+36
View File
@@ -0,0 +1,36 @@
use std::{
fmt::Debug,
fs, io,
path::{Path, PathBuf},
};
use bincode::{Decode, Encode};
use serde::{de::DeserializeOwned, Serialize};
use crate::{io::Serialization, structs::Config};
pub trait AnyState
where
Self: Debug + Encode + Decode + Serialize + DeserializeOwned,
{
fn name<'a>() -> &'a str;
fn path(config: &Config) -> PathBuf {
config.path_states().join(Self::name())
}
fn reset(&mut self, config: &Config) -> color_eyre::Result<(), io::Error> {
self.clear();
fs::remove_file(Self::path(config))
}
fn import(config: &Config) -> color_eyre::Result<Self> {
Serialization::Binary.import(&Self::path(config))
}
fn export(&self, config: &Config) -> color_eyre::Result<()> {
Serialization::Binary.export(Path::new(&Self::path(config)), self)
}
fn clear(&mut self);
}
@@ -0,0 +1,121 @@
use std::ops::AddAssign;
use allocative::Allocative;
use crate::{
parser::states::{DurableStates, IsZero, OneShotStates, PriceToValue, UnrealizedState},
structs::{Amount, Price},
};
#[derive(Default, Debug, Allocative)]
pub struct AddressCohortDurableStates {
pub address_count: f64,
pub durable_states: DurableStates,
pub price_to_amount: PriceToValue<Amount>,
}
impl AddressCohortDurableStates {
#[allow(clippy::too_many_arguments)]
pub fn increment(
&mut self,
address_count: f64,
amount: Amount,
utxo_count: f64,
realized_cap: Price,
mean_price_paid: Price,
) -> color_eyre::Result<()> {
self.address_count += address_count;
self._crement(amount, utxo_count, realized_cap, mean_price_paid, true)
}
#[allow(clippy::too_many_arguments)]
pub fn decrement(
&mut self,
address_count: f64,
amount: Amount,
utxo_count: f64,
realized_cap: Price,
mean_price_paid: Price,
) -> color_eyre::Result<()> {
self.address_count -= address_count;
self._crement(amount, utxo_count, realized_cap, mean_price_paid, false)
}
#[allow(clippy::too_many_arguments)]
pub fn _crement(
&mut self,
amount: Amount,
utxo_count: f64,
realized_cap: Price,
mean_price_paid: Price,
increment: bool,
) -> color_eyre::Result<()> {
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);
})?;
if !amount.is_zero()? {
if increment {
self.price_to_amount.increment(mean_price_paid, amount);
} else {
self.price_to_amount
.decrement(mean_price_paid, amount)
.inspect_err(|report| {
dbg!(report, "cents_to_split_amount decrement",);
})?;
}
}
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 one_shot_states_ref = &mut one_shot_states;
let supply = self.durable_states.supply_state.supply();
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
}
}
impl AddAssign for AddressCohortDurableStates {
fn add_assign(&mut self, rhs: Self) {
self.address_count += rhs.address_count;
self.durable_states += rhs.durable_states;
self.price_to_amount += rhs.price_to_amount;
}
}
@@ -0,0 +1,80 @@
use crate::structs::{AddressLiquidity, AddressSize, AddressSplit, AddressType};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub enum AddressCohortId {
All,
Illiquid,
Liquid,
HighlyLiquid,
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::Illiquid => Some("illiquid"),
Self::Liquid => Some("liquid"),
Self::HighlyLiquid => Some("highly_liquid"),
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::Illiquid => AddressSplit::Liquidity(AddressLiquidity::Illiquid),
Self::Liquid => AddressSplit::Liquidity(AddressLiquidity::Liquid),
Self::HighlyLiquid => AddressSplit::Liquidity(AddressLiquidity::HighlyLiquid),
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),
}
}
}
@@ -0,0 +1,156 @@
use std::iter::Sum;
use allocative::Allocative;
use derive_deref::{Deref, DerefMut};
use rayon::prelude::*;
use crate::{
parser::databases::AddressIndexToAddressData,
structs::{AddressData, AddressRealizedData, Amount, 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 {
address_index_to_address_data.compute_addres_cohorts_durable_states()
}
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
pub 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 f64;
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_address_count = liquidity_classification.split(1.0);
let split_sat_amount = liquidity_classification.split(amount.to_sat() as f64);
let split_utxo_count = liquidity_classification.split(utxo_count);
let split_realized_cap = liquidity_classification.split(realized_cap.to_dollar());
self.0.iterate(
address_data,
|state| {
// Unsplit it must be one
let address_count = 1.0;
if increment {
state.increment(
address_count,
amount,
utxo_count,
realized_cap,
mean_price_paid,
)
} else {
state.decrement(
address_count,
amount,
utxo_count,
realized_cap,
mean_price_paid,
)
}
},
|liquidity, state| {
let address_count = split_address_count.from(liquidity);
let amount = Amount::from_sat(split_sat_amount.from(liquidity).floor() as u64);
let utxo_count = split_utxo_count.from(liquidity);
let realized_cap = Price::from_dollar(split_realized_cap.from(liquidity));
if increment {
state.increment(
address_count,
amount,
utxo_count,
realized_cap,
mean_price_paid,
)
} else {
state.decrement(
address_count,
amount,
utxo_count,
realized_cap,
mean_price_paid,
)
}
},
)?;
Ok(())
}
pub fn compute_one_shot_states(
&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
}
}
impl Sum for AddressCohortsDurableStates {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::default(), |mut a, b| {
a.0 += b.0;
a
})
}
}
@@ -0,0 +1,45 @@
use derive_deref::{Deref, DerefMut};
use crate::{
parser::states::InputState,
structs::{AddressRealizedData, Amount, LiquidityClassification},
};
use super::SplitByAddressCohort;
#[derive(Deref, DerefMut, Default)]
pub struct AddressCohortsInputStates(SplitByAddressCohort<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 normal_iteration = move |state: &mut InputState| -> color_eyre::Result<()> {
state.iterate(count, sent);
Ok(())
};
let split_count = liquidity_classification.split(count);
let split_sent = liquidity_classification.split(sent.to_sat() as f64);
let liquified_iteration =
move |liquidity, state: &mut InputState| -> color_eyre::Result<()> {
state.iterate(
split_count.from(liquidity),
Amount::from_sat(split_sent.from(liquidity).round() as u64),
);
Ok(())
};
self.iterate(
&realized_data.initial_address_data,
normal_iteration,
liquified_iteration,
)
}
}
@@ -0,0 +1,8 @@
use derive_deref::{Deref, DerefMut};
use crate::parser::states::OneShotStates;
use super::SplitByAddressCohort;
#[derive(Deref, DerefMut, Default)]
pub struct AddressCohortsOneShotStates(pub SplitByAddressCohort<OneShotStates>);
@@ -0,0 +1,45 @@
use derive_deref::{Deref, DerefMut};
use crate::{
parser::states::OutputState,
structs::{AddressRealizedData, Amount, LiquidityClassification},
};
use super::SplitByAddressCohort;
#[derive(Deref, DerefMut, Default)]
pub struct AddressCohortsOutputStates(SplitByAddressCohort<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 normal_iteration = move |state: &mut OutputState| -> color_eyre::Result<()> {
state.iterate(count, volume);
Ok(())
};
let split_count = liquidity_classification.split(count);
let split_volume = liquidity_classification.split(volume.to_sat() as f64);
let liquified_iteration =
move |liquidity, state: &mut OutputState| -> color_eyre::Result<()> {
state.iterate(
split_count.from(liquidity),
Amount::from_sat(split_volume.from(liquidity).round() as u64),
);
Ok(())
};
self.iterate(
&realized_data.initial_address_data,
normal_iteration,
liquified_iteration,
)
}
}
@@ -0,0 +1,68 @@
use derive_deref::{Deref, DerefMut};
use crate::{
parser::states::RealizedState,
structs::{AddressRealizedData, LiquidityClassification, Price},
};
use super::SplitByAddressCohort;
#[derive(Deref, DerefMut, Default)]
pub struct AddressCohortsRealizedStates(SplitByAddressCohort<RealizedState>);
impl AddressCohortsRealizedStates {
pub fn iterate_realized(
&mut self,
realized_data: &AddressRealizedData,
liquidity_classification: &LiquidityClassification,
) -> color_eyre::Result<()> {
let realized_profit = realized_data.profit;
let realized_loss = realized_data.loss;
let value_created = realized_data.value_created;
let adjusted_value_created = realized_data.adjusted_value_created;
let value_destroyed = realized_data.value_destroyed;
let adjusted_value_destroyed = realized_data.adjusted_value_destroyed;
let normal_iteration = move |state: &mut RealizedState| -> color_eyre::Result<()> {
state.iterate(
realized_profit,
realized_loss,
value_created,
adjusted_value_created,
value_destroyed,
adjusted_value_destroyed,
);
Ok(())
};
let split_realized_profit =
liquidity_classification.split(realized_profit.to_cent() as f64);
let split_realized_loss = liquidity_classification.split(realized_loss.to_cent() as f64);
let split_value_created = liquidity_classification.split(value_created.to_cent() as f64);
let split_adjusted_value_created =
liquidity_classification.split(adjusted_value_created.to_cent() as f64);
let split_value_destroyed =
liquidity_classification.split(value_destroyed.to_cent() as f64);
let split_adjusted_value_destroyed =
liquidity_classification.split(adjusted_value_destroyed.to_cent() as f64);
let liquified_iteration =
move |liquidity, state: &mut RealizedState| -> color_eyre::Result<()> {
state.iterate(
Price::from_cent(split_realized_profit.from(liquidity) as u64),
Price::from_cent(split_realized_loss.from(liquidity) as u64),
Price::from_cent(split_value_created.from(liquidity) as u64),
Price::from_cent(split_adjusted_value_created.from(liquidity) as u64),
Price::from_cent(split_value_destroyed.from(liquidity) as u64),
Price::from_cent(split_adjusted_value_destroyed.from(liquidity) as u64),
);
Ok(())
};
self.iterate(
&realized_data.initial_address_data,
normal_iteration,
liquified_iteration,
)
}
}
@@ -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::*;
@@ -0,0 +1,273 @@
use std::ops::AddAssign;
use allocative::Allocative;
use crate::structs::{AddressData, AddressLiquidity, AddressSize, AddressSplit, AddressType};
use super::AddressCohortId;
#[derive(Default, Allocative)]
pub struct SplitByAddressCohort<T> {
pub all: T,
pub illiquid: T,
pub liquid: T,
pub highly_liquid: 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::Liquidity(address_liquidity) => match address_liquidity {
AddressLiquidity::Illiquid => Some(&self.illiquid),
AddressLiquidity::Liquid => Some(&self.liquid),
AddressLiquidity::HighlyLiquid => Some(&self.highly_liquid),
},
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,
normal_iteration: impl Fn(&mut T) -> color_eyre::Result<()>,
liquified_iteration: impl Fn(AddressLiquidity, &mut T) -> color_eyre::Result<()>,
) -> color_eyre::Result<()> {
normal_iteration(self.get_mut_from_split(&AddressSplit::All).unwrap())?;
let mut _liquified_iteration = |address_liquidity| {
liquified_iteration(
address_liquidity,
self.get_mut_from_split(&AddressSplit::Liquidity(address_liquidity))
.unwrap(),
)
};
_liquified_iteration(AddressLiquidity::Illiquid)?;
_liquified_iteration(AddressLiquidity::Liquid)?;
_liquified_iteration(AddressLiquidity::HighlyLiquid)?;
if let Some(state) = self.get_mut_from_split(&AddressSplit::Type(address_data.address_type))
{
normal_iteration(state)?;
}
if let Some(state) = self.get_mut_from_split(&AddressSplit::Size(AddressSize::from_amount(
address_data.amount,
))) {
normal_iteration(state)?;
}
Ok(())
}
fn get_mut_from_split(&mut self, split: &AddressSplit) -> Option<&mut T> {
match &split {
AddressSplit::All => Some(&mut self.all),
AddressSplit::Liquidity(address_liquidity) => match address_liquidity {
AddressLiquidity::Illiquid => Some(&mut self.illiquid),
AddressLiquidity::Liquid => Some(&mut self.liquid),
AddressLiquidity::HighlyLiquid => Some(&mut self.highly_liquid),
},
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::Illiquid => &mut self.illiquid,
AddressCohortId::Liquid => &mut self.liquid,
AddressCohortId::HighlyLiquid => &mut self.highly_liquid,
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.illiquid, AddressCohortId::Illiquid),
(&self.liquid, AddressCohortId::Liquid),
(&self.highly_liquid, AddressCohortId::HighlyLiquid),
(&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.illiquid, AddressCohortId::Illiquid),
(&mut self.liquid, AddressCohortId::Liquid),
(&mut self.highly_liquid, AddressCohortId::HighlyLiquid),
(&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),
]
}
}
impl<T> AddAssign for SplitByAddressCohort<T>
where
T: AddAssign,
{
fn add_assign(&mut self, rhs: Self) {
self.all += rhs.all;
self.illiquid += rhs.illiquid;
self.liquid += rhs.liquid;
self.highly_liquid += rhs.highly_liquid;
self.plankton += rhs.plankton;
self.shrimp += rhs.shrimp;
self.crab += rhs.crab;
self.fish += rhs.fish;
self.shark += rhs.shark;
self.whale += rhs.whale;
self.humpback += rhs.humpback;
self.megalodon += rhs.megalodon;
self.p2pk += rhs.p2pk;
self.p2pkh += rhs.p2pkh;
self.p2sh += rhs.p2sh;
self.p2wpkh += rhs.p2wpkh;
self.p2wsh += rhs.p2wsh;
self.p2tr += rhs.p2tr;
}
}
impl<T> SplitByAddressCohort<Option<T>> {
pub fn unwrap(self) -> SplitByAddressCohort<T> {
SplitByAddressCohort {
all: self.all.unwrap(),
illiquid: self.illiquid.unwrap(),
liquid: self.liquid.unwrap(),
highly_liquid: self.highly_liquid.unwrap(),
plankton: self.plankton.unwrap(),
shrimp: self.shrimp.unwrap(),
crab: self.crab.unwrap(),
fish: self.fish.unwrap(),
shark: self.shark.unwrap(),
whale: self.whale.unwrap(),
humpback: self.humpback.unwrap(),
megalodon: self.megalodon.unwrap(),
p2pk: self.p2pk.unwrap(),
p2pkh: self.p2pkh.unwrap(),
p2sh: self.p2sh.unwrap(),
p2wpkh: self.p2wpkh.unwrap(),
p2wsh: self.p2wsh.unwrap(),
p2tr: self.p2tr.unwrap(),
}
}
}
@@ -0,0 +1,30 @@
use std::ops::AddAssign;
use allocative::Allocative;
use crate::structs::Price;
#[derive(Debug, Default, Allocative)]
pub struct CapitalizationState {
realized_cap: Price,
}
impl CapitalizationState {
pub fn realized_cap(&self) -> Price {
self.realized_cap
}
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;
}
}
impl AddAssign for CapitalizationState {
fn add_assign(&mut self, rhs: Self) {
self.realized_cap += rhs.realized_cap;
}
}
@@ -0,0 +1,50 @@
use std::ops::AddAssign;
use allocative::Allocative;
use crate::structs::{Amount, Price};
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: Amount,
utxo_count: f64,
realized_cap: Price,
) -> color_eyre::Result<()> {
self.utxo_state.increment(utxo_count);
self.capitalization_state.increment(realized_cap);
self.supply_state.increment(amount);
Ok(())
}
pub fn decrement(
&mut self,
amount: Amount,
utxo_count: f64,
realized_cap: Price,
) -> color_eyre::Result<()> {
self.utxo_state.decrement(utxo_count)?;
self.capitalization_state.decrement(realized_cap);
self.supply_state.decrement(amount)?;
Ok(())
}
}
impl AddAssign for DurableStates {
fn add_assign(&mut self, rhs: Self) {
self.capitalization_state += rhs.capitalization_state;
self.supply_state += rhs.supply_state;
self.utxo_state += rhs.utxo_state;
}
}
@@ -0,0 +1,22 @@
use crate::structs::Amount;
#[derive(Debug, Default)]
pub struct InputState {
count: f64,
volume: Amount,
}
impl InputState {
pub fn count(&self) -> f64 {
self.count
}
pub fn volume(&self) -> Amount {
self.volume
}
pub fn iterate(&mut self, count: f64, volume: Amount) {
self.count += count;
self.volume += volume;
}
}
@@ -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::*;
@@ -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>,
}
@@ -0,0 +1,22 @@
use crate::structs::Amount;
#[derive(Debug, Default)]
pub struct OutputState {
count: f64,
volume: Amount,
}
impl OutputState {
// pub fn count(&self) -> f64 {
// self.count
// }
// pub fn volume(&self) -> Amount {
// self.volume
// }
pub fn iterate(&mut self, count: f64, volume: Amount) {
self.count += count;
self.volume += volume;
}
}
@@ -0,0 +1,286 @@
use crate::structs::{Amount, Price};
#[derive(Default, Debug)]
pub struct PricePaidState {
pp_05p: Option<Price>,
pp_10p: Option<Price>,
pp_15p: Option<Price>,
pp_20p: Option<Price>,
pp_25p: Option<Price>,
pp_30p: Option<Price>,
pp_35p: Option<Price>,
pp_40p: Option<Price>,
pp_45p: Option<Price>,
pp_median: Option<Price>,
pp_55p: Option<Price>,
pp_60p: Option<Price>,
pp_65p: Option<Price>,
pp_70p: Option<Price>,
pp_75p: Option<Price>,
pp_80p: Option<Price>,
pp_85p: Option<Price>,
pp_90p: Option<Price>,
pp_95p: Option<Price>,
processed_amount: Amount,
}
impl PricePaidState {
pub fn pp_05p(&self) -> Option<Price> {
self.pp_05p
}
pub fn pp_10p(&self) -> Option<Price> {
self.pp_10p
}
pub fn pp_15p(&self) -> Option<Price> {
self.pp_15p
}
pub fn pp_20p(&self) -> Option<Price> {
self.pp_20p
}
pub fn pp_25p(&self) -> Option<Price> {
self.pp_25p
}
pub fn pp_30p(&self) -> Option<Price> {
self.pp_30p
}
pub fn pp_35p(&self) -> Option<Price> {
self.pp_35p
}
pub fn pp_40p(&self) -> Option<Price> {
self.pp_40p
}
pub fn pp_45p(&self) -> Option<Price> {
self.pp_45p
}
pub fn pp_median(&self) -> Option<Price> {
self.pp_median
}
pub fn pp_55p(&self) -> Option<Price> {
self.pp_55p
}
pub fn pp_60p(&self) -> Option<Price> {
self.pp_60p
}
pub fn pp_65p(&self) -> Option<Price> {
self.pp_65p
}
pub fn pp_70p(&self) -> Option<Price> {
self.pp_70p
}
pub fn pp_75p(&self) -> Option<Price> {
self.pp_75p
}
pub fn pp_80p(&self) -> Option<Price> {
self.pp_80p
}
pub fn pp_85p(&self) -> Option<Price> {
self.pp_85p
}
pub fn pp_90p(&self) -> Option<Price> {
self.pp_90p
}
pub fn pp_95p(&self) -> Option<Price> {
self.pp_95p
}
pub fn iterate(&mut self, price: Price, amount: Amount, supply: Amount) {
let PricePaidState {
processed_amount: processed_supply,
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_supply += amount;
if pp_95p.is_some() {
return;
}
let processed_sat_amount = processed_supply.to_sat();
let total_sat_supply = 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);
}
}
}
@@ -0,0 +1,113 @@
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::{Amount, Price};
#[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")
}
}
}
impl<T> AddAssign for PriceToValue<T>
where
T: AddAssign + Copy,
{
fn add_assign(&mut self, rhs: Self) {
rhs.0.into_iter().for_each(|(key, value)| {
self.0
.entry(key)
.and_modify(|previous| *previous += value)
.or_insert(value);
});
}
}
pub trait CanSubtract {
fn can_subtract(&self, other: &Self) -> bool;
}
impl CanSubtract for Amount {
fn can_subtract(&self, other: &Self) -> bool {
self >= other
}
}
pub trait IsZero {
fn is_zero(&self) -> color_eyre::Result<bool>;
}
impl IsZero for Amount {
fn is_zero(&self) -> color_eyre::Result<bool> {
Ok(*self == Amount::ZERO)
}
}
@@ -0,0 +1,54 @@
use crate::structs::Price;
#[derive(Debug, Default)]
pub struct RealizedState {
realized_profit: Price,
realized_loss: Price,
value_created: Price,
adjusted_value_created: Price,
value_destroyed: Price,
adjusted_value_destroyed: Price,
}
impl RealizedState {
pub fn realized_profit(&self) -> Price {
self.realized_profit
}
pub fn realized_loss(&self) -> Price {
self.realized_loss
}
pub fn value_created(&self) -> Price {
self.value_created
}
pub fn adjusted_value_created(&self) -> Price {
self.adjusted_value_created
}
pub fn value_destroyed(&self) -> Price {
self.value_destroyed
}
pub fn adjusted_value_destroyed(&self) -> Price {
self.adjusted_value_destroyed
}
pub fn iterate(
&mut self,
realized_profit: Price,
realized_loss: Price,
value_created: Price,
adjusted_value_created: Price,
value_destroyed: Price,
adjusted_value_destroyed: Price,
) {
self.realized_profit += realized_profit;
self.realized_loss += realized_loss;
self.value_created += value_created;
self.adjusted_value_created += adjusted_value_created;
self.value_destroyed += value_destroyed;
self.adjusted_value_destroyed += adjusted_value_destroyed;
}
}
@@ -0,0 +1,39 @@
use std::ops::AddAssign;
use allocative::Allocative;
use color_eyre::eyre::eyre;
use crate::structs::Amount;
#[derive(Debug, Default, Allocative)]
pub struct SupplyState {
supply: Amount,
}
impl SupplyState {
pub fn supply(&self) -> Amount {
self.supply
}
pub fn increment(&mut self, amount: Amount) {
self.supply += amount;
}
pub fn decrement(&mut self, amount: Amount) -> color_eyre::Result<()> {
if self.supply < amount {
dbg!(self.supply, amount);
return Err(eyre!("supply smaller than supply"));
}
self.supply -= amount;
Ok(())
}
}
impl AddAssign for SupplyState {
fn add_assign(&mut self, rhs: Self) {
self.supply += rhs.supply;
}
}
@@ -0,0 +1,50 @@
use std::{cmp::Ordering, ops::Add};
use crate::structs::{Amount, Price};
#[derive(Debug, Default)]
pub struct UnrealizedState {
supply_in_profit: Amount,
unrealized_profit: Price,
unrealized_loss: Price,
}
impl UnrealizedState {
pub fn supply_in_profit(&self) -> Amount {
self.supply_in_profit
}
pub fn unrealized_profit(&self) -> Price {
self.unrealized_profit
}
pub fn unrealized_loss(&self) -> Price {
self.unrealized_loss
}
#[inline]
pub fn iterate(&mut self, price_then: Price, price_now: Price, amount: Amount) {
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,
}
}
}
@@ -0,0 +1,37 @@
use std::ops::AddAssign;
use allocative::Allocative;
use color_eyre::eyre::eyre;
#[derive(Debug, Default, Allocative)]
pub struct UTXOState {
count: f64,
}
impl UTXOState {
pub fn count(&self) -> f64 {
self.count
}
pub fn increment(&mut self, utxo_count: f64) {
self.count += utxo_count;
}
pub fn decrement(&mut self, utxo_count: f64) -> 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(())
}
}
impl AddAssign for UTXOState {
fn add_assign(&mut self, rhs: Self) {
self.count += rhs.count;
}
}
+7
View File
@@ -0,0 +1,7 @@
mod address;
mod any;
mod utxo;
pub use address::*;
pub use any::*;
pub use utxo::*;
@@ -0,0 +1,109 @@
use allocative::Allocative;
use crate::{
parser::states::{DurableStates, OneShotStates, PriceToValue, UnrealizedState},
structs::{Amount, Price},
};
#[derive(Default, Debug, Allocative)]
pub struct UTXOCohortDurableStates {
pub durable_states: DurableStates,
pub price_to_amount: PriceToValue<Amount>,
}
impl UTXOCohortDurableStates {
pub fn increment(
&mut self,
amount: Amount,
utxo_count: f64,
price: Price,
) -> color_eyre::Result<()> {
self._crement(amount, utxo_count, price, true)
}
pub fn decrement(
&mut self,
amount: Amount,
utxo_count: f64,
price: Price,
) -> color_eyre::Result<()> {
self._crement(amount, utxo_count, price, false)
}
pub fn _crement(
&mut self,
amount: Amount,
utxo_count: f64,
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",
rounded_price,
price,
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
}
}
@@ -0,0 +1,34 @@
use crate::structs::{Epoch, Height};
pub enum UTXOFilter {
To(u32),
FromTo { from: u32, to: u32 },
From(u32),
Epoch(Epoch),
}
impl UTXOCheck for UTXOFilter {
fn check(&self, days_old: &u32, height: &Height) -> 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::Epoch(epoch) => *epoch == height.into(),
}
}
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::Epoch(_) => unreachable!(),
}
}
}
pub trait UTXOCheck {
fn check(&self, days_old: &u32, height: &Height) -> bool;
fn check_days_old(&self, days_old: &u32) -> bool;
}
@@ -0,0 +1,75 @@
use crate::structs::Epoch;
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),
epoch_1: UTXOFilter::Epoch(Epoch(1)),
epoch_2: UTXOFilter::Epoch(Epoch(2)),
epoch_3: UTXOFilter::Epoch(Epoch(3)),
epoch_4: UTXOFilter::Epoch(Epoch(4)),
epoch_5: UTXOFilter::Epoch(Epoch(5)),
sth: UTXOFilter::To(155),
lth: UTXOFilter::From(155),
};
@@ -0,0 +1,97 @@
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,
Epoch1,
Epoch2,
Epoch3,
Epoch4,
Epoch5,
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::Epoch1 => "epoch_1",
Self::Epoch2 => "epoch_2",
Self::Epoch3 => "epoch_3",
Self::Epoch4 => "epoch_4",
Self::Epoch5 => "epoch_5",
Self::ShortTermHolders => "sth",
Self::LongTermHolders => "lth",
}
}
}
@@ -0,0 +1,152 @@
use allocative::Allocative;
use derive_deref::{Deref, DerefMut};
use rayon::prelude::*;
use crate::{
parser::states::DateDataVec,
structs::{Amount, BlockData, Price, SentData, Timestamp},
};
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_block_data) = date_data_vec.last_block() {
date_data_vec.iter().for_each(|date_data| {
date_data.blocks.iter().for_each(|block_data| {
let amount = block_data.amount;
let utxo_count = block_data.utxos as f64;
// No need to either insert or remove if 0
if amount == Amount::ZERO {
return;
}
let increment_days_old = Timestamp::difference_in_days_between(
block_data.timestamp,
last_block_data.timestamp,
);
s.initial_filtered_apply(&increment_days_old, &block_data.height, |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 height = block_data.height;
let amount = block_data.amount;
let utxo_count = block_data.utxos as f64;
let price = block_data.price;
// No need to either insert or remove if 0
if amount == Amount::ZERO {
return;
}
if block_data.height == last_block_data.height {
self.initial_filtered_apply(&0, &height, |state| {
state.increment(amount, utxo_count, price).unwrap();
})
} else {
let increment_days_old = Timestamp::difference_in_days_between(
block_data.timestamp,
last_block_data.timestamp,
);
let decrement_days_old = Timestamp::difference_in_days_between(
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 f64;
// No need to either insert or remove if 0
if amount == Amount::ZERO {
return;
}
let days_old = Timestamp::difference_in_days_between(
block_data.timestamp,
previous_last_block_data.timestamp,
);
self.initial_filtered_apply(&days_old, &block_data.height, |state| {
state
.decrement(amount, utxo_count, block_data.price)
.unwrap_or_else(|report| {
dbg!(
report.to_string(),
block_data,
sent_data,
previous_last_block_data
);
panic!()
});
})
}
pub fn compute_one_shot_states(
&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
}
}
@@ -0,0 +1,8 @@
use derive_deref::{Deref, DerefMut};
use crate::parser::states::OneShotStates;
use super::SplitByUTXOCohort;
#[derive(Deref, DerefMut, Default)]
pub struct UTXOCohortsOneShotStates(pub SplitByUTXOCohort<OneShotStates>);
@@ -0,0 +1,86 @@
use std::{cmp::Ordering, collections::BTreeMap};
use derive_deref::{Deref, DerefMut};
use crate::{
parser::states::{DateDataVec, InputState, RealizedState},
structs::{BlockPath, Price, SentData, Timestamp},
};
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,
current_timestamp: Timestamp,
) {
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 block_data = date_data.get_block_data(block_path).unwrap();
let days_old = Timestamp::difference_in_days_between(
block_data.timestamp,
last_block_data.timestamp,
);
let previous_timestamp = block_data.timestamp;
let previous_price = block_data.price;
let height = block_data.height;
let amount_sent = sent_data.volume;
self.initial_filtered_apply(&days_old, &height, |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;
let mut realized_profit = Price::ZERO;
let mut realized_loss = Price::ZERO;
let value_created = current_value;
let mut adjusted_value_created = Price::ZERO;
let value_destroyed = previous_value;
let mut adjusted_value_destroyed = Price::ZERO;
match previous_value.cmp(&current_value) {
Ordering::Less => realized_profit = current_value - previous_value,
Ordering::Greater => {
realized_loss = previous_value - current_value;
}
Ordering::Equal => {}
}
if previous_timestamp.older_by_1h_plus_than(current_timestamp) {
adjusted_value_created = value_created;
adjusted_value_destroyed = value_destroyed;
}
state.realized.iterate(
realized_profit,
realized_loss,
value_created,
adjusted_value_created,
value_destroyed,
adjusted_value_destroyed,
);
})
})
}
}
}
@@ -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::*;
@@ -0,0 +1,719 @@
use allocative::Allocative;
use super::{UTXOCheck, UTXOCohortId, UTXO_FILTERS};
use crate::structs::Height;
#[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 epoch_1: T,
pub epoch_2: T,
pub epoch_3: T,
pub epoch_4: T,
pub epoch_5: 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::Epoch1 => &self.epoch_1,
UTXOCohortId::Epoch2 => &self.epoch_2,
UTXOCohortId::Epoch3 => &self.epoch_3,
UTXOCohortId::Epoch4 => &self.epoch_4,
UTXOCohortId::Epoch5 => &self.epoch_5,
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::Epoch1 => &mut self.epoch_1,
UTXOCohortId::Epoch2 => &mut self.epoch_2,
UTXOCohortId::Epoch3 => &mut self.epoch_3,
UTXOCohortId::Epoch4 => &mut self.epoch_4,
UTXOCohortId::Epoch5 => &mut self.epoch_5,
UTXOCohortId::ShortTermHolders => &mut self.sth,
UTXOCohortId::LongTermHolders => &mut self.lth,
}
}
/// Excluding epochs 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 epochs since it's the initial apply
pub fn initial_filtered_apply(
&mut self,
days_old: &u32,
height: &Height,
apply: impl Fn(&mut T),
) {
if UTXO_FILTERS.up_to_1d.check(days_old, height) {
apply(&mut self.up_to_1d);
} else if UTXO_FILTERS.from_1d_to_1w.check(days_old, height) {
apply(&mut self.from_1d_to_1w);
} else if UTXO_FILTERS.from_1w_to_1m.check(days_old, height) {
apply(&mut self.from_1w_to_1m);
} else if UTXO_FILTERS.from_1m_to_3m.check(days_old, height) {
apply(&mut self.from_1m_to_3m);
} else if UTXO_FILTERS.from_3m_to_6m.check(days_old, height) {
apply(&mut self.from_3m_to_6m);
} else if UTXO_FILTERS.from_6m_to_1y.check(days_old, height) {
apply(&mut self.from_6m_to_1y);
} else if UTXO_FILTERS.from_1y_to_2y.check(days_old, height) {
apply(&mut self.from_1y_to_2y);
} else if UTXO_FILTERS.from_2y_to_3y.check(days_old, height) {
apply(&mut self.from_2y_to_3y);
} else if UTXO_FILTERS.from_3y_to_5y.check(days_old, height) {
apply(&mut self.from_3y_to_5y);
} else if UTXO_FILTERS.from_5y_to_7y.check(days_old, height) {
apply(&mut self.from_5y_to_7y);
} else if UTXO_FILTERS.from_7y_to_10y.check(days_old, height) {
apply(&mut self.from_7y_to_10y);
} else if UTXO_FILTERS.from_10y_to_15y.check(days_old, height) {
apply(&mut self.from_10y_to_15y);
}
if UTXO_FILTERS.epoch_1.check(days_old, height) {
apply(&mut self.epoch_1);
} else if UTXO_FILTERS.epoch_2.check(days_old, height) {
apply(&mut self.epoch_2);
} else if UTXO_FILTERS.epoch_3.check(days_old, height) {
apply(&mut self.epoch_3);
} else if UTXO_FILTERS.epoch_4.check(days_old, height) {
apply(&mut self.epoch_4);
} else if UTXO_FILTERS.epoch_5.check(days_old, height) {
apply(&mut self.epoch_5);
}
if UTXO_FILTERS.sth.check(days_old, height) {
apply(&mut self.sth);
} else if UTXO_FILTERS.lth.check(days_old, height) {
apply(&mut self.lth);
} else {
unreachable!()
}
if UTXO_FILTERS.from_1y.check(days_old, height) {
apply(&mut self.from_1y);
}
if UTXO_FILTERS.from_2y.check(days_old, height) {
apply(&mut self.from_2y);
}
if UTXO_FILTERS.from_4y.check(days_old, height) {
apply(&mut self.from_4y);
}
if UTXO_FILTERS.from_10y.check(days_old, height) {
apply(&mut self.from_10y);
}
if UTXO_FILTERS.from_15y.check(days_old, height) {
apply(&mut self.from_15y);
}
if UTXO_FILTERS.up_to_15y.check(days_old, height) {
apply(&mut self.up_to_15y);
} else {
return;
}
if UTXO_FILTERS.up_to_10y.check(days_old, height) {
apply(&mut self.up_to_10y);
} else {
return;
}
if UTXO_FILTERS.up_to_7y.check(days_old, height) {
apply(&mut self.up_to_7y);
} else {
return;
}
if UTXO_FILTERS.up_to_5y.check(days_old, height) {
apply(&mut self.up_to_5y);
} else {
return;
}
if UTXO_FILTERS.up_to_3y.check(days_old, height) {
apply(&mut self.up_to_3y);
} else {
return;
}
if UTXO_FILTERS.up_to_2y.check(days_old, height) {
apply(&mut self.up_to_2y);
} else {
return;
}
if UTXO_FILTERS.up_to_1y.check(days_old, height) {
apply(&mut self.up_to_1y);
} else {
return;
}
if UTXO_FILTERS.up_to_6m.check(days_old, height) {
apply(&mut self.up_to_6m);
} else {
return;
}
if UTXO_FILTERS.up_to_5m.check(days_old, height) {
apply(&mut self.up_to_5m);
} else {
return;
}
if UTXO_FILTERS.up_to_4m.check(days_old, height) {
apply(&mut self.up_to_4m);
} else {
return;
}
if UTXO_FILTERS.up_to_3m.check(days_old, height) {
apply(&mut self.up_to_3m);
} else {
return;
}
if UTXO_FILTERS.up_to_2m.check(days_old, height) {
apply(&mut self.up_to_2m);
} else {
return;
}
if UTXO_FILTERS.up_to_1m.check(days_old, height) {
apply(&mut self.up_to_1m);
} else {
return;
}
if UTXO_FILTERS.up_to_1w.check(days_old, height) {
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.epoch_1, UTXOCohortId::Epoch1),
(&self.epoch_2, UTXOCohortId::Epoch2),
(&self.epoch_3, UTXOCohortId::Epoch3),
(&self.epoch_4, UTXOCohortId::Epoch4),
(&self.epoch_5, UTXOCohortId::Epoch5),
(&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.epoch_1, UTXOCohortId::Epoch1),
(&mut self.epoch_2, UTXOCohortId::Epoch2),
(&mut self.epoch_3, UTXOCohortId::Epoch3),
(&mut self.epoch_4, UTXOCohortId::Epoch4),
(&mut self.epoch_5, UTXOCohortId::Epoch5),
(&mut self.sth, UTXOCohortId::ShortTermHolders),
(&mut self.lth, UTXOCohortId::LongTermHolders),
]
}
}
impl<T> SplitByUTXOCohort<Option<T>> {
pub fn unwrap(self) -> SplitByUTXOCohort<T> {
SplitByUTXOCohort {
sth: self.sth.unwrap(),
lth: self.lth.unwrap(),
up_to_1d: self.up_to_1d.unwrap(),
up_to_1w: self.up_to_1w.unwrap(),
up_to_1m: self.up_to_1m.unwrap(),
up_to_2m: self.up_to_2m.unwrap(),
up_to_3m: self.up_to_3m.unwrap(),
up_to_4m: self.up_to_4m.unwrap(),
up_to_5m: self.up_to_5m.unwrap(),
up_to_6m: self.up_to_6m.unwrap(),
up_to_1y: self.up_to_1y.unwrap(),
up_to_2y: self.up_to_2y.unwrap(),
up_to_3y: self.up_to_3y.unwrap(),
up_to_5y: self.up_to_5y.unwrap(),
up_to_7y: self.up_to_7y.unwrap(),
up_to_10y: self.up_to_10y.unwrap(),
up_to_15y: self.up_to_15y.unwrap(),
from_1d_to_1w: self.from_1d_to_1w.unwrap(),
from_1w_to_1m: self.from_1w_to_1m.unwrap(),
from_1m_to_3m: self.from_1m_to_3m.unwrap(),
from_3m_to_6m: self.from_3m_to_6m.unwrap(),
from_6m_to_1y: self.from_6m_to_1y.unwrap(),
from_1y_to_2y: self.from_1y_to_2y.unwrap(),
from_2y_to_3y: self.from_2y_to_3y.unwrap(),
from_3y_to_5y: self.from_3y_to_5y.unwrap(),
from_5y_to_7y: self.from_5y_to_7y.unwrap(),
from_7y_to_10y: self.from_7y_to_10y.unwrap(),
from_10y_to_15y: self.from_10y_to_15y.unwrap(),
from_1y: self.from_1y.unwrap(),
from_2y: self.from_2y.unwrap(),
from_4y: self.from_4y.unwrap(),
from_10y: self.from_10y.unwrap(),
from_15y: self.from_15y.unwrap(),
epoch_1: self.epoch_1.unwrap(),
epoch_2: self.epoch_2.unwrap(),
epoch_3: self.epoch_3.unwrap(),
epoch_4: self.epoch_4.unwrap(),
epoch_5: self.epoch_5.unwrap(),
}
}
}
+31
View File
@@ -0,0 +1,31 @@
use allocative::Allocative;
use bincode::{Decode, Encode};
use serde::{Deserialize, Serialize};
use crate::structs::Counter;
use super::AnyState;
#[derive(Default, Debug, Encode, Decode, Serialize, Deserialize, Allocative)]
pub struct Counters {
pub multisig_addresses: Counter,
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.multisig_addresses.reset();
self.push_only_addresses.reset();
self.unknown_addresses.reset();
self.empty_addresses.reset();
}
}
+50
View File
@@ -0,0 +1,50 @@
use allocative::Allocative;
use bincode::{Decode, Encode};
use derive_deref::{Deref, DerefMut};
use serde::{Deserialize, Serialize};
use crate::structs::{BlockData, BlockPath, DateData};
use super::AnyState;
#[derive(Default, Deref, DerefMut, Debug, Serialize, Deserialize, Encode, Decode, Allocative)]
pub struct DateDataVec(Vec<DateData>);
impl DateDataVec {
pub fn last_block(&self) -> Option<&BlockData> {
self.iter().flat_map(|date_data| &date_data.blocks).last()
}
pub fn last_mut_block(&mut self) -> Option<&mut BlockData> {
self.iter_mut()
.flat_map(|date_data| &mut date_data.blocks)
.last()
}
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();
}
}
+64
View File
@@ -0,0 +1,64 @@
use std::{fs, 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 log::info;
use crate::structs::Config;
#[derive(Default, Allocative)]
pub struct States {
pub address_counters: Counters,
pub date_data_vec: DateDataVec,
pub address_cohorts_durable_states: Option<AddressCohortsDurableStates>,
pub utxo_cohorts_durable_states: Option<UTXOCohortsDurableStates>,
}
impl States {
pub fn import(config: &Config) -> color_eyre::Result<Self> {
fs::create_dir_all(config.path_states())?;
let date_data_vec = DateDataVec::import(config)?;
let address_counters = Counters::import(config)?;
Ok(Self {
address_cohorts_durable_states: None,
address_counters,
date_data_vec,
utxo_cohorts_durable_states: None,
})
}
pub fn reset(&mut self, config: &Config, include_addresses: bool) {
info!("Reseting all states...");
let _ = self.date_data_vec.reset(config);
self.utxo_cohorts_durable_states = None;
if include_addresses {
let _ = self.address_counters.reset(config);
self.address_cohorts_durable_states = None;
}
}
pub fn export(&self, config: &Config) -> color_eyre::Result<()> {
thread::scope(|s| {
s.spawn(|| self.address_counters.export(config).unwrap());
s.spawn(|| self.date_data_vec.export(config).unwrap());
});
Ok(())
}
}