global: utxos part 7

This commit is contained in:
nym21
2025-05-24 12:52:15 +02:00
parent 2883f88de6
commit 4ab10670c9
24 changed files with 4237 additions and 3385 deletions

View File

@@ -1,8 +1,8 @@
use std::ops::{Add, AddAssign, SubAssign}; use std::ops::{Add, AddAssign, SubAssign};
use brk_core::{Dollars, Sats, Timestamp}; use brk_core::{Dollars, Timestamp};
use super::{OutputsByType, SupplyState}; use super::SupplyState;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct BlockState { pub struct BlockState {
@@ -30,33 +30,3 @@ impl SubAssign<&BlockState> for BlockState {
self.supply -= &rhs.supply; self.supply -= &rhs.supply;
} }
} }
pub struct ReceivedBlockStateData<'a> {
pub received: &'a OutputsByType<(SupplyState, Vec<Sats>)>,
pub timestamp: Timestamp,
pub price: Option<Dollars>,
}
impl<'a> From<ReceivedBlockStateData<'a>> for BlockState {
fn from(
ReceivedBlockStateData {
received,
timestamp,
price,
}: ReceivedBlockStateData<'a>,
) -> Self {
let mut block_state = BlockState {
supply: SupplyState::default(),
price,
timestamp,
};
received
.spendable
.as_vec()
.into_iter()
.for_each(|spendable_block_state| {
block_state.supply += &spendable_block_state.0;
});
block_state.supply.utxos += received.unspendable.empty.0.utxos;
block_state
}
}

View File

@@ -1,29 +1,27 @@
use brk_core::{Bitcoin, CheckedSub, Dollars}; use brk_core::Dollars;
use super::SupplyState; use super::{RealizedState, SupplyState};
// Vecs ? probably // Vecs ? probably
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
pub struct CohortState { pub struct CohortState {
pub supply: SupplyState, pub supply: SupplyState,
pub realized_cap: Option<Dollars>, pub realized: Option<RealizedState>,
// pub price_to_amount: PriceToValue<Amount>, save it not rounded in fjall // pub price_to_amount: PriceToValue<Amount>, save it not rounded in fjall
} }
impl CohortState { impl CohortState {
pub fn increment(&mut self, supply_state: &SupplyState, price: Option<Dollars>) { pub fn increment(&mut self, supply_state: &SupplyState, price: Option<Dollars>) {
self.supply += supply_state; self.supply += supply_state;
if let Some(realized_cap) = self.realized_cap.as_mut() { if let Some(realized) = self.realized.as_mut() {
*realized_cap += price.unwrap() * Bitcoin::from(supply_state.value); realized.increment(supply_state, price.unwrap());
} }
} }
pub fn decrement(&mut self, supply_state: &SupplyState, price: Option<Dollars>) { pub fn decrement(&mut self, supply_state: &SupplyState, price: Option<Dollars>) {
if let Some(realized_cap) = self.realized_cap.as_mut() {
*realized_cap = realized_cap
.checked_sub(price.unwrap() * Bitcoin::from(supply_state.value))
.unwrap();
}
self.supply -= supply_state; self.supply -= supply_state;
if let Some(realized) = self.realized.as_mut() {
realized.decrement(supply_state, price.unwrap());
}
} }
} }

View File

@@ -1,13 +1,15 @@
mod block; mod block;
mod cohort; mod cohort;
mod outputs; mod outputs;
// mod realized; mod realized;
// mod hot; // mod hot;
mod supply; mod supply;
mod transacted;
pub use block::*; pub use block::*;
pub use cohort::*; pub use cohort::*;
pub use outputs::*; pub use outputs::*;
// pub use realized::*; pub use realized::*;
// pub use hot::*; // pub use hot::*;
pub use supply::*; pub use supply::*;
pub use transacted::*;

View File

@@ -1,9 +1,8 @@
use brk_core::Sats;
use super::OutputFilter; use super::OutputFilter;
#[derive(Default, Clone)] #[derive(Default, Clone)]
pub struct OutputsBySize<T> { pub struct OutputsBySize<T> {
pub _0sat: T,
pub from_1sat_to_10sats: T, pub from_1sat_to_10sats: T,
pub from_10sats_to_100sats: T, pub from_10sats_to_100sats: T,
pub from_100sats_to_1_000sats: T, pub from_100sats_to_1_000sats: T,
@@ -24,60 +23,79 @@ impl<T> From<OutputsBySize<T>> for OutputsBySize<(OutputFilter, T)> {
fn from(value: OutputsBySize<T>) -> Self { fn from(value: OutputsBySize<T>) -> Self {
#[allow(clippy::inconsistent_digit_grouping)] #[allow(clippy::inconsistent_digit_grouping)]
Self { Self {
_0sat: (
// OutputFilter::Zero,
OutputFilter::Size,
value._0sat,
),
from_1sat_to_10sats: ( from_1sat_to_10sats: (
OutputFilter::Size(Sats::new(1)..Sats::new(10)), // OutputFilter::Size(Sats::new(1)..Sats::new(10)),
OutputFilter::Size,
value.from_1sat_to_10sats, value.from_1sat_to_10sats,
), ),
from_10sats_to_100sats: ( from_10sats_to_100sats: (
OutputFilter::Size(Sats::new(10)..Sats::new(100)), // OutputFilter::Size(Sats::new(10)..Sats::new(100)),
OutputFilter::Size,
value.from_10sats_to_100sats, value.from_10sats_to_100sats,
), ),
from_100sats_to_1_000sats: ( from_100sats_to_1_000sats: (
OutputFilter::Size(Sats::new(100)..Sats::new(1_000)), // OutputFilter::Size(Sats::new(100)..Sats::new(1_000)),
OutputFilter::Size,
value.from_100sats_to_1_000sats, value.from_100sats_to_1_000sats,
), ),
from_1_000sats_to_10_000sats: ( from_1_000sats_to_10_000sats: (
OutputFilter::Size(Sats::new(1_000)..Sats::new(10_000)), // OutputFilter::Size(Sats::new(1_000)..Sats::new(10_000)),
OutputFilter::Size,
value.from_1_000sats_to_10_000sats, value.from_1_000sats_to_10_000sats,
), ),
from_10_000sats_to_100_000sats: ( from_10_000sats_to_100_000sats: (
OutputFilter::Size(Sats::new(10_000)..Sats::new(100_000)), // OutputFilter::Size(Sats::new(10_000)..Sats::new(100_000)),
OutputFilter::Size,
value.from_10_000sats_to_100_000sats, value.from_10_000sats_to_100_000sats,
), ),
from_100_000sats_to_1_000_000sats: ( from_100_000sats_to_1_000_000sats: (
OutputFilter::Size(Sats::new(100_000)..Sats::new(1_000_000)), // OutputFilter::Size(Sats::new(100_000)..Sats::new(1_000_000)),
OutputFilter::Size,
value.from_100_000sats_to_1_000_000sats, value.from_100_000sats_to_1_000_000sats,
), ),
from_1_000_000sats_to_10_000_000sats: ( from_1_000_000sats_to_10_000_000sats: (
OutputFilter::Size(Sats::new(1_000_000)..Sats::new(10_000_000)), // OutputFilter::Size(Sats::new(1_000_000)..Sats::new(10_000_000)),
OutputFilter::Size,
value.from_1_000_000sats_to_10_000_000sats, value.from_1_000_000sats_to_10_000_000sats,
), ),
from_10_000_000sats_to_1btc: ( from_10_000_000sats_to_1btc: (
OutputFilter::Size(Sats::new(10_000_000)..Sats::new(1_00_000_000)), // OutputFilter::Size(Sats::new(10_000_000)..Sats::new(1_00_000_000)),
OutputFilter::Size,
value.from_10_000_000sats_to_1btc, value.from_10_000_000sats_to_1btc,
), ),
from_1btc_to_10btc: ( from_1btc_to_10btc: (
OutputFilter::Size(Sats::new(1_00_000_000)..Sats::new(10_00_000_000)), // OutputFilter::Size(Sats::new(1_00_000_000)..Sats::new(10_00_000_000)),
OutputFilter::Size,
value.from_1btc_to_10btc, value.from_1btc_to_10btc,
), ),
from_10btc_to_100btc: ( from_10btc_to_100btc: (
OutputFilter::Size(Sats::new(10_00_000_000)..Sats::new(100_00_000_000)), // OutputFilter::Size(Sats::new(10_00_000_000)..Sats::new(100_00_000_000)),
OutputFilter::Size,
value.from_10btc_to_100btc, value.from_10btc_to_100btc,
), ),
from_100btc_to_1_000btc: ( from_100btc_to_1_000btc: (
OutputFilter::Size(Sats::new(100_00_000_000)..Sats::new(1_000_00_000_000)), // OutputFilter::Size(Sats::new(100_00_000_000)..Sats::new(1_000_00_000_000)),
OutputFilter::Size,
value.from_100btc_to_1_000btc, value.from_100btc_to_1_000btc,
), ),
from_1_000btc_to_10_000btc: ( from_1_000btc_to_10_000btc: (
OutputFilter::Size(Sats::new(1_000_00_000_000)..Sats::new(10_000_00_000_000)), // OutputFilter::Size(Sats::new(1_000_00_000_000)..Sats::new(10_000_00_000_000)),
OutputFilter::Size,
value.from_1_000btc_to_10_000btc, value.from_1_000btc_to_10_000btc,
), ),
from_10_000btc_to_100_000btc: ( from_10_000btc_to_100_000btc: (
OutputFilter::Size(Sats::new(10_000_00_000_000)..Sats::new(100_000_00_000_000)), // OutputFilter::Size(Sats::new(10_000_00_000_000)..Sats::new(100_000_00_000_000)),
OutputFilter::Size,
value.from_10_000btc_to_100_000btc, value.from_10_000btc_to_100_000btc,
), ),
from_100_000btc: ( from_100_000btc: (
OutputFilter::Size(Sats::new(100_000_00_000_000)..Sats::MAX), // OutputFilter::Size(Sats::new(100_000_00_000_000)..Sats::MAX),
OutputFilter::Size,
value.from_100_000btc, value.from_100_000btc,
), ),
} }
@@ -85,8 +103,44 @@ impl<T> From<OutputsBySize<T>> for OutputsBySize<(OutputFilter, T)> {
} }
impl<T> OutputsBySize<T> { impl<T> OutputsBySize<T> {
pub fn as_mut_vec(&mut self) -> [&mut T; 14] { #[allow(clippy::inconsistent_digit_grouping)]
pub fn get_mut(&mut self, group: usize) -> &mut T {
if group == 0 {
&mut self._0sat
} else if group == 10 {
&mut self.from_1sat_to_10sats
} else if group == 100 {
&mut self.from_10sats_to_100sats
} else if group == 1_000 {
&mut self.from_100sats_to_1_000sats
} else if group == 10_000 {
&mut self.from_1_000sats_to_10_000sats
} else if group == 100_000 {
&mut self.from_10_000sats_to_100_000sats
} else if group == 1_000_000 {
&mut self.from_100_000sats_to_1_000_000sats
} else if group == 10_000_000 {
&mut self.from_1_000_000sats_to_10_000_000sats
} else if group == 1_00_000_000 {
&mut self.from_10_000_000sats_to_1btc
} else if group == 10_00_000_000 {
&mut self.from_1btc_to_10btc
} else if group == 100_00_000_000 {
&mut self.from_10btc_to_100btc
} else if group == 1_000_00_000_000 {
&mut self.from_100btc_to_1_000btc
} else if group == 10_000_00_000_000 {
&mut self.from_1_000btc_to_10_000btc
} else if group == 100_000_00_000_000 {
&mut self.from_10_000btc_to_100_000btc
} else {
&mut self.from_100_000btc
}
}
pub fn as_mut_vec(&mut self) -> [&mut T; 15] {
[ [
&mut self._0sat,
&mut self.from_1sat_to_10sats, &mut self.from_1sat_to_10sats,
&mut self.from_10sats_to_100sats, &mut self.from_10sats_to_100sats,
&mut self.from_100sats_to_1_000sats, &mut self.from_100sats_to_1_000sats,
@@ -106,8 +160,9 @@ impl<T> OutputsBySize<T> {
} }
impl<T> OutputsBySize<(OutputFilter, T)> { impl<T> OutputsBySize<(OutputFilter, T)> {
pub fn vecs(&self) -> [&T; 14] { pub fn vecs(&self) -> [&T; 15] {
[ [
&self._0sat.1,
&self.from_1sat_to_10sats.1, &self.from_1sat_to_10sats.1,
&self.from_10sats_to_100sats.1, &self.from_10sats_to_100sats.1,
&self.from_100sats_to_1_000sats.1, &self.from_100sats_to_1_000sats.1,

View File

@@ -1,6 +1,6 @@
use brk_core::{OutputType, Sats}; use std::ops::{Add, AddAssign};
use crate::states::SupplyState; use brk_core::OutputType;
use super::OutputFilter; use super::OutputFilter;
@@ -16,24 +16,26 @@ pub struct OutputsBySpendableType<T> {
pub p2tr: T, pub p2tr: T,
pub p2a: T, pub p2a: T,
pub unknown: T, pub unknown: T,
pub empty: T,
} }
impl<T> OutputsBySpendableType<T> { impl<T> OutputsBySpendableType<T> {
pub fn get(&self, output_type: OutputType) -> &T { // pub fn get(&self, output_type: OutputType) -> &T {
match output_type { // match output_type {
OutputType::P2PK65 => &self.p2pk65, // OutputType::P2PK65 => &self.p2pk65,
OutputType::P2PK33 => &self.p2pk33, // OutputType::P2PK33 => &self.p2pk33,
OutputType::P2PKH => &self.p2pkh, // OutputType::P2PKH => &self.p2pkh,
OutputType::P2MS => &self.p2ms, // OutputType::P2MS => &self.p2ms,
OutputType::P2SH => &self.p2sh, // OutputType::P2SH => &self.p2sh,
OutputType::P2WPKH => &self.p2wpkh, // OutputType::P2WPKH => &self.p2wpkh,
OutputType::P2WSH => &self.p2wsh, // OutputType::P2WSH => &self.p2wsh,
OutputType::P2TR => &self.p2tr, // OutputType::P2TR => &self.p2tr,
OutputType::P2A => &self.p2a, // OutputType::P2A => &self.p2a,
OutputType::Unknown => &self.unknown, // OutputType::Unknown => &self.unknown,
_ => unreachable!(), // OutputType::Empty => &self.empty,
} // _ => unreachable!(),
} // }
// }
pub fn get_mut(&mut self, output_type: OutputType) -> &mut T { pub fn get_mut(&mut self, output_type: OutputType) -> &mut T {
match output_type { match output_type {
@@ -47,26 +49,28 @@ impl<T> OutputsBySpendableType<T> {
OutputType::P2TR => &mut self.p2tr, OutputType::P2TR => &mut self.p2tr,
OutputType::P2A => &mut self.p2a, OutputType::P2A => &mut self.p2a,
OutputType::Unknown => &mut self.unknown, OutputType::Unknown => &mut self.unknown,
OutputType::Empty => &mut self.empty,
_ => unreachable!(), _ => unreachable!(),
} }
} }
pub fn as_vec(&self) -> [&T; 10] { // pub fn as_vec(&self) -> [&T; 11] {
[ // [
&self.p2pk65, // &self.p2pk65,
&self.p2pk33, // &self.p2pk33,
&self.p2pkh, // &self.p2pkh,
&self.p2ms, // &self.p2ms,
&self.p2sh, // &self.p2sh,
&self.p2wpkh, // &self.p2wpkh,
&self.p2wsh, // &self.p2wsh,
&self.p2tr, // &self.p2tr,
&self.p2a, // &self.p2a,
&self.unknown, // &self.unknown,
] // &self.empty,
} // ]
// }
pub fn as_mut_vec(&mut self) -> [&mut T; 10] { pub fn as_mut_vec(&mut self) -> [&mut T; 11] {
[ [
&mut self.p2pk65, &mut self.p2pk65,
&mut self.p2pk33, &mut self.p2pk33,
@@ -78,10 +82,11 @@ impl<T> OutputsBySpendableType<T> {
&mut self.p2tr, &mut self.p2tr,
&mut self.p2a, &mut self.p2a,
&mut self.unknown, &mut self.unknown,
&mut self.empty,
] ]
} }
pub fn as_typed_vec(&self) -> [(OutputType, &T); 10] { pub fn as_typed_vec(&self) -> [(OutputType, &T); 11] {
[ [
(OutputType::P2PK65, &self.p2pk65), (OutputType::P2PK65, &self.p2pk65),
(OutputType::P2PK33, &self.p2pk33), (OutputType::P2PK33, &self.p2pk33),
@@ -93,12 +98,13 @@ impl<T> OutputsBySpendableType<T> {
(OutputType::P2TR, &self.p2tr), (OutputType::P2TR, &self.p2tr),
(OutputType::P2A, &self.p2a), (OutputType::P2A, &self.p2a),
(OutputType::Unknown, &self.unknown), (OutputType::Unknown, &self.unknown),
(OutputType::Empty, &self.empty),
] ]
} }
} }
impl<T> OutputsBySpendableType<(OutputFilter, T)> { impl<T> OutputsBySpendableType<(OutputFilter, T)> {
pub fn vecs(&self) -> [&T; 10] { pub fn vecs(&self) -> [&T; 11] {
[ [
&self.p2pk65.1, &self.p2pk65.1,
&self.p2pk33.1, &self.p2pk33.1,
@@ -110,6 +116,7 @@ impl<T> OutputsBySpendableType<(OutputFilter, T)> {
&self.p2tr.1, &self.p2tr.1,
&self.p2a.1, &self.p2a.1,
&self.unknown.1, &self.unknown.1,
&self.empty.1,
] ]
} }
} }
@@ -127,16 +134,48 @@ impl<T> From<OutputsBySpendableType<T>> for OutputsBySpendableType<(OutputFilter
p2tr: (OutputFilter::Type(OutputType::P2TR), value.p2tr), p2tr: (OutputFilter::Type(OutputType::P2TR), value.p2tr),
p2a: (OutputFilter::Type(OutputType::P2A), value.p2a), p2a: (OutputFilter::Type(OutputType::P2A), value.p2a),
unknown: (OutputFilter::Type(OutputType::Unknown), value.unknown), unknown: (OutputFilter::Type(OutputType::Unknown), value.unknown),
empty: (OutputFilter::Type(OutputType::Empty), value.empty),
} }
} }
} }
impl OutputsBySpendableType<(SupplyState, Vec<Sats>)> { impl<T> Add for OutputsBySpendableType<T>
pub fn reduce(&self) -> SupplyState { where
let mut supply_state = SupplyState::default(); T: Add<Output = T>,
self.as_vec().iter().for_each(|(supply_state_, _)| { {
supply_state += supply_state_; type Output = Self;
}); fn add(self, rhs: Self) -> Self::Output {
supply_state Self {
p2pk65: self.p2pk65 + rhs.p2pk65,
p2pk33: self.p2pk33 + rhs.p2pk33,
p2pkh: self.p2pkh + rhs.p2pkh,
p2ms: self.p2ms + rhs.p2ms,
p2sh: self.p2sh + rhs.p2sh,
p2wpkh: self.p2wpkh + rhs.p2wpkh,
p2wsh: self.p2wsh + rhs.p2wsh,
p2tr: self.p2tr + rhs.p2tr,
p2a: self.p2a + rhs.p2a,
unknown: self.unknown + rhs.unknown,
empty: self.empty + rhs.empty,
}
}
}
impl<T> AddAssign for OutputsBySpendableType<T>
where
T: AddAssign,
{
fn add_assign(&mut self, rhs: Self) {
self.p2pk65 += rhs.p2pk65;
self.p2pk33 += rhs.p2pk33;
self.p2pkh += rhs.p2pkh;
self.p2ms += rhs.p2ms;
self.p2sh += rhs.p2sh;
self.p2wpkh += rhs.p2wpkh;
self.p2wsh += rhs.p2wsh;
self.p2tr += rhs.p2tr;
self.p2a += rhs.p2a;
self.unknown += rhs.unknown;
self.empty += rhs.empty;
} }
} }

View File

@@ -1,3 +1,5 @@
use std::ops::{Add, AddAssign};
use brk_core::OutputType; use brk_core::OutputType;
use super::{OutputsBySpendableType, OutputsByUnspendableType}; use super::{OutputsBySpendableType, OutputsByUnspendableType};
@@ -9,22 +11,22 @@ pub struct OutputsByType<T> {
} }
impl<T> OutputsByType<T> { impl<T> OutputsByType<T> {
// pub fn get(&self, output_type: OutputType) -> &T { pub fn get(&self, output_type: OutputType) -> &T {
// match output_type { match output_type {
// OutputType::P2PK65 => &self.spendable.p2pk65, OutputType::P2PK65 => &self.spendable.p2pk65,
// OutputType::P2PK33 => &self.spendable.p2pk33, OutputType::P2PK33 => &self.spendable.p2pk33,
// OutputType::P2PKH => &self.spendable.p2pkh, OutputType::P2PKH => &self.spendable.p2pkh,
// OutputType::P2MS => &self.spendable.p2ms, OutputType::P2MS => &self.spendable.p2ms,
// OutputType::P2SH => &self.spendable.p2sh, OutputType::P2SH => &self.spendable.p2sh,
// OutputType::P2WPKH => &self.spendable.p2wpkh, OutputType::P2WPKH => &self.spendable.p2wpkh,
// OutputType::P2WSH => &self.spendable.p2wsh, OutputType::P2WSH => &self.spendable.p2wsh,
// OutputType::P2TR => &self.spendable.p2tr, OutputType::P2TR => &self.spendable.p2tr,
// OutputType::P2A => &self.spendable.p2a, OutputType::P2A => &self.spendable.p2a,
// OutputType::OpReturn => &self.unspendable.op_return, OutputType::Empty => &self.spendable.empty,
// OutputType::Empty => &self.unspendable.empty, OutputType::Unknown => &self.spendable.unknown,
// OutputType::Unknown => &self.unspendable.unknown, OutputType::OpReturn => &self.unspendable.op_return,
// } }
// } }
pub fn get_mut(&mut self, output_type: OutputType) -> &mut T { pub fn get_mut(&mut self, output_type: OutputType) -> &mut T {
match output_type { match output_type {
@@ -38,18 +40,18 @@ impl<T> OutputsByType<T> {
OutputType::P2TR => &mut self.spendable.p2tr, OutputType::P2TR => &mut self.spendable.p2tr,
OutputType::P2A => &mut self.spendable.p2a, OutputType::P2A => &mut self.spendable.p2a,
OutputType::Unknown => &mut self.spendable.unknown, OutputType::Unknown => &mut self.spendable.unknown,
OutputType::Empty => &mut self.spendable.empty,
OutputType::OpReturn => &mut self.unspendable.op_return, OutputType::OpReturn => &mut self.unspendable.op_return,
OutputType::Empty => &mut self.unspendable.empty,
} }
} }
pub fn as_vec(&self) -> Vec<&T> { // pub fn as_vec(&self) -> Vec<&T> {
self.spendable // self.spendable
.as_vec() // .as_vec()
.into_iter() // .into_iter()
.chain(self.unspendable.as_vec()) // .chain(self.unspendable.as_vec())
.collect::<Vec<_>>() // .collect::<Vec<_>>()
} // }
// pub fn as_mut_vec(&mut self) -> Vec<&mut T> { // pub fn as_mut_vec(&mut self) -> Vec<&mut T> {
// self.spendable // self.spendable
@@ -59,3 +61,26 @@ impl<T> OutputsByType<T> {
// .collect::<Vec<_>>() // .collect::<Vec<_>>()
// } // }
} }
impl<T> Add for OutputsByType<T>
where
T: Add<Output = T>,
{
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self {
spendable: self.spendable + rhs.spendable,
unspendable: self.unspendable + rhs.unspendable,
}
}
}
impl<T> AddAssign for OutputsByType<T>
where
T: AddAssign,
{
fn add_assign(&mut self, rhs: Self) {
self.spendable += rhs.spendable;
self.unspendable += rhs.unspendable;
}
}

View File

@@ -1,47 +1,33 @@
// use brk_core::OutputType; use std::ops::{Add, AddAssign};
#[derive(Default, Clone)] #[derive(Default, Clone)]
pub struct OutputsByUnspendableType<T> { pub struct OutputsByUnspendableType<T> {
pub op_return: T, pub op_return: T,
pub empty: T,
// pub unknown: T,
} }
impl<T> OutputsByUnspendableType<T> { impl<T> OutputsByUnspendableType<T> {
// pub fn get(&self, output_type: OutputType) -> &T { pub fn as_vec(&self) -> [&T; 1] {
// match output_type { [&self.op_return]
// OutputType::OpReturn => &self.op_return, }
// OutputType::Empty => &self.empty, }
// OutputType::Unknown => &self.unknown,
// _ => unreachable!(), impl<T> Add for OutputsByUnspendableType<T>
// } where
// } T: Add<Output = T>,
{
// pub fn get_mut(&mut self, output_type: OutputType) -> &mut T { type Output = Self;
// match output_type { fn add(self, rhs: Self) -> Self::Output {
// OutputType::OpReturn => &mut self.op_return, Self {
// OutputType::Empty => &mut self.empty, op_return: self.op_return + rhs.op_return,
// OutputType::Unknown => &mut self.unknown, }
// _ => unreachable!(), }
// } }
// }
impl<T> AddAssign for OutputsByUnspendableType<T>
// pub fn to_unspendable_vec(&self) -> Vec<&T> { where
// OutputType::as_vec() T: AddAssign,
// .into_iter() {
// .map(|t| (self.get(t))) fn add_assign(&mut self, rhs: Self) {
// .collect::<Vec<_>>() self.op_return += rhs.op_return;
// }
pub fn as_vec(&self) -> [&T; 2] {
[
&self.op_return,
&self.empty,
// &self.unknown
]
} }
// pub fn as_mut_vec(&mut self) -> [&mut T; 3] {
// [&mut self.op_return, &mut self.empty, &mut self.unknown]
// }
} }

View File

@@ -1,6 +1,6 @@
use std::ops::Range; use std::ops::Range;
use brk_core::{HalvingEpoch, OutputType, Sats}; use brk_core::{HalvingEpoch, OutputType};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum OutputFilter { pub enum OutputFilter {
@@ -8,7 +8,7 @@ pub enum OutputFilter {
To(usize), To(usize),
Range(Range<usize>), Range(Range<usize>),
From(usize), From(usize),
Size(Range<Sats>), Size,
Epoch(HalvingEpoch), Epoch(HalvingEpoch),
Type(OutputType), Type(OutputType),
} }

View File

@@ -2,7 +2,7 @@ use brk_vec::StoredIndex;
use rayon::prelude::*; use rayon::prelude::*;
use std::{collections::BTreeMap, ops::ControlFlow}; use std::{collections::BTreeMap, ops::ControlFlow};
use brk_core::{Dollars, HalvingEpoch, Height, Sats, Timestamp}; use brk_core::{Dollars, HalvingEpoch, Height, Timestamp};
mod by_epoch; mod by_epoch;
mod by_from; mod by_from;
@@ -30,7 +30,7 @@ pub use filter::*;
use crate::vecs; use crate::vecs;
use super::{BlockState, SupplyState}; use super::{BlockState, Transacted};
#[derive(Default, Clone)] #[derive(Default, Clone)]
pub struct Outputs<T> { pub struct Outputs<T> {
@@ -38,12 +38,12 @@ pub struct Outputs<T> {
pub by_term: OutputsByTerm<T>, pub by_term: OutputsByTerm<T>,
// pub by_up_to: OutputsByUpTo<T>, // pub by_up_to: OutputsByUpTo<T>,
pub by_from: OutputsByFrom<T>, pub by_from: OutputsByFrom<T>,
// pub by_range: OutputsByRange<T>, pub by_range: OutputsByRange<T>,
pub by_epoch: OutputsByEpoch<T>, pub by_epoch: OutputsByEpoch<T>,
pub by_type: OutputsBySpendableType<T>,
pub by_size: OutputsBySize<T>, pub by_size: OutputsBySize<T>,
// // Needs whole UTXO set, TODO later // // Needs whole UTXO set, TODO later
// // pub by_value: OutputsByValue<T>, // // pub by_value: OutputsByValue<T>,
pub by_spendable_type: OutputsBySpendableType<T>,
} }
impl<T> Outputs<T> { impl<T> Outputs<T> {
@@ -53,11 +53,11 @@ impl<T> Outputs<T> {
.chain(self.by_term.as_mut_vec()) .chain(self.by_term.as_mut_vec())
// .chain(self.by_up_to.as_mut_vec()) // .chain(self.by_up_to.as_mut_vec())
.chain(self.by_from.as_mut_vec()) .chain(self.by_from.as_mut_vec())
// .chain(self.by_range.as_mut_vec()) .chain(self.by_range.as_mut_vec())
.chain(self.by_epoch.as_mut_vec()) .chain(self.by_epoch.as_mut_vec())
.chain(self.by_size.as_mut_vec()) .chain(self.by_size.as_mut_vec())
.chain(self.by_type.as_mut_vec())
// // .chain(self.by_value.as_mut_vec()) // // .chain(self.by_value.as_mut_vec())
.chain(self.by_spendable_type.as_mut_vec())
.collect::<Vec<_>>() .collect::<Vec<_>>()
} }
} }
@@ -75,7 +75,7 @@ impl Outputs<(OutputFilter, vecs::utxos::cohort::Vecs)> {
.into_par_iter() .into_par_iter()
// .chain(self.by_up_to.as_mut_vec()) // .chain(self.by_up_to.as_mut_vec())
.chain(self.by_from.as_mut_vec()) .chain(self.by_from.as_mut_vec())
// .chain(self.by_range.as_mut_vec()) .chain(self.by_range.as_mut_vec())
.for_each(|(filter, v)| { .for_each(|(filter, v)| {
let state = &mut v.state; let state = &mut v.state;
@@ -86,7 +86,7 @@ impl Outputs<(OutputFilter, vecs::utxos::cohort::Vecs)> {
OutputFilter::Range(range) => range.contains(&days_old), OutputFilter::Range(range) => range.contains(&days_old),
OutputFilter::All OutputFilter::All
| OutputFilter::Epoch(_) | OutputFilter::Epoch(_)
| OutputFilter::Size(_) | OutputFilter::Size
| OutputFilter::Type(_) => unreachable!(), | OutputFilter::Type(_) => unreachable!(),
} }
}; };
@@ -98,12 +98,14 @@ impl Outputs<(OutputFilter, vecs::utxos::cohort::Vecs)> {
.timestamp .timestamp
.difference_in_days_between(prev_timestamp); .difference_in_days_between(prev_timestamp);
let days_old = block_state.timestamp.difference_in_days_between(timestamp); let days_old = block_state.timestamp.difference_in_days_between(timestamp);
if prev_days_old == days_old { if prev_days_old == days_old {
return ControlFlow::Continue(()); return ControlFlow::Continue(());
} }
let is = check_days_old(days_old); let is = check_days_old(days_old);
let was = check_days_old(prev_days_old); let was = check_days_old(prev_days_old);
if is && !was { if is && !was {
state.increment(&block_state.supply, block_state.price); state.increment(&block_state.supply, block_state.price);
} else if was && !is { } else if was && !is {
@@ -117,7 +119,7 @@ impl Outputs<(OutputFilter, vecs::utxos::cohort::Vecs)> {
pub fn send( pub fn send(
&mut self, &mut self,
height_to_sent: BTreeMap<Height, OutputsByType<(SupplyState, Vec<Sats>)>>, height_to_sent: BTreeMap<Height, Transacted>,
chain_state: &[BlockState], chain_state: &[BlockState],
) { ) {
let mut time_based_vecs = self let mut time_based_vecs = self
@@ -126,58 +128,21 @@ impl Outputs<(OutputFilter, vecs::utxos::cohort::Vecs)> {
.into_iter() .into_iter()
// .chain(self.by_up_to.as_mut_vec()) // .chain(self.by_up_to.as_mut_vec())
.chain(self.by_from.as_mut_vec()) .chain(self.by_from.as_mut_vec())
// .chain(self.by_range.as_mut_vec()) .chain(self.by_range.as_mut_vec())
.chain(self.by_epoch.as_mut_vec()) .chain(self.by_epoch.as_mut_vec())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let last_timestamp = chain_state.last().unwrap().timestamp; let last_timestamp = chain_state.last().unwrap().timestamp;
height_to_sent.into_iter().for_each(|(height, by_type)| { height_to_sent.into_iter().for_each(|(height, sent)| {
let by_spendable_type = by_type.spendable;
let block_state = chain_state.get(height.unwrap_to_usize()).unwrap(); let block_state = chain_state.get(height.unwrap_to_usize()).unwrap();
let price = block_state.price; let price = block_state.price;
let supply_state = by_spendable_type.reduce();
let days_old = block_state let days_old = block_state
.timestamp .timestamp
.difference_in_days_between(last_timestamp); .difference_in_days_between(last_timestamp);
self.all.1.state.decrement(&supply_state, price); self.all.1.state.decrement(&sent.spendable_supply, price);
by_spendable_type.as_typed_vec().into_iter().for_each(
|(output_type, (supply_state, _))| {
self.by_spendable_type
.get_mut(output_type)
.1
.state
.decrement(supply_state, price)
},
);
by_spendable_type
.as_vec()
.into_iter()
.flat_map(|(_, sats_received)| sats_received.iter())
.for_each(|sats| {
let sats = *sats;
self.by_size
.as_mut_vec()
.iter_mut()
.filter(|(filter, _)| match filter {
OutputFilter::Size(range) => range.contains(&sats),
_ => unreachable!(),
})
.for_each(|(_, vec)| {
vec.state.decrement(
&SupplyState {
utxos: 1,
value: sats,
},
price,
);
});
});
time_based_vecs time_based_vecs
.iter_mut() .iter_mut()
@@ -189,18 +154,31 @@ impl Outputs<(OutputFilter, vecs::utxos::cohort::Vecs)> {
_ => unreachable!(), _ => unreachable!(),
}) })
.for_each(|(_, vecs)| { .for_each(|(_, vecs)| {
vecs.state.decrement(&supply_state, price); vecs.state.decrement(&sent.spendable_supply, price);
}); });
sent.by_type.spendable.as_typed_vec().into_iter().for_each(
|(output_type, supply_state)| {
self.by_type
.get_mut(output_type)
.1
.state
.decrement(supply_state, price)
},
);
sent.by_size.into_iter().for_each(|(group, supply_state)| {
self.by_size
.get_mut(group)
.1
.state
.decrement(&supply_state, price);
});
}); });
} }
pub fn receive( pub fn receive(&mut self, received: Transacted, height: Height, price: Option<Dollars>) {
&mut self, let supply_state = received.spendable_supply;
received: OutputsByType<(SupplyState, Vec<Sats>)>,
height: Height,
price: Option<Dollars>,
) {
let supply_state = received.spendable.reduce();
[ [
&mut self.all.1, &mut self.all.1,
@@ -214,34 +192,7 @@ impl Outputs<(OutputFilter, vecs::utxos::cohort::Vecs)> {
v.state.increment(&supply_state, price); v.state.increment(&supply_state, price);
}); });
let mut by_size = self.by_size.as_mut_vec(); self.by_type
received
.spendable
.as_vec()
.into_iter()
.flat_map(|(_, sats_received)| sats_received.iter())
.for_each(|sats| {
let sats = *sats;
by_size
.iter_mut()
.filter(|(filter, _)| match filter {
OutputFilter::Size(range) => range.contains(&sats),
_ => unreachable!(),
})
.for_each(|(_, vec)| {
vec.state.increment(
&SupplyState {
utxos: 1,
value: sats,
},
price,
);
});
});
self.by_spendable_type
.as_mut_vec() .as_mut_vec()
.into_iter() .into_iter()
.for_each(|(filter, vecs)| { .for_each(|(filter, vecs)| {
@@ -250,7 +201,18 @@ impl Outputs<(OutputFilter, vecs::utxos::cohort::Vecs)> {
_ => unreachable!(), _ => unreachable!(),
}; };
vecs.state vecs.state
.increment(&received.spendable.get(output_type).0, price) .increment(received.by_type.get(output_type), price)
});
received
.by_size
.into_iter()
.for_each(|(group, supply_state)| {
self.by_size
.get_mut(group)
.1
.state
.increment(&supply_state, price);
}); });
} }
} }
@@ -262,11 +224,11 @@ impl<T> Outputs<(OutputFilter, T)> {
.chain(self.by_term.vecs()) .chain(self.by_term.vecs())
// .chain(self.by_up_to.vecs()) // .chain(self.by_up_to.vecs())
.chain(self.by_from.vecs()) .chain(self.by_from.vecs())
// .chain(self.by_range.vecs()) .chain(self.by_range.vecs())
.chain(self.by_epoch.vecs()) .chain(self.by_epoch.vecs())
.chain(self.by_size.vecs()) .chain(self.by_size.vecs())
// // .chain(self.by_value.vecs()) // // .chain(self.by_value.vecs())
.chain(self.by_spendable_type.vecs()) .chain(self.by_type.vecs())
.collect::<Vec<_>>() .collect::<Vec<_>>()
} }
} }
@@ -278,12 +240,12 @@ impl<T> From<Outputs<T>> for Outputs<(OutputFilter, T)> {
by_term: OutputsByTerm::from(value.by_term), by_term: OutputsByTerm::from(value.by_term),
// by_up_to: OutputsByUpTo::from(value.by_up_to), // by_up_to: OutputsByUpTo::from(value.by_up_to),
by_from: OutputsByFrom::from(value.by_from), by_from: OutputsByFrom::from(value.by_from),
// by_range: OutputsByRange::from(value.by_range), by_range: OutputsByRange::from(value.by_range),
by_epoch: OutputsByEpoch::from(value.by_epoch), by_epoch: OutputsByEpoch::from(value.by_epoch),
by_size: OutputsBySize::from(value.by_size), by_size: OutputsBySize::from(value.by_size),
// // Needs whole UTXO set, TODO later // // Needs whole UTXO set, TODO later
// // by_value: OutputsByValue<T>, // // by_value: OutputsByValue<T>,
by_spendable_type: OutputsBySpendableType::from(value.by_spendable_type), by_type: OutputsBySpendableType::from(value.by_type),
} }
} }
} }

View File

@@ -1,11 +1,61 @@
use brk_core::Dollars; use brk_core::{Bitcoin, CheckedSub, Dollars};
#[derive(Debug, Default)] use super::SupplyState;
#[derive(Debug, Default, Clone)]
pub struct RealizedState { pub struct RealizedState {
realized_profit: Dollars, pub realized_cap: Dollars,
realized_loss: Dollars, // pub realized_profit: Dollars,
value_created: Dollars, // pub realized_loss: Dollars,
adjusted_value_created: Dollars, // pub value_created: Dollars,
value_destroyed: Dollars, // pub adjusted_value_created: Dollars,
adjusted_value_destroyed: Dollars, // pub value_destroyed: Dollars,
// pub adjusted_value_destroyed: Dollars,
}
impl RealizedState {
pub const NAN: Self = Self {
realized_cap: Dollars::NAN,
// realized_profit: Dollars::NAN,
// realized_loss: Dollars::NAN,
// value_created: Dollars::NAN,
// adjusted_value_created: Dollars::NAN,
// value_destroyed: Dollars::NAN,
// adjusted_value_destroyed: Dollars::NAN,
};
pub fn increment(&mut self, supply_state: &SupplyState, price: Dollars) {
if supply_state.value.is_not_zero() {
if self.realized_cap == Dollars::NAN {
self.realized_cap = Dollars::ZERO;
}
self.realized_cap += price * Bitcoin::from(supply_state.value);
}
// if self.realized_profit == Dollars::NAN {
// self.realized_profit = Dollars::ZERO;
// }
// if self.realized_loss == Dollars::NAN {
// self.realized_loss = Dollars::ZERO;
// }
// if self.value_created == Dollars::NAN {
// self.value_created = Dollars::ZERO;
// }
// if self.adjusted_value_created == Dollars::NAN {
// self.adjusted_value_created = Dollars::ZERO;
// }
// if self.value_destroyed == Dollars::NAN {
// self.value_destroyed = Dollars::ZERO;
// }
// if self.adjusted_value_destroyed == Dollars::NAN {
// self.adjusted_value_destroyed = Dollars::ZERO;
// }
}
pub fn decrement(&mut self, supply_state: &SupplyState, price: Dollars) {
self.realized_cap = self
.realized_cap
.checked_sub(price * Bitcoin::from(supply_state.value))
.unwrap();
}
} }

View File

@@ -20,6 +20,12 @@ impl Add<SupplyState> for SupplyState {
} }
} }
impl AddAssign<SupplyState> for SupplyState {
fn add_assign(&mut self, rhs: Self) {
*self += &rhs;
}
}
impl AddAssign<&SupplyState> for SupplyState { impl AddAssign<&SupplyState> for SupplyState {
fn add_assign(&mut self, rhs: &Self) { fn add_assign(&mut self, rhs: &Self) {
self.utxos += rhs.utxos; self.utxos += rhs.utxos;

View File

@@ -0,0 +1,100 @@
use std::{
collections::BTreeMap,
mem,
ops::{Add, AddAssign},
};
use brk_core::{OutputType, Sats};
use super::{OutputsByType, SupplyState};
#[derive(Default)]
pub struct Transacted {
pub spendable_supply: SupplyState,
pub by_type: OutputsByType<SupplyState>,
pub by_size: BTreeMap<usize, SupplyState>,
}
impl Transacted {
#[allow(clippy::inconsistent_digit_grouping)]
pub fn iterate(&mut self, value: Sats, _type: OutputType) {
let supply = SupplyState { utxos: 1, value };
*self.by_type.get_mut(_type) += &supply;
if _type.is_unspendable() {
return;
}
self.spendable_supply += &supply;
let _value = usize::from(value);
// Need to be in sync with by_size !! but plenty fast (I think)
if _value == 0 {
*self.by_size.entry(0).or_default() += &supply;
} else if _value < 10 {
*self.by_size.entry(1).or_default() += &supply;
} else if _value < 100 {
*self.by_size.entry(10).or_default() += &supply;
} else if _value < 1_000 {
*self.by_size.entry(100).or_default() += &supply;
} else if _value < 10_000 {
*self.by_size.entry(1_000).or_default() += &supply;
} else if _value < 100_000 {
*self.by_size.entry(10_000).or_default() += &supply;
} else if _value < 1_000_000 {
*self.by_size.entry(100_000).or_default() += &supply;
} else if _value < 10_000_000 {
*self.by_size.entry(1_000_000).or_default() += &supply;
} else if _value < 1_00_000_000 {
*self.by_size.entry(10_000_000).or_default() += &supply;
} else if _value < 10_00_000_000 {
*self.by_size.entry(1_00_000_000).or_default() += &supply;
} else if _value < 100_00_000_000 {
*self.by_size.entry(10_00_000_000).or_default() += &supply;
} else if _value < 1_000_00_000_000 {
*self.by_size.entry(100_00_000_000).or_default() += &supply;
} else if _value < 10_000_00_000_000 {
*self.by_size.entry(1_000_00_000_000).or_default() += &supply;
} else if _value < 100_000_00_000_000 {
*self.by_size.entry(10_000_00_000_000).or_default() += &supply;
} else {
*self.by_size.entry(100_000_00_000_000).or_default() += &supply;
}
}
fn merge_by_size(
first: BTreeMap<usize, SupplyState>,
second: BTreeMap<usize, SupplyState>,
) -> BTreeMap<usize, SupplyState> {
let (mut source, to_consume) = if first.len() > second.len() {
(first, second)
} else {
(second, first)
};
to_consume.into_iter().for_each(|(k, v)| {
*source.entry(k).or_default() += &v;
});
source
}
}
impl Add for Transacted {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self {
spendable_supply: self.spendable_supply + rhs.spendable_supply,
by_type: self.by_type + rhs.by_type,
by_size: Self::merge_by_size(self.by_size, rhs.by_size),
}
}
}
impl AddAssign for Transacted {
fn add_assign(&mut self, rhs: Self) {
self.by_size = Self::merge_by_size(mem::take(&mut self.by_size), rhs.by_size);
self.spendable_supply += &rhs.spendable_supply;
self.by_type += rhs.by_type;
}
}

View File

@@ -405,6 +405,7 @@ impl ComputedVecsFromTxindex<Bitcoin> {
} }
impl ComputedVecsFromTxindex<Dollars> { impl ComputedVecsFromTxindex<Dollars> {
#[allow(clippy::too_many_arguments)]
pub fn compute_rest_from_bitcoin( pub fn compute_rest_from_bitcoin(
&mut self, &mut self,
indexer: &Indexer, indexer: &Indexer,

View File

@@ -8,7 +8,7 @@ use brk_vec::{
}; };
use crate::{ use crate::{
states::CohortState, states::{CohortState, RealizedState},
vecs::{ vecs::{
Indexes, fetched, Indexes, fetched,
grouped::{ grouped::{
@@ -55,7 +55,7 @@ impl Vecs {
let mut state = CohortState::default(); let mut state = CohortState::default();
if compute_dollars { if compute_dollars {
state.realized_cap = Some(Dollars::ZERO); state.realized = Some(RealizedState::NAN);
} }
Ok(Self { Ok(Self {
@@ -169,11 +169,9 @@ impl Vecs {
.unwrap_get_inner(prev_height); .unwrap_get_inner(prev_height);
if let Some(height_to_realized_cap) = self.height_to_realized_cap.as_mut() { if let Some(height_to_realized_cap) = self.height_to_realized_cap.as_mut() {
self.state.realized_cap = Some( self.state.realized.as_mut().unwrap().realized_cap = height_to_realized_cap
height_to_realized_cap .into_iter()
.into_iter() .unwrap_get_inner(prev_height);
.unwrap_get_inner(prev_height),
);
} }
} }
} }
@@ -215,10 +213,14 @@ impl Vecs {
if let Some(height_to_realized_cap) = self.height_to_realized_cap.as_mut() { if let Some(height_to_realized_cap) = self.height_to_realized_cap.as_mut() {
height_to_realized_cap.forced_push_at( height_to_realized_cap.forced_push_at(
height, height,
self.state.realized_cap.unwrap_or_else(|| { self.state
dbg!(&self.state); .realized
panic!(); .as_ref()
}), .unwrap_or_else(|| {
dbg!(&self.state);
panic!();
})
.realized_cap,
exit, exit,
)?; )?;
} }

View File

@@ -1,18 +1,18 @@
use std::{cmp::Ordering, collections::BTreeMap, mem, path::Path, thread}; use std::{cmp::Ordering, collections::BTreeMap, mem, path::Path, thread};
use brk_core::{Height, InputIndex, OutputIndex, Sats}; use brk_core::{Height, InputIndex, OutputIndex, OutputType, Sats};
use brk_exit::Exit; use brk_exit::Exit;
use brk_indexer::Indexer; use brk_indexer::Indexer;
use brk_vec::{ use brk_vec::{
AnyCollectableVec, AnyVec, BaseVecIterator, CollectableVec, Compressed, Computation, EagerVec, AnyCollectableVec, AnyVec, BaseVecIterator, CollectableVec, Compressed, Computation, EagerVec,
GenericStoredVec, StoredIndex, StoredVec, VecIterator, Version, GenericStoredVec, StoredIndex, StoredVec, UnsafeSlice, VecIterator, Version,
}; };
use log::info; use log::info;
use rayon::prelude::*;
use crate::states::{ use crate::states::{
BlockState, OutputFilter, Outputs, OutputsByEpoch, OutputsByFrom, OutputsByRange, BlockState, OutputFilter, Outputs, OutputsByEpoch, OutputsByFrom, OutputsByRange,
OutputsBySize, OutputsBySpendableType, OutputsByTerm, OutputsByType, OutputsByUpTo, OutputsBySize, OutputsBySpendableType, OutputsByTerm, SupplyState, Transacted,
ReceivedBlockStateData, SupplyState,
}; };
use super::{ use super::{
@@ -23,7 +23,7 @@ use super::{
pub mod cohort; pub mod cohort;
const VERSION: Version = Version::new(99); const VERSION: Version = Version::new(999);
#[derive(Clone)] #[derive(Clone)]
pub struct Vecs { pub struct Vecs {
@@ -32,6 +32,8 @@ pub struct Vecs {
// cointime,... // cointime,...
pub height_to_unspendable_supply: EagerVec<Height, Sats>, pub height_to_unspendable_supply: EagerVec<Height, Sats>,
pub indexes_to_unspendable_supply: ComputedValueVecsFromHeight, pub indexes_to_unspendable_supply: ComputedValueVecsFromHeight,
// pub height_to_opreturn_supply: EagerVec<Height, Sats>,
// pub indexes_to_opreturn_supply: ComputedValueVecsFromHeight,
utxos_vecs: Outputs<(OutputFilter, cohort::Vecs)>, utxos_vecs: Outputs<(OutputFilter, cohort::Vecs)>,
} }
@@ -391,104 +393,104 @@ impl Vecs {
fetched, fetched,
)?, )?,
}, },
// by_range: OutputsByRange { by_range: OutputsByRange {
// _1d_to_1w: cohort::Vecs::forced_import( _1d_to_1w: cohort::Vecs::forced_import(
// path, path,
// Some("from_1d_to_1w"), Some("from_1d_to_1w"),
// _computation, _computation,
// compressed, compressed,
// VERSION + Version::ZERO, VERSION + Version::ZERO,
// fetched, fetched,
// )?, )?,
// _1w_to_1m: cohort::Vecs::forced_import( _1w_to_1m: cohort::Vecs::forced_import(
// path, path,
// Some("from_1w_to_1m"), Some("from_1w_to_1m"),
// _computation, _computation,
// compressed, compressed,
// VERSION + Version::ZERO, VERSION + Version::ZERO,
// fetched, fetched,
// )?, )?,
// _1m_to_3m: cohort::Vecs::forced_import( _1m_to_3m: cohort::Vecs::forced_import(
// path, path,
// Some("from_1m_to_3m"), Some("from_1m_to_3m"),
// _computation, _computation,
// compressed, compressed,
// VERSION + Version::ZERO, VERSION + Version::ZERO,
// fetched, fetched,
// )?, )?,
// _3m_to_6m: cohort::Vecs::forced_import( _3m_to_6m: cohort::Vecs::forced_import(
// path, path,
// Some("from_3m_to_6m"), Some("from_3m_to_6m"),
// _computation, _computation,
// compressed, compressed,
// VERSION + Version::ZERO, VERSION + Version::ZERO,
// fetched, fetched,
// )?, )?,
// _6m_to_1y: cohort::Vecs::forced_import( _6m_to_1y: cohort::Vecs::forced_import(
// path, path,
// Some("from_6m_to_1y"), Some("from_6m_to_1y"),
// _computation, _computation,
// compressed, compressed,
// VERSION + Version::ZERO, VERSION + Version::ZERO,
// fetched, fetched,
// )?, )?,
// _1y_to_2y: cohort::Vecs::forced_import( _1y_to_2y: cohort::Vecs::forced_import(
// path, path,
// Some("from_1y_to_2y"), Some("from_1y_to_2y"),
// _computation, _computation,
// compressed, compressed,
// VERSION + Version::ZERO, VERSION + Version::ZERO,
// fetched, fetched,
// )?, )?,
// _2y_to_3y: cohort::Vecs::forced_import( _2y_to_3y: cohort::Vecs::forced_import(
// path, path,
// Some("from_2y_to_3y"), Some("from_2y_to_3y"),
// _computation, _computation,
// compressed, compressed,
// VERSION + Version::ZERO, VERSION + Version::ZERO,
// fetched, fetched,
// )?, )?,
// _3y_to_4y: cohort::Vecs::forced_import( _3y_to_4y: cohort::Vecs::forced_import(
// path, path,
// Some("from_3y_to_4y"), Some("from_3y_to_4y"),
// _computation, _computation,
// compressed, compressed,
// VERSION + Version::ZERO, VERSION + Version::ZERO,
// fetched, fetched,
// )?, )?,
// _4y_to_5y: cohort::Vecs::forced_import( _4y_to_5y: cohort::Vecs::forced_import(
// path, path,
// Some("from_4y_to_5y"), Some("from_4y_to_5y"),
// _computation, _computation,
// compressed, compressed,
// VERSION + Version::ZERO, VERSION + Version::ZERO,
// fetched, fetched,
// )?, )?,
// _5y_to_7y: cohort::Vecs::forced_import( _5y_to_7y: cohort::Vecs::forced_import(
// path, path,
// Some("from_5y_to_7y"), Some("from_5y_to_7y"),
// _computation, _computation,
// compressed, compressed,
// VERSION + Version::ZERO, VERSION + Version::ZERO,
// fetched, fetched,
// )?, )?,
// _7y_to_10y: cohort::Vecs::forced_import( _7y_to_10y: cohort::Vecs::forced_import(
// path, path,
// Some("from_7y_to_10y"), Some("from_7y_to_10y"),
// _computation, _computation,
// compressed, compressed,
// VERSION + Version::ZERO, VERSION + Version::ZERO,
// fetched, fetched,
// )?, )?,
// _10y_to_15y: cohort::Vecs::forced_import( _10y_to_15y: cohort::Vecs::forced_import(
// path, path,
// Some("from_10y_to_15y"), Some("from_10y_to_15y"),
// _computation, _computation,
// compressed, compressed,
// VERSION + Version::ZERO, VERSION + Version::ZERO,
// fetched, fetched,
// )?, )?,
// }, },
by_epoch: OutputsByEpoch { by_epoch: OutputsByEpoch {
_0: cohort::Vecs::forced_import( _0: cohort::Vecs::forced_import(
path, path,
@@ -532,6 +534,14 @@ impl Vecs {
)?, )?,
}, },
by_size: OutputsBySize { by_size: OutputsBySize {
_0sat: cohort::Vecs::forced_import(
path,
Some("0sat"),
_computation,
compressed,
VERSION + Version::ZERO,
fetched,
)?,
from_1sat_to_10sats: cohort::Vecs::forced_import( from_1sat_to_10sats: cohort::Vecs::forced_import(
path, path,
Some("from_1sat_to_10sats"), Some("from_1sat_to_10sats"),
@@ -738,7 +748,7 @@ impl Vecs {
// fetched, // fetched,
// )?, // )?,
// }, // },
by_spendable_type: OutputsBySpendableType { by_type: OutputsBySpendableType {
p2pk65: cohort::Vecs::forced_import( p2pk65: cohort::Vecs::forced_import(
path, path,
Some("p2pk65"), Some("p2pk65"),
@@ -819,14 +829,14 @@ impl Vecs {
VERSION + Version::ZERO, VERSION + Version::ZERO,
fetched, fetched,
)?, )?,
// empty: cohort::Vecs::forced_import( empty: cohort::Vecs::forced_import(
// path, path,
// Some("empty"), Some("empty"),
// _computation, _computation,
// compressed, compressed,
// VERSION + Version::ZERO, VERSION + Version::ZERO,
// fetched, fetched,
// )?, )?,
unknown: cohort::Vecs::forced_import( unknown: cohort::Vecs::forced_import(
path, path,
Some("unknown"), Some("unknown"),
@@ -873,18 +883,19 @@ impl Vecs {
.as_ref() .as_ref()
.map(|fetched| &fetched.chainindexes_to_close.height); .map(|fetched| &fetched.chainindexes_to_close.height);
let inputindex_to_outputindex_mmap = inputindex_to_outputindex.mmap().load();
let outputindex_to_value_mmap = outputindex_to_value.mmap().load();
let outputindex_to_outputtype_mmap = outputindex_to_outputtype.mmap().load();
let outputindex_to_txindex_mmap = outputindex_to_txindex.mmap().load();
let txindex_to_height_mmap = txindex_to_height.mmap().load();
let mut height_to_first_outputindex_iter = height_to_first_outputindex.into_iter(); let mut height_to_first_outputindex_iter = height_to_first_outputindex.into_iter();
let mut height_to_first_inputindex_iter = height_to_first_inputindex.into_iter(); let mut height_to_first_inputindex_iter = height_to_first_inputindex.into_iter();
let mut height_to_output_count_iter = height_to_output_count.into_iter(); let mut height_to_output_count_iter = height_to_output_count.into_iter();
let mut height_to_input_count_iter = height_to_input_count.into_iter(); let mut height_to_input_count_iter = height_to_input_count.into_iter();
let mut inputindex_to_outputindex_iter = inputindex_to_outputindex.into_iter(); // let mut outputindex_to_value_iter_2 = outputindex_to_value.into_iter();
let mut outputindex_to_value_iter = outputindex_to_value.into_iter();
let mut outputindex_to_value_iter_2 = outputindex_to_value.into_iter();
let mut txindex_to_height_iter = txindex_to_height.into_iter();
let mut outputindex_to_txindex_iter = outputindex_to_txindex.into_iter();
let mut height_to_close_iter = height_to_close.as_ref().map(|v| v.into_iter()); let mut height_to_close_iter = height_to_close.as_ref().map(|v| v.into_iter());
let mut outputindex_to_outputtype_iter = outputindex_to_outputtype.into_iter(); // let mut outputindex_to_outputtype_iter_2 = outputindex_to_outputtype.into_iter();
let mut outputindex_to_outputtype_iter_2 = outputindex_to_outputtype.into_iter();
let mut height_to_unclaimed_rewards_iter = height_to_unclaimed_rewards.into_iter(); let mut height_to_unclaimed_rewards_iter = height_to_unclaimed_rewards.into_iter();
let mut height_to_timestamp_fixed_iter = height_to_timestamp_fixed.into_iter(); let mut height_to_timestamp_fixed_iter = height_to_timestamp_fixed.into_iter();
@@ -1012,104 +1023,134 @@ impl Vecs {
} }
let sent_handle = s.spawn(|| { let sent_handle = s.spawn(|| {
let mut txindex_to_height = BTreeMap::new();
let mut height_to_sent: BTreeMap<
Height,
OutputsByType<(SupplyState, Vec<Sats>)>,
> = BTreeMap::new();
// Skip coinbase // Skip coinbase
(first_inputindex + 1..first_inputindex + *input_count) (first_inputindex + 1..first_inputindex + *input_count)
.into_par_iter()
.map(InputIndex::from) .map(InputIndex::from)
.for_each(|inputindex| { .map(|inputindex| {
let outputindex = let outputindex = inputindex_to_outputindex
inputindex_to_outputindex_iter.unwrap_get_inner(inputindex); .get_or_read(inputindex, &inputindex_to_outputindex_mmap)
.unwrap()
.unwrap()
.into_inner();
let value = outputindex_to_value_iter.unwrap_get_inner(outputindex); let value = outputindex_to_value
.get_or_read(outputindex, &outputindex_to_value_mmap)
.unwrap()
.unwrap()
.into_inner();
let input_type = let input_type = outputindex_to_outputtype
outputindex_to_outputtype_iter.unwrap_get_inner(outputindex); .get_or_read(outputindex, &outputindex_to_outputtype_mmap)
.unwrap()
.unwrap()
.into_inner();
let input_txindex = if input_type.is_unspendable() {
outputindex_to_txindex_iter.unwrap_get_inner(outputindex); unreachable!()
}
let input_height = let input_txindex = outputindex_to_txindex
*txindex_to_height.entry(input_txindex).or_insert_with(|| { .get_or_read(outputindex, &outputindex_to_txindex_mmap)
txindex_to_height_iter.unwrap_get_inner(input_txindex) .unwrap()
}); .unwrap()
.into_inner();
let input_data = height_to_sent.entry(input_height).or_default(); // let input_height = *cached_txindex_to_height
// .entry(input_txindex)
// .or_insert_with(|| {
let height = txindex_to_height
.get_or_read(input_txindex, &txindex_to_height_mmap)
.unwrap()
.unwrap()
.into_inner();
// });
let (sent_supply, sats_vec) = input_data.get_mut(input_type); (height, value, input_type)
})
sent_supply.utxos += 1; .fold(
sent_supply.value += value; BTreeMap::<Height, Transacted>::default,
sats_vec.push(value); |mut tree, (height, value, input_type)| {
}); tree.entry(height).or_default().iterate(value, input_type);
tree
height_to_sent },
)
.reduce(BTreeMap::<Height, Transacted>::default, |first, second| {
let (mut source, to_consume) = if first.len() > second.len() {
(first, second)
} else {
(second, first)
};
to_consume.into_iter().for_each(|(k, v)| {
*source.entry(k).or_default() += v;
});
source
})
}); });
let received_handle = s.spawn(|| { let received_handle = s.spawn(|| {
let mut by_type: OutputsByType<(SupplyState, Vec<Sats>)> =
OutputsByType::default();
(first_outputindex..first_outputindex + *output_count) (first_outputindex..first_outputindex + *output_count)
.into_par_iter()
.map(OutputIndex::from) .map(OutputIndex::from)
.for_each(|outputindex| { .map(|outputindex| {
let value = let value = outputindex_to_value
outputindex_to_value_iter_2.unwrap_get_inner(outputindex); .get_or_read(outputindex, &outputindex_to_value_mmap)
.unwrap()
.unwrap()
.into_inner();
let output_type = let output_type = outputindex_to_outputtype
outputindex_to_outputtype_iter_2.unwrap_get_inner(outputindex); .get_or_read(outputindex, &outputindex_to_outputtype_mmap)
.unwrap()
.unwrap()
.into_inner();
let (received_supply, sats_vec) = by_type.get_mut(output_type); (value, output_type)
})
received_supply.value += value; .fold(
received_supply.utxos += 1; Transacted::default,
sats_vec.push(value); |mut transacted, (value, output_type)| {
}); transacted.iterate(value, output_type);
transacted
by_type },
)
.reduce(Transacted::default, |acc, transacted| acc + transacted)
}); });
(sent_handle.join().unwrap(), received_handle.join().unwrap()) (sent_handle.join().unwrap(), received_handle.join().unwrap())
}); });
unspendable_supply += received unspendable_supply += received
.by_type
.unspendable .unspendable
.as_vec() .as_vec()
.into_iter() .into_iter()
.map(|state| state.0.value) .map(|state| state.value)
.sum::<Sats>() .sum::<Sats>()
+ height_to_unclaimed_rewards_iter.unwrap_get_inner(height); + height_to_unclaimed_rewards_iter.unwrap_get_inner(height);
if height == Height::new(0) { if height == Height::new(0) {
received.spendable.p2pk65.1.remove(0); received = Transacted::default();
received.spendable.p2pk65.0.utxos -= 1;
received.spendable.p2pk65.0.value -= Sats::FIFTY_BTC;
unspendable_supply += Sats::FIFTY_BTC; unspendable_supply += Sats::FIFTY_BTC;
} else if height == Height::new(91_842) || height == Height::new(91_880) { } else if height == Height::new(91_842) || height == Height::new(91_880) {
// Need to destroy invalid coinbases due to duplicate txids // Need to destroy invalid coinbases due to duplicate txids
let entry = if height == Height::new(91_842) { if height == Height::new(91_842) {
height_to_sent.entry(Height::new(91_812)).or_default() height_to_sent.entry(Height::new(91_812)).or_default()
} else { } else {
height_to_sent.entry(Height::new(91_722)).or_default() height_to_sent.entry(Height::new(91_722)).or_default()
}; }
entry.spendable.p2pk65.0.value += Sats::FIFTY_BTC; .iterate(Sats::FIFTY_BTC, OutputType::P2PK65);
entry.spendable.p2pk65.0.utxos += 1;
entry.spendable.p2pk65.1.push(Sats::FIFTY_BTC);
}; };
if chain_state_starting_height <= height { if chain_state_starting_height <= height {
// RECEIVE // RECEIVE
// Push current block state before processing sends and receives // Push current block state before processing sends and receives
chain_state.push(BlockState::from(ReceivedBlockStateData { chain_state.push(BlockState {
received: &received, supply: received.spendable_supply.clone(),
price, price,
timestamp, timestamp,
})); });
self.utxos_vecs.receive(received, height, price); self.utxos_vecs.receive(received, height, price);
@@ -1117,17 +1158,12 @@ impl Vecs {
// SEND // SEND
height_to_sent let unsafe_chain_state = UnsafeSlice::new(&mut chain_state);
.iter()
.for_each(|(height, sent_data_by_type)| {
let block_state =
chain_state.get_mut(height.unwrap_to_usize()).unwrap();
sent_data_by_type height_to_sent.par_iter().for_each(|(height, sent)| unsafe {
.as_vec() (*unsafe_chain_state.get(height.unwrap_to_usize())).supply -=
.into_iter() &sent.spendable_supply;
.for_each(|(supply, _)| block_state.supply -= supply); });
});
self.utxos_vecs.send(height_to_sent, chain_state.as_slice()); self.utxos_vecs.send(height_to_sent, chain_state.as_slice());
} else { } else {
@@ -1151,6 +1187,8 @@ impl Vecs {
exit.block(); exit.block();
info!("Computing utxo set datasets...");
let mut flat_vecs_ = self.utxos_vecs.as_mut_vec(); let mut flat_vecs_ = self.utxos_vecs.as_mut_vec();
// Flush rest of values // Flush rest of values

View File

@@ -29,6 +29,7 @@ pub struct Dollars(f64);
impl Dollars { impl Dollars {
pub const ZERO: Self = Self(0.0); pub const ZERO: Self = Self(0.0);
pub const NAN: Self = Self(f64::NAN);
pub const fn mint(dollars: f64) -> Self { pub const fn mint(dollars: f64) -> Self {
Self(dollars) Self(dollars)

View File

@@ -45,8 +45,8 @@ impl OutputType {
Self::P2WSH => true, Self::P2WSH => true,
Self::P2TR => true, Self::P2TR => true,
Self::P2A => true, Self::P2A => true,
Self::Empty => false, Self::Empty => true,
Self::Unknown => false, Self::Unknown => true,
} }
} }

View File

@@ -42,6 +42,10 @@ impl Sats {
pub fn is_zero(&self) -> bool { pub fn is_zero(&self) -> bool {
*self == Self::ZERO *self == Self::ZERO
} }
pub fn is_not_zero(&self) -> bool {
*self != Self::ZERO
}
} }
impl Add for Sats { impl Add for Sats {
@@ -140,6 +144,12 @@ impl From<Sats> for f64 {
} }
} }
impl From<Sats> for usize {
fn from(value: Sats) -> Self {
value.0 as usize
}
}
impl From<Amount> for Sats { impl From<Amount> for Sats {
fn from(value: Amount) -> Self { fn from(value: Amount) -> Self {
Self(value.to_sat()) Self(value.to_sat())

View File

@@ -35,7 +35,7 @@ impl Fetcher {
pub fn get_date(&mut self, date: Date) -> color_eyre::Result<OHLCCents> { pub fn get_date(&mut self, date: Date) -> color_eyre::Result<OHLCCents> {
self.kraken self.kraken
.get_from_1d(&date) .get_from_1d(&date)
.or_else(|e| { .or_else(|_| {
// eprintln!("{e}"); // eprintln!("{e}");
self.binance.get_from_1d(&date) self.binance.get_from_1d(&date)
}) })

View File

@@ -44,7 +44,7 @@ impl<'a> VecTrees<'a> {
.id_to_index_to_vec .id_to_index_to_vec
.entry(key.clone()) .entry(key.clone())
.or_default() .or_default()
.insert(index.clone(), vec); .insert(index, vec);
if prev.is_some() { if prev.is_some() {
dbg!(&key, str, name); dbg!(&key, str, name);
panic!() panic!()

View File

@@ -19,6 +19,11 @@ impl<'a, T> UnsafeSlice<'a, T> {
} }
} }
/// SAFETY: It is UB
pub fn get(&self, i: usize) -> *mut T {
self.0[i].get()
}
pub fn copy_slice(&self, start: usize, slice: &[T]) pub fn copy_slice(&self, start: usize, slice: &[T])
where where
T: Copy, T: Copy,

View File

@@ -8,11 +8,13 @@ use std::{
time::Duration, time::Duration,
}; };
use arc_swap::ArcSwap;
use brk_core::{ use brk_core::{
Bitcoin, CheckedSub, Close, Date, DateIndex, Dollars, Height, Sats, StoredUsize, TxIndex, Bitcoin, CheckedSub, Close, Date, DateIndex, Dollars, Height, Sats, StoredUsize, TxIndex,
}; };
use brk_exit::Exit; use brk_exit::Exit;
use log::info; use log::info;
use memmap2::Mmap;
use crate::{ use crate::{
AnyCollectableVec, AnyIterableVec, AnyVec, BoxedVecIterator, CollectableVec, Compressed, Error, AnyCollectableVec, AnyIterableVec, AnyVec, BoxedVecIterator, CollectableVec, Compressed, Error,
@@ -97,6 +99,14 @@ where
self.inner.path() self.inner.path()
} }
pub fn get_or_read(&self, index: I, mmap: &Mmap) -> Result<Option<Value<T>>> {
self.inner.get_or_read(index, mmap)
}
pub fn mmap(&self) -> &ArcSwap<Mmap> {
self.inner.mmap()
}
pub fn inner_version(&self) -> Version { pub fn inner_version(&self) -> Version {
self.inner.version() self.inner.version()
} }

View File

@@ -352,18 +352,19 @@ function createPartialOptions(colors) {
* *
* @param {Object} args * @param {Object} args
* @param {string} args.name * @param {string} args.name
* @param {string} args.legend
* @param {string} args.title * @param {string} args.title
* @param {VecIdRatioCapBase} args.key * @param {VecIdRatioCapBase} args.key
* @param {Color} [args.color] * @param {Color} [args.color]
*/ */
function createPriceWithRatio({ name, title, key, color }) { function createPriceWithRatio({ name, title, legend, key, color }) {
return { return {
name, name,
title, title,
top: [ top: [
createBaseSeries({ createBaseSeries({
key, key,
name: "Average", name: legend,
color, color,
}), }),
createBaseSeries({ createBaseSeries({
@@ -633,6 +634,7 @@ function createPartialOptions(colors) {
createPriceWithRatio({ createPriceWithRatio({
key: `${key}realized-price`, key: `${key}realized-price`,
name: "realized price", name: "realized price",
legend: "realized",
title: `${title} Realized Price`, title: `${title} Realized Price`,
}), }),
], ],
@@ -747,6 +749,7 @@ function createPartialOptions(colors) {
key: `${key}-sma`, key: `${key}-sma`,
name, name,
title: `${name} Market Price Moving Average`, title: `${name} Market Price Moving Average`,
legend: "average",
color: colors[`_${key}`], color: colors[`_${key}`],
}), }),
), ),
@@ -785,6 +788,11 @@ function createPartialOptions(colors) {
], ],
})), })),
}, },
],
},
{
name: "Investing",
tree: [
{ {
name: "DCA vs Lump sum", name: "DCA vs Lump sum",
tree: [ tree: [
@@ -1581,6 +1589,71 @@ function createPartialOptions(colors) {
}), }),
], ],
}, },
{
name: "Range",
tree: [
createUTXOGroupFolder({
key: "from-1d-to-1w",
name: "1d..1w",
title: "Between 1 Day and 1 Week",
}),
createUTXOGroupFolder({
key: "from-1w-to-1m",
name: "1w..1m",
title: "Between 1 Week and 1 Month",
}),
createUTXOGroupFolder({
key: "from-1m-to-3m",
name: "1m..3m",
title: "Between 1 Month and 3 Months",
}),
createUTXOGroupFolder({
key: "from-3m-to-6m",
name: "3m..6m",
title: "Between 3 Month and 6 Months",
}),
createUTXOGroupFolder({
key: "from-6m-to-1y",
name: "6m..1y",
title: "Between 6 Months and 1 Year",
}),
createUTXOGroupFolder({
key: "from-1y-to-2y",
name: "1y..2y",
title: "Between 1 Year and 2 Years",
}),
createUTXOGroupFolder({
key: "from-2y-to-3y",
name: "2y..3y",
title: "Between 2 Years and 3 Years",
}),
createUTXOGroupFolder({
key: "from-3y-to-4y",
name: "3y..4y",
title: "Between 3 Years and 4 Years",
}),
createUTXOGroupFolder({
key: "from-4y-to-5y",
name: "4y..5y",
title: "Between 4 Years and 5 Years",
}),
createUTXOGroupFolder({
key: "from-5y-to-7y",
name: "5y..7y",
title: "Between 5 Years and 7 Years",
}),
createUTXOGroupFolder({
key: "from-7y-to-10y",
name: "7y..10y",
title: "Between 7 Years and 10 Years",
}),
createUTXOGroupFolder({
key: "from-10y-to-15y",
name: "10y..15y",
title: "Between 10 Years and 15 Years",
}),
],
},
{ {
name: "Epoch", name: "Epoch",
tree: [ tree: [
@@ -1615,74 +1688,79 @@ function createPartialOptions(colors) {
name: "size", name: "size",
tree: [ tree: [
createUTXOGroupFolder({ createUTXOGroupFolder({
key: "from-1sat-10sats", key: "0sat",
name: "0sat",
title: "0 sat",
}),
createUTXOGroupFolder({
key: "from-1sat-to-10sats",
name: "1sat..10sats", name: "1sat..10sats",
title: "UTXOs from 1 sat to 10 sats", title: "From 1 sat to 10 sats",
}), }),
createUTXOGroupFolder({ createUTXOGroupFolder({
key: "from-10sats-100sats", key: "from-10sats-to-100sats",
name: "10sat..100sats", name: "10sat..100sats",
title: "UTXOs from 10 sats to 100 sats", title: "From 10 sats to 100 sats",
}), }),
createUTXOGroupFolder({ createUTXOGroupFolder({
key: "from-100sats-1-000sats", key: "from-100sats-to-1-000sats",
name: "100sat..1_000sats", name: "100sat..1_000sats",
title: "UTXOs from 100 sats to 1,000 sats", title: "From 100 sats to 1,000 sats",
}), }),
createUTXOGroupFolder({ createUTXOGroupFolder({
key: "from-1-000sats-10-000sats", key: "from-1-000sats-to-10-000sats",
name: "1_000sat..10_000sats", name: "1_000sat..10_000sats",
title: "UTXOs from 1,000 sats to 10,000 sats", title: "From 1,000 sats to 10,000 sats",
}), }),
createUTXOGroupFolder({ createUTXOGroupFolder({
key: "from-10-000sats-100-000sats", key: "from-10-000sats-to-100-000sats",
name: "10_000sat..100_000sats", name: "10_000sat..100_000sats",
title: "UTXOs from 10,000 sats to 100,000 sats", title: "From 10,000 sats to 100,000 sats",
}), }),
createUTXOGroupFolder({ createUTXOGroupFolder({
key: "from-100-000sats-1-000-000sats", key: "from-100-000sats-to-1-000-000sats",
name: "100_000sat..1_000_000sats", name: "100_000sat..1_000_000sats",
title: "UTXOs from 100,000 sats to 1,000,000 sats", title: "From 100,000 sats to 1,000,000 sats",
}), }),
createUTXOGroupFolder({ createUTXOGroupFolder({
key: "from-1-000-000sats-10-000-000sats", key: "from-1-000-000sats-to-10-000-000sats",
name: "1_000_000sat..10_000_000sats", name: "1_000_000sat..10_000_000sats",
title: "UTXOs from 1,000,000 sats to 10,000,000 sats", title: "From 1,000,000 sats to 10,000,000 sats",
}), }),
createUTXOGroupFolder({ createUTXOGroupFolder({
key: "from-10-000-000sats-1btc", key: "from-10-000-000sats-to-1btc",
name: "10_000_000sat..1btc", name: "10_000_000sat..1btc",
title: "UTXOs from 10,000,000 sats to 1 BTC", title: "From 10,000,000 sats to 1 BTC",
}), }),
createUTXOGroupFolder({ createUTXOGroupFolder({
key: "from-1btc-10btc", key: "from-1btc-to-10btc",
name: "1btc..10btc", name: "1btc..10btc",
title: "UTXOs from 1 BTC to 10 BTC", title: "From 1 BTC to 10 BTC",
}), }),
createUTXOGroupFolder({ createUTXOGroupFolder({
key: "from-10btc-100btc", key: "from-10btc-to-100btc",
name: "10btc..100btc", name: "10btc..100btc",
title: "UTXOs from 10 BTC to 100 BTC", title: "From 10 BTC to 100 BTC",
}), }),
createUTXOGroupFolder({ createUTXOGroupFolder({
key: "from-100btc-1-000btc", key: "from-100btc-to-1-000btc",
name: "100btc..1_000btc", name: "100btc..1_000btc",
title: "UTXOs from 100 BTC to 1,000 BTC", title: "From 100 BTC to 1,000 BTC",
}), }),
createUTXOGroupFolder({ createUTXOGroupFolder({
key: "from-1-000btc-10-000btc", key: "from-1-000btc-to-10-000btc",
name: "1_000btc..10_000btc", name: "1_000btc..10_000btc",
title: "UTXOs from 1,000 BTC to 10,000 BTC", title: "From 1,000 BTC to 10,000 BTC",
}), }),
createUTXOGroupFolder({ createUTXOGroupFolder({
key: "from-10-000btc-100-000btc", key: "from-10-000btc-to-100-000btc",
name: "10_000btc..100_000btc", name: "10_000btc..100_000btc",
title: "UTXOs from 10,000 BTC to 100,000 BTC", title: "From 10,000 BTC to 100,000 BTC",
}), }),
createUTXOGroupFolder({ createUTXOGroupFolder({
key: "from-100-000btc", key: "from-100-000btc",
name: "100_000btc..inf", name: "100_000btc+",
title: "UTXOs from 100,000 BTC", title: "From 100,000 BTC",
}), }),
], ],
}, },
@@ -1739,6 +1817,11 @@ function createPartialOptions(colors) {
name: "unknown", name: "unknown",
title: "Pay To Unknown", title: "Pay To Unknown",
}), }),
createUTXOGroupFolder({
key: "empty",
name: "empty",
title: "Pay To Empty",
}),
], ],
}, },
{ {

File diff suppressed because it is too large Load Diff