mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
global: final snapshot and fixes before release
This commit is contained in:
12
Cargo.lock
generated
12
Cargo.lock
generated
@@ -2538,9 +2538,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rawdb"
|
name = "rawdb"
|
||||||
version = "0.7.0"
|
version = "0.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f1a1534553d4e626de325e000d3250490fb4020d8f177747615f345016c8f0a"
|
checksum = "dc7e70161aa9dbfcc1f858cae94eda70c9073bab5b22167bc6ab0f85d27054cf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
@@ -3432,9 +3432,9 @@ checksum = "8f54a172d0620933a27a4360d3db3e2ae0dd6cceae9730751a036bbf182c4b23"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vecdb"
|
name = "vecdb"
|
||||||
version = "0.7.0"
|
version = "0.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a45f0491e73f467ff4dcb360d4341ad6281719362f29b040c2b769d18e161ab1"
|
checksum = "417cdef9fd0ada1659e1499c7180b3b8edf5256b99eb846c7f960c10a755ea3c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -3455,9 +3455,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vecdb_derive"
|
name = "vecdb_derive"
|
||||||
version = "0.7.0"
|
version = "0.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aac60cdf47669f66acd6debfd66705464fb7c2519eed7b3692495d037f6d6399"
|
checksum = "aba470bc1a709df1efaace5885b25e7685988c64b61ac379758d861d12312735"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ tower-http = { version = "0.6.8", features = ["catch-panic", "compression-br", "
|
|||||||
tower-layer = "0.3"
|
tower-layer = "0.3"
|
||||||
tracing = { version = "0.1", default-features = false, features = ["std"] }
|
tracing = { version = "0.1", default-features = false, features = ["std"] }
|
||||||
ureq = { version = "3.3.0", features = ["json"] }
|
ureq = { version = "3.3.0", features = ["json"] }
|
||||||
vecdb = { version = "0.7.0", features = ["derive", "serde_json", "pco", "schemars"] }
|
vecdb = { version = "0.7.1", features = ["derive", "serde_json", "pco", "schemars"] }
|
||||||
# vecdb = { path = "../anydb/crates/vecdb", features = ["derive", "serde_json", "pco", "schemars"] }
|
# vecdb = { path = "../anydb/crates/vecdb", features = ["derive", "serde_json", "pco", "schemars"] }
|
||||||
|
|
||||||
[workspace.metadata.release]
|
[workspace.metadata.release]
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ impl Vecs {
|
|||||||
indexer: &Indexer,
|
indexer: &Indexer,
|
||||||
indexes: &indexes::Vecs,
|
indexes: &indexes::Vecs,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let db = open_db(parent_path, super::DB_NAME, 50_000_000)?;
|
let db = open_db(parent_path, super::DB_NAME, 1_000_000)?;
|
||||||
let version = parent_version;
|
let version = parent_version;
|
||||||
|
|
||||||
let lookback = LookbackVecs::forced_import(&db, version)?;
|
let lookback = LookbackVecs::forced_import(&db, version)?;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ impl Vecs {
|
|||||||
indexes: &indexes::Vecs,
|
indexes: &indexes::Vecs,
|
||||||
cached_starts: &CachedWindowStarts,
|
cached_starts: &CachedWindowStarts,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let db = open_db(parent_path, DB_NAME, 1_000_000)?;
|
let db = open_db(parent_path, DB_NAME, 250_000)?;
|
||||||
let version = parent_version;
|
let version = parent_version;
|
||||||
let v1 = version + Version::ONE;
|
let v1 = version + Version::ONE;
|
||||||
let activity = ActivityVecs::forced_import(&db, version, indexes, cached_starts)?;
|
let activity = ActivityVecs::forced_import(&db, version, indexes, cached_starts)?;
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ use brk_types::{Cents, CentsCompact, Sats};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
distribution::state::PendingDelta,
|
distribution::state::PendingDelta,
|
||||||
internal::{PERCENTILES, PERCENTILES_LEN, algo::{FenwickNode, FenwickTree}},
|
internal::{
|
||||||
|
PERCENTILES, PERCENTILES_LEN,
|
||||||
|
algo::{FenwickNode, FenwickTree},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::COST_BASIS_PRICE_DIGITS;
|
use super::COST_BASIS_PRICE_DIGITS;
|
||||||
@@ -70,7 +73,6 @@ pub(super) struct CostBasisFenwick {
|
|||||||
// to a flat bucket index across two tiers.
|
// to a flat bucket index across two tiers.
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
/// Map rounded dollars to a flat bucket index.
|
|
||||||
/// Prices >= $1M are clamped to the last bucket.
|
/// Prices >= $1M are clamped to the last bucket.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn dollars_to_bucket(dollars: u64) -> usize {
|
fn dollars_to_bucket(dollars: u64) -> usize {
|
||||||
@@ -83,7 +85,6 @@ fn dollars_to_bucket(dollars: u64) -> usize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a bucket index back to a price in Cents.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bucket_to_cents(bucket: usize) -> Cents {
|
fn bucket_to_cents(bucket: usize) -> Cents {
|
||||||
let dollars: u64 = if bucket < TIER1_START {
|
let dollars: u64 = if bucket < TIER1_START {
|
||||||
@@ -96,24 +97,18 @@ fn bucket_to_cents(bucket: usize) -> Cents {
|
|||||||
Cents::from(dollars * 100)
|
Cents::from(dollars * 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Map a CentsCompact price to a bucket index.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn price_to_bucket(price: CentsCompact) -> usize {
|
fn price_to_bucket(price: CentsCompact) -> usize {
|
||||||
cents_to_bucket(price.into())
|
cents_to_bucket(price.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Map a Cents price to a bucket index.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn cents_to_bucket(price: Cents) -> usize {
|
fn cents_to_bucket(price: Cents) -> usize {
|
||||||
dollars_to_bucket(u64::from(price.round_to_dollar(COST_BASIS_PRICE_DIGITS)) / 100)
|
dollars_to_bucket(u64::from(price.round_to_dollar(COST_BASIS_PRICE_DIGITS)) / 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
impl Default for CostBasisFenwick {
|
||||||
// CostBasisFenwick implementation
|
fn default() -> Self {
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
impl CostBasisFenwick {
|
|
||||||
pub(super) fn new() -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
tree: FenwickTree::new(TREE_SIZE),
|
tree: FenwickTree::new(TREE_SIZE),
|
||||||
totals: CostBasisNode::default(),
|
totals: CostBasisNode::default(),
|
||||||
@@ -121,7 +116,9 @@ impl CostBasisFenwick {
|
|||||||
initialized: false,
|
initialized: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CostBasisFenwick {
|
||||||
pub(super) fn is_initialized(&self) -> bool {
|
pub(super) fn is_initialized(&self) -> bool {
|
||||||
self.initialized
|
self.initialized
|
||||||
}
|
}
|
||||||
@@ -153,7 +150,8 @@ impl CostBasisFenwick {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let bucket = price_to_bucket(price);
|
let bucket = price_to_bucket(price);
|
||||||
let delta = CostBasisNode::new(net_sats, price.as_u128() as i128 * net_sats as i128, is_sth);
|
let delta =
|
||||||
|
CostBasisNode::new(net_sats, price.as_u128() as i128 * net_sats as i128, is_sth);
|
||||||
self.tree.add(bucket, &delta);
|
self.tree.add(bucket, &delta);
|
||||||
self.totals.add_assign(&delta);
|
self.totals.add_assign(&delta);
|
||||||
}
|
}
|
||||||
@@ -236,8 +234,7 @@ impl CostBasisFenwick {
|
|||||||
sat_targets[PERCENTILES_LEN + 1] = total_sats - 1; // max
|
sat_targets[PERCENTILES_LEN + 1] = total_sats - 1; // max
|
||||||
|
|
||||||
let mut sat_buckets = [0usize; PERCENTILES_LEN + 2];
|
let mut sat_buckets = [0usize; PERCENTILES_LEN + 2];
|
||||||
self.tree
|
self.tree.kth(&sat_targets, &sat_field, &mut sat_buckets);
|
||||||
.kth(&sat_targets, &sat_field, &mut sat_buckets);
|
|
||||||
|
|
||||||
result.min_price = bucket_to_cents(sat_buckets[0]);
|
result.min_price = bucket_to_cents(sat_buckets[0]);
|
||||||
(0..PERCENTILES_LEN).for_each(|i| {
|
(0..PERCENTILES_LEN).for_each(|i| {
|
||||||
@@ -253,8 +250,7 @@ impl CostBasisFenwick {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut usd_buckets = [0usize; PERCENTILES_LEN];
|
let mut usd_buckets = [0usize; PERCENTILES_LEN];
|
||||||
self.tree
|
self.tree.kth(&usd_targets, &usd_field, &mut usd_buckets);
|
||||||
.kth(&usd_targets, &usd_field, &mut usd_buckets);
|
|
||||||
|
|
||||||
(0..PERCENTILES_LEN).for_each(|i| {
|
(0..PERCENTILES_LEN).for_each(|i| {
|
||||||
result.usd_prices[i] = bucket_to_cents(usd_buckets[i]);
|
result.usd_prices[i] = bucket_to_cents(usd_buckets[i]);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use brk_cohort::{
|
use brk_cohort::{
|
||||||
AgeRange, AmountRange, Class, ByEpoch, OverAmount, UnderAmount, UnderAge,
|
AgeRange, AmountRange, ByEpoch, Class, CohortContext, Filter, Filtered, OverAge, OverAmount,
|
||||||
OverAge, SpendableType, CohortContext, Filter, Filtered, Term,
|
SpendableType, Term, UnderAge, UnderAmount,
|
||||||
};
|
};
|
||||||
use brk_error::Result;
|
use brk_error::Result;
|
||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
@@ -17,8 +17,8 @@ use crate::{
|
|||||||
distribution::{
|
distribution::{
|
||||||
DynCohortVecs,
|
DynCohortVecs,
|
||||||
metrics::{
|
metrics::{
|
||||||
AllCohortMetrics, BasicCohortMetrics, CohortMetricsBase,
|
AllCohortMetrics, BasicCohortMetrics, CohortMetricsBase, CoreCohortMetrics,
|
||||||
CoreCohortMetrics, ExtendedAdjustedCohortMetrics, ExtendedCohortMetrics, ImportConfig,
|
ExtendedAdjustedCohortMetrics, ExtendedCohortMetrics, ImportConfig,
|
||||||
MinimalCohortMetrics, ProfitabilityMetrics, RealizedFullAccum, SupplyCore,
|
MinimalCohortMetrics, ProfitabilityMetrics, RealizedFullAccum, SupplyCore,
|
||||||
TypeCohortMetrics,
|
TypeCohortMetrics,
|
||||||
},
|
},
|
||||||
@@ -52,11 +52,16 @@ pub struct UTXOCohorts<M: StorageMode = Rw> {
|
|||||||
pub profitability: ProfitabilityMetrics<M>,
|
pub profitability: ProfitabilityMetrics<M>,
|
||||||
pub matured: AgeRange<AmountPerBlockCumulativeRolling<M>>,
|
pub matured: AgeRange<AmountPerBlockCumulativeRolling<M>>,
|
||||||
#[traversable(skip)]
|
#[traversable(skip)]
|
||||||
|
pub(super) caches: UTXOCohortsTransientState,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// In-memory state that does NOT survive rollback.
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
pub(crate) struct UTXOCohortsTransientState {
|
||||||
pub(super) fenwick: CostBasisFenwick,
|
pub(super) fenwick: CostBasisFenwick,
|
||||||
/// Cached partition_point positions for tick_tock boundary searches.
|
/// Cached partition_point positions for tick_tock boundary searches.
|
||||||
/// Avoids O(log n) binary search per boundary per block; scans forward
|
/// Avoids O(log n) binary search per boundary per block; scans forward
|
||||||
/// from last known position (typically O(1) per boundary).
|
/// from last known position (typically O(1) per boundary).
|
||||||
#[traversable(skip)]
|
|
||||||
pub(super) tick_tock_cached_positions: [usize; 20],
|
pub(super) tick_tock_cached_positions: [usize; 20],
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,24 +289,30 @@ impl UTXOCohorts<Rw> {
|
|||||||
over_amount,
|
over_amount,
|
||||||
profitability,
|
profitability,
|
||||||
matured,
|
matured,
|
||||||
fenwick: CostBasisFenwick::new(),
|
caches: UTXOCohortsTransientState::default(),
|
||||||
tick_tock_cached_positions: [0; 20],
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reset in-memory caches that become stale after rollback.
|
||||||
|
pub(crate) fn reset_caches(&mut self) {
|
||||||
|
self.caches = UTXOCohortsTransientState::default();
|
||||||
|
}
|
||||||
|
|
||||||
/// Initialize the Fenwick tree from all age-range BTreeMaps.
|
/// Initialize the Fenwick tree from all age-range BTreeMaps.
|
||||||
/// Call after state import when all pending maps have been drained.
|
/// Call after state import when all pending maps have been drained.
|
||||||
pub(crate) fn init_fenwick_if_needed(&mut self) {
|
pub(crate) fn init_fenwick_if_needed(&mut self) {
|
||||||
if self.fenwick.is_initialized() {
|
if self.caches.fenwick.is_initialized() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let Self {
|
let Self {
|
||||||
sth,
|
sth,
|
||||||
fenwick,
|
caches,
|
||||||
age_range,
|
age_range,
|
||||||
..
|
..
|
||||||
} = self;
|
} = self;
|
||||||
fenwick.compute_is_sth(&sth.metrics.filter, age_range.iter().map(|v| v.filter()));
|
caches
|
||||||
|
.fenwick
|
||||||
|
.compute_is_sth(&sth.metrics.filter, age_range.iter().map(|v| v.filter()));
|
||||||
|
|
||||||
let maps: Vec<_> = age_range
|
let maps: Vec<_> = age_range
|
||||||
.iter()
|
.iter()
|
||||||
@@ -312,27 +323,27 @@ impl UTXOCohorts<Rw> {
|
|||||||
if map.is_empty() {
|
if map.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
Some((map, fenwick.is_sth_at(i)))
|
Some((map, caches.fenwick.is_sth_at(i)))
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
fenwick.bulk_init(maps.into_iter());
|
caches.fenwick.bulk_init(maps.into_iter());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply pending deltas from all age-range cohorts to the Fenwick tree.
|
/// Apply pending deltas from all age-range cohorts to the Fenwick tree.
|
||||||
/// Call after receive/send, before push_cohort_states.
|
/// Call after receive/send, before push_cohort_states.
|
||||||
pub(crate) fn update_fenwick_from_pending(&mut self) {
|
pub(crate) fn update_fenwick_from_pending(&mut self) {
|
||||||
if !self.fenwick.is_initialized() {
|
if !self.caches.fenwick.is_initialized() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Destructure to get separate borrows on fenwick and age_range
|
// Destructure to get separate borrows on caches and age_range
|
||||||
let Self {
|
let Self {
|
||||||
fenwick, age_range, ..
|
caches, age_range, ..
|
||||||
} = self;
|
} = self;
|
||||||
for (i, sub) in age_range.iter().enumerate() {
|
for (i, sub) in age_range.iter().enumerate() {
|
||||||
if let Some(state) = sub.state.as_ref() {
|
if let Some(state) = sub.state.as_ref() {
|
||||||
let is_sth = fenwick.is_sth_at(i);
|
let is_sth = caches.fenwick.is_sth_at(i);
|
||||||
state.for_each_cost_basis_pending(|&price, delta| {
|
state.for_each_cost_basis_pending(|&price, delta| {
|
||||||
fenwick.apply_delta(price, delta, is_sth);
|
caches.fenwick.apply_delta(price, delta, is_sth);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -455,8 +466,7 @@ impl UTXOCohorts<Rw> {
|
|||||||
.try_for_each(|vecs| {
|
.try_for_each(|vecs| {
|
||||||
let sources =
|
let sources =
|
||||||
filter_minimal_sources_from(amr.iter(), Some(&vecs.metrics.filter));
|
filter_minimal_sources_from(amr.iter(), Some(&vecs.metrics.filter));
|
||||||
vecs.metrics
|
vecs.metrics.compute_from_sources(si, &sources, exit)
|
||||||
.compute_from_sources(si, &sources, exit)
|
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
@@ -483,8 +493,16 @@ impl UTXOCohorts<Rw> {
|
|||||||
all.push(&mut self.all);
|
all.push(&mut self.all);
|
||||||
all.push(&mut self.sth);
|
all.push(&mut self.sth);
|
||||||
all.push(&mut self.lth);
|
all.push(&mut self.lth);
|
||||||
all.extend(self.under_age.iter_mut().map(|x| x as &mut dyn DynCohortVecs));
|
all.extend(
|
||||||
all.extend(self.over_age.iter_mut().map(|x| x as &mut dyn DynCohortVecs));
|
self.under_age
|
||||||
|
.iter_mut()
|
||||||
|
.map(|x| x as &mut dyn DynCohortVecs),
|
||||||
|
);
|
||||||
|
all.extend(
|
||||||
|
self.over_age
|
||||||
|
.iter_mut()
|
||||||
|
.map(|x| x as &mut dyn DynCohortVecs),
|
||||||
|
);
|
||||||
all.extend(
|
all.extend(
|
||||||
self.over_amount
|
self.over_amount
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
@@ -542,7 +560,8 @@ impl UTXOCohorts<Rw> {
|
|||||||
.metrics
|
.metrics
|
||||||
.activity
|
.activity
|
||||||
.transfer_volume
|
.transfer_volume
|
||||||
.block.cents
|
.block
|
||||||
|
.cents
|
||||||
.read_only_clone();
|
.read_only_clone();
|
||||||
let under_1h_value_destroyed = self
|
let under_1h_value_destroyed = self
|
||||||
.age_range
|
.age_range
|
||||||
@@ -567,7 +586,13 @@ impl UTXOCohorts<Rw> {
|
|||||||
|
|
||||||
// Clone all_supply_sats and all_utxo_count for non-all cohorts.
|
// Clone all_supply_sats and all_utxo_count for non-all cohorts.
|
||||||
let all_supply_sats = self.all.metrics.supply.total.sats.height.read_only_clone();
|
let all_supply_sats = self.all.metrics.supply.total.sats.height.read_only_clone();
|
||||||
let all_utxo_count = self.all.metrics.outputs.unspent_count.height.read_only_clone();
|
let all_utxo_count = self
|
||||||
|
.all
|
||||||
|
.metrics
|
||||||
|
.outputs
|
||||||
|
.unspent_count
|
||||||
|
.height
|
||||||
|
.read_only_clone();
|
||||||
|
|
||||||
// Destructure to allow parallel mutable access to independent fields.
|
// Destructure to allow parallel mutable access to independent fields.
|
||||||
let Self {
|
let Self {
|
||||||
@@ -636,9 +661,10 @@ impl UTXOCohorts<Rw> {
|
|||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
Box::new(|| {
|
Box::new(|| {
|
||||||
over_amount
|
over_amount.par_iter_mut().try_for_each(|v| {
|
||||||
.par_iter_mut()
|
v.metrics
|
||||||
.try_for_each(|v| v.metrics.compute_rest_part2(prices, starting_indexes, au, exit))
|
.compute_rest_part2(prices, starting_indexes, au, exit)
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
Box::new(|| {
|
Box::new(|| {
|
||||||
epoch.par_iter_mut().try_for_each(|v| {
|
epoch.par_iter_mut().try_for_each(|v| {
|
||||||
@@ -653,19 +679,22 @@ impl UTXOCohorts<Rw> {
|
|||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
Box::new(|| {
|
Box::new(|| {
|
||||||
amount_range
|
amount_range.par_iter_mut().try_for_each(|v| {
|
||||||
.par_iter_mut()
|
v.metrics
|
||||||
.try_for_each(|v| v.metrics.compute_rest_part2(prices, starting_indexes, au, exit))
|
.compute_rest_part2(prices, starting_indexes, au, exit)
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
Box::new(|| {
|
Box::new(|| {
|
||||||
under_amount
|
under_amount.par_iter_mut().try_for_each(|v| {
|
||||||
.par_iter_mut()
|
v.metrics
|
||||||
.try_for_each(|v| v.metrics.compute_rest_part2(prices, starting_indexes, au, exit))
|
.compute_rest_part2(prices, starting_indexes, au, exit)
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
Box::new(|| {
|
Box::new(|| {
|
||||||
type_
|
type_.par_iter_mut().try_for_each(|v| {
|
||||||
.par_iter_mut()
|
v.metrics
|
||||||
.try_for_each(|v| v.metrics.compute_rest_part2(prices, starting_indexes, au, exit))
|
.compute_rest_part2(prices, starting_indexes, au, exit)
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -829,12 +858,30 @@ impl UTXOCohorts<Rw> {
|
|||||||
sth.metrics.realized.push_accum(&sth_acc);
|
sth.metrics.realized.push_accum(&sth_acc);
|
||||||
lth.metrics.realized.push_accum(<h_acc);
|
lth.metrics.realized.push_accum(<h_acc);
|
||||||
|
|
||||||
all.metrics.unrealized.investor_cap_in_profit_raw.push(CentsSquaredSats::new(all_icap.0));
|
all.metrics
|
||||||
all.metrics.unrealized.investor_cap_in_loss_raw.push(CentsSquaredSats::new(all_icap.1));
|
.unrealized
|
||||||
sth.metrics.unrealized.investor_cap_in_profit_raw.push(CentsSquaredSats::new(sth_icap.0));
|
.investor_cap_in_profit_raw
|
||||||
sth.metrics.unrealized.investor_cap_in_loss_raw.push(CentsSquaredSats::new(sth_icap.1));
|
.push(CentsSquaredSats::new(all_icap.0));
|
||||||
lth.metrics.unrealized.investor_cap_in_profit_raw.push(CentsSquaredSats::new(lth_icap.0));
|
all.metrics
|
||||||
lth.metrics.unrealized.investor_cap_in_loss_raw.push(CentsSquaredSats::new(lth_icap.1));
|
.unrealized
|
||||||
|
.investor_cap_in_loss_raw
|
||||||
|
.push(CentsSquaredSats::new(all_icap.1));
|
||||||
|
sth.metrics
|
||||||
|
.unrealized
|
||||||
|
.investor_cap_in_profit_raw
|
||||||
|
.push(CentsSquaredSats::new(sth_icap.0));
|
||||||
|
sth.metrics
|
||||||
|
.unrealized
|
||||||
|
.investor_cap_in_loss_raw
|
||||||
|
.push(CentsSquaredSats::new(sth_icap.1));
|
||||||
|
lth.metrics
|
||||||
|
.unrealized
|
||||||
|
.investor_cap_in_profit_raw
|
||||||
|
.push(CentsSquaredSats::new(lth_icap.0));
|
||||||
|
lth.metrics
|
||||||
|
.unrealized
|
||||||
|
.investor_cap_in_loss_raw
|
||||||
|
.push(CentsSquaredSats::new(lth_icap.1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ impl UTXOCohorts {
|
|||||||
date_opt: Option<Date>,
|
date_opt: Option<Date>,
|
||||||
states_path: &Path,
|
states_path: &Path,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if self.fenwick.is_initialized() {
|
if self.caches.fenwick.is_initialized() {
|
||||||
self.push_fenwick_results(spot_price);
|
self.push_fenwick_results(spot_price);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,18 +38,18 @@ impl UTXOCohorts {
|
|||||||
|
|
||||||
/// Push all Fenwick-derived per-block results: percentiles, density, profitability.
|
/// Push all Fenwick-derived per-block results: percentiles, density, profitability.
|
||||||
fn push_fenwick_results(&mut self, spot_price: Cents) {
|
fn push_fenwick_results(&mut self, spot_price: Cents) {
|
||||||
let (all_d, sth_d, lth_d) = self.fenwick.density(spot_price);
|
let (all_d, sth_d, lth_d) = self.caches.fenwick.density(spot_price);
|
||||||
|
|
||||||
let all = self.fenwick.percentiles_all();
|
let all = self.caches.fenwick.percentiles_all();
|
||||||
push_cost_basis(&all, all_d, &mut self.all.metrics.cost_basis);
|
push_cost_basis(&all, all_d, &mut self.all.metrics.cost_basis);
|
||||||
|
|
||||||
let sth = self.fenwick.percentiles_sth();
|
let sth = self.caches.fenwick.percentiles_sth();
|
||||||
push_cost_basis(&sth, sth_d, &mut self.sth.metrics.cost_basis);
|
push_cost_basis(&sth, sth_d, &mut self.sth.metrics.cost_basis);
|
||||||
|
|
||||||
let lth = self.fenwick.percentiles_lth();
|
let lth = self.caches.fenwick.percentiles_lth();
|
||||||
push_cost_basis(<h, lth_d, &mut self.lth.metrics.cost_basis);
|
push_cost_basis(<h, lth_d, &mut self.lth.metrics.cost_basis);
|
||||||
|
|
||||||
let prof = self.fenwick.profitability(spot_price);
|
let prof = self.caches.fenwick.profitability(spot_price);
|
||||||
push_profitability(&prof, &mut self.profitability);
|
push_profitability(&prof, &mut self.profitability);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ impl UTXOCohorts<Rw> {
|
|||||||
// Cohort 0 covers [0, 1) hours
|
// Cohort 0 covers [0, 1) hours
|
||||||
// Cohort 20 covers [15*365*24, infinity) hours
|
// Cohort 20 covers [15*365*24, infinity) hours
|
||||||
let mut age_cohorts: Vec<_> = self.age_range.iter_mut().map(|v| &mut v.state).collect();
|
let mut age_cohorts: Vec<_> = self.age_range.iter_mut().map(|v| &mut v.state).collect();
|
||||||
let cached = &mut self.tick_tock_cached_positions;
|
let cached = &mut self.caches.tick_tock_cached_positions;
|
||||||
|
|
||||||
// For each boundary (in hours), find blocks that just crossed it
|
// For each boundary (in hours), find blocks that just crossed it
|
||||||
for (boundary_idx, &boundary_hours) in AGE_BOUNDARIES.iter().enumerate() {
|
for (boundary_idx, &boundary_hours) in AGE_BOUNDARIES.iter().enumerate() {
|
||||||
|
|||||||
@@ -128,6 +128,9 @@ pub(crate) fn reset_state(
|
|||||||
utxo_cohorts.reset_separate_cost_basis_data()?;
|
utxo_cohorts.reset_separate_cost_basis_data()?;
|
||||||
addr_cohorts.reset_separate_cost_basis_data()?;
|
addr_cohorts.reset_separate_cost_basis_data()?;
|
||||||
|
|
||||||
|
// Reset in-memory caches (fenwick, tick_tock positions)
|
||||||
|
utxo_cohorts.reset_caches();
|
||||||
|
|
||||||
Ok(RecoveredState {
|
Ok(RecoveredState {
|
||||||
starting_height: Height::ZERO,
|
starting_height: Height::ZERO,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -249,7 +249,6 @@ pub struct CostBasisData<S: Accumulate> {
|
|||||||
pending: FxHashMap<CentsCompact, PendingDelta>,
|
pending: FxHashMap<CentsCompact, PendingDelta>,
|
||||||
cache: Option<CachedUnrealizedState<S>>,
|
cache: Option<CachedUnrealizedState<S>>,
|
||||||
rounding_digits: Option<i32>,
|
rounding_digits: Option<i32>,
|
||||||
generation: u64,
|
|
||||||
investor_cap_raw: CentsSquaredSats,
|
investor_cap_raw: CentsSquaredSats,
|
||||||
pending_investor_cap: PendingInvestorCapDelta,
|
pending_investor_cap: PendingInvestorCapDelta,
|
||||||
}
|
}
|
||||||
@@ -297,7 +296,6 @@ impl<S: Accumulate> CostBasisData<S> {
|
|||||||
if self.pending.is_empty() {
|
if self.pending.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.generation = self.generation.wrapping_add(1);
|
|
||||||
let map = &mut self.map.as_mut().unwrap().map;
|
let map = &mut self.map.as_mut().unwrap().map;
|
||||||
for (cents, PendingDelta { inc, dec }) in self.pending.drain() {
|
for (cents, PendingDelta { inc, dec }) in self.pending.drain() {
|
||||||
match map.entry(cents) {
|
match map.entry(cents) {
|
||||||
@@ -353,7 +351,6 @@ impl<S: Accumulate> CostBasisOps for CostBasisData<S> {
|
|||||||
pending: FxHashMap::default(),
|
pending: FxHashMap::default(),
|
||||||
cache: None,
|
cache: None,
|
||||||
rounding_digits: None,
|
rounding_digits: None,
|
||||||
generation: 0,
|
|
||||||
investor_cap_raw: CentsSquaredSats::ZERO,
|
investor_cap_raw: CentsSquaredSats::ZERO,
|
||||||
pending_investor_cap: PendingInvestorCapDelta::default(),
|
pending_investor_cap: PendingInvestorCapDelta::default(),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ use brk_error::Result;
|
|||||||
use brk_indexer::Indexer;
|
use brk_indexer::Indexer;
|
||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::{
|
use brk_types::{
|
||||||
Cents, EmptyAddrData, EmptyAddrIndex, FundedAddrData, FundedAddrIndex, Height,
|
Cents, EmptyAddrData, EmptyAddrIndex, FundedAddrData, FundedAddrIndex, Height, Indexes,
|
||||||
Indexes, StoredF64, SupplyState, Timestamp, TxIndex, Version,
|
StoredF64, SupplyState, Timestamp, TxIndex, Version,
|
||||||
};
|
};
|
||||||
use tracing::{debug, info};
|
use tracing::{debug, info};
|
||||||
use vecdb::{
|
use vecdb::{
|
||||||
@@ -23,15 +23,16 @@ use crate::{
|
|||||||
state::BlockState,
|
state::BlockState,
|
||||||
},
|
},
|
||||||
indexes, inputs,
|
indexes, inputs,
|
||||||
internal::{CachedWindowStarts, PerBlockCumulativeRolling, db_utils::{finalize_db, open_db}},
|
internal::{
|
||||||
|
CachedWindowStarts, PerBlockCumulativeRolling,
|
||||||
|
db_utils::{finalize_db, open_db},
|
||||||
|
},
|
||||||
outputs, prices, transactions,
|
outputs, prices, transactions,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
AddrCohorts, AddrsDataVecs, AnyAddrIndexesVecs, RangeMap, UTXOCohorts,
|
AddrCohorts, AddrsDataVecs, AnyAddrIndexesVecs, RangeMap, UTXOCohorts,
|
||||||
addr::{
|
addr::{AddrActivityVecs, AddrCountsVecs, DeltaVecs, NewAddrCountVecs, TotalAddrCountVecs},
|
||||||
AddrCountsVecs, AddrActivityVecs, DeltaVecs, NewAddrCountVecs, TotalAddrCountVecs,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const VERSION: Version = Version::new(22);
|
const VERSION: Version = Version::new(22);
|
||||||
@@ -48,8 +49,7 @@ pub struct AddrMetricsVecs<M: StorageMode = Rw> {
|
|||||||
pub funded_index:
|
pub funded_index:
|
||||||
LazyVecFrom1<FundedAddrIndex, FundedAddrIndex, FundedAddrIndex, FundedAddrData>,
|
LazyVecFrom1<FundedAddrIndex, FundedAddrIndex, FundedAddrIndex, FundedAddrData>,
|
||||||
#[traversable(wrap = "indexes", rename = "empty")]
|
#[traversable(wrap = "indexes", rename = "empty")]
|
||||||
pub empty_index:
|
pub empty_index: LazyVecFrom1<EmptyAddrIndex, EmptyAddrIndex, EmptyAddrIndex, EmptyAddrData>,
|
||||||
LazyVecFrom1<EmptyAddrIndex, EmptyAddrIndex, EmptyAddrIndex, EmptyAddrData>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Traversable)]
|
#[derive(Traversable)]
|
||||||
@@ -73,23 +73,26 @@ pub struct Vecs<M: StorageMode = Rw> {
|
|||||||
pub coinblocks_destroyed: PerBlockCumulativeRolling<StoredF64, StoredF64, M>,
|
pub coinblocks_destroyed: PerBlockCumulativeRolling<StoredF64, StoredF64, M>,
|
||||||
pub addrs: AddrMetricsVecs<M>,
|
pub addrs: AddrMetricsVecs<M>,
|
||||||
|
|
||||||
/// In-memory block state for UTXO processing. Persisted via supply_state.
|
/// In-memory state that does NOT survive rollback.
|
||||||
/// Kept across compute() calls to avoid O(n) rebuild on resume.
|
/// Grouped so that adding a new field automatically gets it reset.
|
||||||
#[traversable(skip)]
|
#[traversable(skip)]
|
||||||
chain_state: Vec<BlockState>,
|
caches: DistributionTransientState,
|
||||||
/// In-memory tx_index→height reverse lookup. Kept across compute() calls.
|
}
|
||||||
#[traversable(skip)]
|
|
||||||
tx_index_to_height: RangeMap<TxIndex, Height>,
|
|
||||||
|
|
||||||
/// Cached height→price mapping. Incrementally extended, O(new_blocks) on resume.
|
/// In-memory state that does NOT survive rollback.
|
||||||
#[traversable(skip)]
|
/// On rollback, the entire struct is replaced with `Default::default()`.
|
||||||
cached_prices: Vec<Cents>,
|
#[derive(Clone, Default)]
|
||||||
/// Cached height→timestamp mapping. Incrementally extended, O(new_blocks) on resume.
|
struct DistributionTransientState {
|
||||||
#[traversable(skip)]
|
/// Block state for UTXO processing. Persisted via supply_state.
|
||||||
cached_timestamps: Vec<Timestamp>,
|
chain_state: Vec<BlockState>,
|
||||||
/// Cached sparse table for O(1) range-max price queries. Incrementally extended.
|
/// tx_index→height reverse lookup.
|
||||||
#[traversable(skip)]
|
tx_index_to_height: RangeMap<TxIndex, Height>,
|
||||||
cached_price_range_max: PriceRangeMax,
|
/// Height→price mapping. Incrementally extended.
|
||||||
|
prices: Vec<Cents>,
|
||||||
|
/// Height→timestamp mapping. Incrementally extended.
|
||||||
|
timestamps: Vec<Timestamp>,
|
||||||
|
/// Sparse table for O(1) range-max price queries. Incrementally extended.
|
||||||
|
price_range_max: PriceRangeMax,
|
||||||
}
|
}
|
||||||
|
|
||||||
const SAVED_STAMPED_CHANGES: u16 = 10;
|
const SAVED_STAMPED_CHANGES: u16 = 10;
|
||||||
@@ -109,9 +112,11 @@ impl Vecs {
|
|||||||
|
|
||||||
let version = parent_version + VERSION;
|
let version = parent_version + VERSION;
|
||||||
|
|
||||||
let utxo_cohorts = UTXOCohorts::forced_import(&db, version, indexes, &states_path, cached_starts)?;
|
let utxo_cohorts =
|
||||||
|
UTXOCohorts::forced_import(&db, version, indexes, &states_path, cached_starts)?;
|
||||||
|
|
||||||
let addr_cohorts = AddrCohorts::forced_import(&db, version, indexes, &states_path, cached_starts)?;
|
let addr_cohorts =
|
||||||
|
AddrCohorts::forced_import(&db, version, indexes, &states_path, cached_starts)?;
|
||||||
|
|
||||||
// Create address data BytesVecs first so we can also use them for identity mappings
|
// Create address data BytesVecs first so we can also use them for identity mappings
|
||||||
let funded_addr_index_to_funded_addr_data = BytesVec::forced_import_with(
|
let funded_addr_index_to_funded_addr_data = BytesVec::forced_import_with(
|
||||||
@@ -147,8 +152,7 @@ impl Vecs {
|
|||||||
let total_addr_count = TotalAddrCountVecs::forced_import(&db, version, indexes)?;
|
let total_addr_count = TotalAddrCountVecs::forced_import(&db, version, indexes)?;
|
||||||
|
|
||||||
// Per-block delta of total (global + per-type)
|
// Per-block delta of total (global + per-type)
|
||||||
let new_addr_count =
|
let new_addr_count = NewAddrCountVecs::forced_import(&db, version, indexes, cached_starts)?;
|
||||||
NewAddrCountVecs::forced_import(&db, version, indexes, cached_starts)?;
|
|
||||||
|
|
||||||
// Growth rate: delta change + rate (global + per-type)
|
// Growth rate: delta change + rate (global + per-type)
|
||||||
let delta = DeltaVecs::new(version, &addr_count, cached_starts, indexes);
|
let delta = DeltaVecs::new(version, &addr_count, cached_starts, indexes);
|
||||||
@@ -186,12 +190,7 @@ impl Vecs {
|
|||||||
funded: funded_addr_index_to_funded_addr_data,
|
funded: funded_addr_index_to_funded_addr_data,
|
||||||
empty: empty_addr_index_to_empty_addr_data,
|
empty: empty_addr_index_to_empty_addr_data,
|
||||||
},
|
},
|
||||||
chain_state: Vec::new(),
|
caches: DistributionTransientState::default(),
|
||||||
tx_index_to_height: RangeMap::default(),
|
|
||||||
|
|
||||||
cached_prices: Vec::new(),
|
|
||||||
cached_timestamps: Vec::new(),
|
|
||||||
cached_price_range_max: PriceRangeMax::default(),
|
|
||||||
|
|
||||||
db,
|
db,
|
||||||
states_path,
|
states_path,
|
||||||
@@ -201,6 +200,12 @@ impl Vecs {
|
|||||||
Ok(this)
|
Ok(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reset in-memory caches that become stale after rollback.
|
||||||
|
fn reset_in_memory_caches(&mut self) {
|
||||||
|
self.utxo_cohorts.reset_caches();
|
||||||
|
self.caches = DistributionTransientState::default();
|
||||||
|
}
|
||||||
|
|
||||||
/// Main computation loop.
|
/// Main computation loop.
|
||||||
///
|
///
|
||||||
/// Processes blocks to compute UTXO and address cohort metrics:
|
/// Processes blocks to compute UTXO and address cohort metrics:
|
||||||
@@ -222,32 +227,6 @@ impl Vecs {
|
|||||||
starting_indexes: &mut Indexes,
|
starting_indexes: &mut Indexes,
|
||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let cache_target_len = prices
|
|
||||||
.spot
|
|
||||||
.cents
|
|
||||||
.height
|
|
||||||
.len()
|
|
||||||
.min(blocks.time.timestamp_monotonic.len());
|
|
||||||
let cache_current_len = self.cached_prices.len();
|
|
||||||
if cache_target_len < cache_current_len {
|
|
||||||
self.cached_prices.truncate(cache_target_len);
|
|
||||||
self.cached_timestamps.truncate(cache_target_len);
|
|
||||||
self.cached_price_range_max.truncate(cache_target_len);
|
|
||||||
} else if cache_target_len > cache_current_len {
|
|
||||||
let new_prices = prices
|
|
||||||
.spot
|
|
||||||
.cents
|
|
||||||
.height
|
|
||||||
.collect_range_at(cache_current_len, cache_target_len);
|
|
||||||
let new_timestamps = blocks
|
|
||||||
.time
|
|
||||||
.timestamp_monotonic
|
|
||||||
.collect_range_at(cache_current_len, cache_target_len);
|
|
||||||
self.cached_prices.extend(new_prices);
|
|
||||||
self.cached_timestamps.extend(new_timestamps);
|
|
||||||
}
|
|
||||||
self.cached_price_range_max.extend(&self.cached_prices);
|
|
||||||
|
|
||||||
// 1. Find minimum height we have data for across stateful vecs
|
// 1. Find minimum height we have data for across stateful vecs
|
||||||
let current_height = Height::from(self.supply_state.len());
|
let current_height = Height::from(self.supply_state.len());
|
||||||
let min_stateful = self.min_stateful_len();
|
let min_stateful = self.min_stateful_len();
|
||||||
@@ -281,9 +260,6 @@ impl Vecs {
|
|||||||
&mut self.addr_cohorts,
|
&mut self.addr_cohorts,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if recovered.starting_height.is_zero() {
|
|
||||||
info!("State recovery validation failed, falling back to fresh start");
|
|
||||||
}
|
|
||||||
debug!(
|
debug!(
|
||||||
"recover_state completed, starting_height={}",
|
"recover_state completed, starting_height={}",
|
||||||
recovered.starting_height
|
recovered.starting_height
|
||||||
@@ -295,12 +271,14 @@ impl Vecs {
|
|||||||
|
|
||||||
debug!("recovered_height={}", recovered_height);
|
debug!("recovered_height={}", recovered_height);
|
||||||
|
|
||||||
// Take chain_state and tx_index_to_height out of self to avoid borrow conflicts
|
let needs_fresh_start = recovered_height.is_zero();
|
||||||
let mut chain_state = std::mem::take(&mut self.chain_state);
|
let needs_rollback = recovered_height < current_height;
|
||||||
let mut tx_index_to_height = std::mem::take(&mut self.tx_index_to_height);
|
|
||||||
|
|
||||||
// Recover or reuse chain_state
|
if needs_fresh_start || needs_rollback {
|
||||||
let starting_height = if recovered_height.is_zero() {
|
self.reset_in_memory_caches();
|
||||||
|
}
|
||||||
|
|
||||||
|
if needs_fresh_start {
|
||||||
self.supply_state.reset()?;
|
self.supply_state.reset()?;
|
||||||
self.addrs.funded.reset_height()?;
|
self.addrs.funded.reset_height()?;
|
||||||
self.addrs.empty.reset_height()?;
|
self.addrs.empty.reset_height()?;
|
||||||
@@ -311,11 +289,44 @@ impl Vecs {
|
|||||||
&mut self.utxo_cohorts,
|
&mut self.utxo_cohorts,
|
||||||
&mut self.addr_cohorts,
|
&mut self.addr_cohorts,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
chain_state.clear();
|
|
||||||
tx_index_to_height.truncate(0);
|
|
||||||
|
|
||||||
info!("State recovery: fresh start");
|
info!("State recovery: fresh start");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate price/timestamp caches from the prices module.
|
||||||
|
// Must happen AFTER rollback/reset (which clears caches) but BEFORE
|
||||||
|
// chain_state rebuild (which reads from them).
|
||||||
|
let cache_target_len = prices
|
||||||
|
.spot
|
||||||
|
.cents
|
||||||
|
.height
|
||||||
|
.len()
|
||||||
|
.min(blocks.time.timestamp_monotonic.len());
|
||||||
|
let cache_current_len = self.caches.prices.len();
|
||||||
|
if cache_target_len < cache_current_len {
|
||||||
|
self.caches.prices.truncate(cache_target_len);
|
||||||
|
self.caches.timestamps.truncate(cache_target_len);
|
||||||
|
self.caches.price_range_max.truncate(cache_target_len);
|
||||||
|
} else if cache_target_len > cache_current_len {
|
||||||
|
let new_prices = prices
|
||||||
|
.spot
|
||||||
|
.cents
|
||||||
|
.height
|
||||||
|
.collect_range_at(cache_current_len, cache_target_len);
|
||||||
|
let new_timestamps = blocks
|
||||||
|
.time
|
||||||
|
.timestamp_monotonic
|
||||||
|
.collect_range_at(cache_current_len, cache_target_len);
|
||||||
|
self.caches.prices.extend(new_prices);
|
||||||
|
self.caches.timestamps.extend(new_timestamps);
|
||||||
|
}
|
||||||
|
self.caches.price_range_max.extend(&self.caches.prices);
|
||||||
|
|
||||||
|
// Take chain_state and tx_index_to_height out of self to avoid borrow conflicts
|
||||||
|
let mut chain_state = std::mem::take(&mut self.caches.chain_state);
|
||||||
|
let mut tx_index_to_height = std::mem::take(&mut self.caches.tx_index_to_height);
|
||||||
|
|
||||||
|
// Recover or reuse chain_state
|
||||||
|
let starting_height = if recovered_height.is_zero() {
|
||||||
Height::ZERO
|
Height::ZERO
|
||||||
} else if chain_state.len() == usize::from(recovered_height) {
|
} else if chain_state.len() == usize::from(recovered_height) {
|
||||||
// Normal resume: chain_state already matches, reuse as-is
|
// Normal resume: chain_state already matches, reuse as-is
|
||||||
@@ -335,8 +346,8 @@ impl Vecs {
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(h, supply)| BlockState {
|
.map(|(h, supply)| BlockState {
|
||||||
supply,
|
supply,
|
||||||
price: self.cached_prices[h],
|
price: self.caches.prices[h],
|
||||||
timestamp: self.cached_timestamps[h],
|
timestamp: self.caches.timestamps[h],
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
debug!("chain_state rebuilt");
|
debug!("chain_state rebuilt");
|
||||||
@@ -352,12 +363,11 @@ impl Vecs {
|
|||||||
starting_indexes.height = starting_height;
|
starting_indexes.height = starting_height;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2b. Validate computed versions
|
// 2c. Validate computed versions
|
||||||
debug!("validating computed versions");
|
debug!("validating computed versions");
|
||||||
let base_version = VERSION;
|
let base_version = VERSION;
|
||||||
self.utxo_cohorts.validate_computed_versions(base_version)?;
|
self.utxo_cohorts.validate_computed_versions(base_version)?;
|
||||||
self.addr_cohorts
|
self.addr_cohorts.validate_computed_versions(base_version)?;
|
||||||
.validate_computed_versions(base_version)?;
|
|
||||||
debug!("computed versions validated");
|
debug!("computed versions validated");
|
||||||
|
|
||||||
// 3. Get last height from indexer
|
// 3. Get last height from indexer
|
||||||
@@ -371,9 +381,9 @@ impl Vecs {
|
|||||||
if starting_height <= last_height {
|
if starting_height <= last_height {
|
||||||
debug!("calling process_blocks");
|
debug!("calling process_blocks");
|
||||||
|
|
||||||
let cached_prices = std::mem::take(&mut self.cached_prices);
|
let prices = std::mem::take(&mut self.caches.prices);
|
||||||
let cached_timestamps = std::mem::take(&mut self.cached_timestamps);
|
let timestamps = std::mem::take(&mut self.caches.timestamps);
|
||||||
let cached_price_range_max = std::mem::take(&mut self.cached_price_range_max);
|
let price_range_max = std::mem::take(&mut self.caches.price_range_max);
|
||||||
|
|
||||||
process_blocks(
|
process_blocks(
|
||||||
self,
|
self,
|
||||||
@@ -386,27 +396,33 @@ impl Vecs {
|
|||||||
last_height,
|
last_height,
|
||||||
&mut chain_state,
|
&mut chain_state,
|
||||||
&mut tx_index_to_height,
|
&mut tx_index_to_height,
|
||||||
&cached_prices,
|
&prices,
|
||||||
&cached_timestamps,
|
×tamps,
|
||||||
&cached_price_range_max,
|
&price_range_max,
|
||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
self.cached_prices = cached_prices;
|
self.caches.prices = prices;
|
||||||
self.cached_timestamps = cached_timestamps;
|
self.caches.timestamps = timestamps;
|
||||||
self.cached_price_range_max = cached_price_range_max;
|
self.caches.price_range_max = price_range_max;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put chain_state and tx_index_to_height back
|
// Put chain_state and tx_index_to_height back
|
||||||
self.chain_state = chain_state;
|
self.caches.chain_state = chain_state;
|
||||||
self.tx_index_to_height = tx_index_to_height;
|
self.caches.tx_index_to_height = tx_index_to_height;
|
||||||
|
|
||||||
// 5. Compute aggregates (overlapping cohorts from separate cohorts)
|
// 5. Compute aggregates (overlapping cohorts from separate cohorts)
|
||||||
info!("Computing overlapping cohorts...");
|
info!("Computing overlapping cohorts...");
|
||||||
{
|
{
|
||||||
let (r1, r2) = rayon::join(
|
let (r1, r2) = rayon::join(
|
||||||
|| self.utxo_cohorts.compute_overlapping_vecs(starting_indexes, exit),
|
|| {
|
||||||
|| self.addr_cohorts.compute_overlapping_vecs(starting_indexes, exit),
|
self.utxo_cohorts
|
||||||
|
.compute_overlapping_vecs(starting_indexes, exit)
|
||||||
|
},
|
||||||
|
|| {
|
||||||
|
self.addr_cohorts
|
||||||
|
.compute_overlapping_vecs(starting_indexes, exit)
|
||||||
|
},
|
||||||
);
|
);
|
||||||
r1?;
|
r1?;
|
||||||
r2?;
|
r2?;
|
||||||
@@ -420,8 +436,14 @@ impl Vecs {
|
|||||||
info!("Computing rest part 1...");
|
info!("Computing rest part 1...");
|
||||||
{
|
{
|
||||||
let (r1, r2) = rayon::join(
|
let (r1, r2) = rayon::join(
|
||||||
|| self.utxo_cohorts.compute_rest_part1(prices, starting_indexes, exit),
|
|| {
|
||||||
|| self.addr_cohorts.compute_rest_part1(prices, starting_indexes, exit),
|
self.utxo_cohorts
|
||||||
|
.compute_rest_part1(prices, starting_indexes, exit)
|
||||||
|
},
|
||||||
|
|| {
|
||||||
|
self.addr_cohorts
|
||||||
|
.compute_rest_part1(prices, starting_indexes, exit)
|
||||||
|
},
|
||||||
);
|
);
|
||||||
r1?;
|
r1?;
|
||||||
r2?;
|
r2?;
|
||||||
@@ -442,11 +464,9 @@ impl Vecs {
|
|||||||
self.addrs
|
self.addrs
|
||||||
.activity
|
.activity
|
||||||
.compute_rest(starting_indexes.height, exit)?;
|
.compute_rest(starting_indexes.height, exit)?;
|
||||||
self.addrs.new.compute(
|
self.addrs
|
||||||
starting_indexes.height,
|
.new
|
||||||
&self.addrs.total,
|
.compute(starting_indexes.height, &self.addrs.total, exit)?;
|
||||||
exit,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// 7. Compute rest part2 (relative metrics)
|
// 7. Compute rest part2 (relative metrics)
|
||||||
let height_to_market_cap = self
|
let height_to_market_cap = self
|
||||||
@@ -468,7 +488,14 @@ impl Vecs {
|
|||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let all_utxo_count = self.utxo_cohorts.all.metrics.outputs.unspent_count.height.read_only_clone();
|
let all_utxo_count = self
|
||||||
|
.utxo_cohorts
|
||||||
|
.all
|
||||||
|
.metrics
|
||||||
|
.outputs
|
||||||
|
.unspent_count
|
||||||
|
.height
|
||||||
|
.read_only_clone();
|
||||||
self.addr_cohorts
|
self.addr_cohorts
|
||||||
.compute_rest_part2(prices, starting_indexes, &all_utxo_count, exit)?;
|
.compute_rest_part2(prices, starting_indexes, &all_utxo_count, exit)?;
|
||||||
|
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ impl Vecs {
|
|||||||
parent_version: Version,
|
parent_version: Version,
|
||||||
indexer: &Indexer,
|
indexer: &Indexer,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let db = open_db(parent, DB_NAME, 10_000_000)?;
|
let db = open_db(parent, DB_NAME, 1_000_000)?;
|
||||||
|
|
||||||
let version = parent_version;
|
let version = parent_version;
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ impl Vecs {
|
|||||||
indexes: &indexes::Vecs,
|
indexes: &indexes::Vecs,
|
||||||
cached_starts: &CachedWindowStarts,
|
cached_starts: &CachedWindowStarts,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let db = open_db(parent_path, super::DB_NAME, 50_000_000)?;
|
let db = open_db(parent_path, super::DB_NAME, 20_000_000)?;
|
||||||
let version = parent_version;
|
let version = parent_version;
|
||||||
|
|
||||||
let spent = SpentVecs::forced_import(&db, version)?;
|
let spent = SpentVecs::forced_import(&db, version)?;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ impl Vecs {
|
|||||||
parent_version: Version,
|
parent_version: Version,
|
||||||
indexes: &indexes::Vecs,
|
indexes: &indexes::Vecs,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let db = open_db(parent_path, super::DB_NAME, 1_000_000)?;
|
let db = open_db(parent_path, super::DB_NAME, 250_000)?;
|
||||||
let version = parent_version;
|
let version = parent_version;
|
||||||
|
|
||||||
let ath = AthVecs::forced_import(&db, version, indexes)?;
|
let ath = AthVecs::forced_import(&db, version, indexes)?;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ impl Vecs {
|
|||||||
indexes: &indexes::Vecs,
|
indexes: &indexes::Vecs,
|
||||||
cached_starts: &CachedWindowStarts,
|
cached_starts: &CachedWindowStarts,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let db = open_db(parent_path, super::DB_NAME, 50_000_000)?;
|
let db = open_db(parent_path, super::DB_NAME, 1_000_000)?;
|
||||||
let version = parent_version;
|
let version = parent_version;
|
||||||
|
|
||||||
let rewards = RewardsVecs::forced_import(&db, version, indexes, cached_starts)?;
|
let rewards = RewardsVecs::forced_import(&db, version, indexes, cached_starts)?;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ impl Vecs {
|
|||||||
indexes: &indexes::Vecs,
|
indexes: &indexes::Vecs,
|
||||||
cached_starts: &CachedWindowStarts,
|
cached_starts: &CachedWindowStarts,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let db = open_db(parent_path, super::DB_NAME, 10_000_000)?;
|
let db = open_db(parent_path, super::DB_NAME, 20_000_000)?;
|
||||||
let version = parent_version;
|
let version = parent_version;
|
||||||
|
|
||||||
let spent = SpentVecs::forced_import(&db, version)?;
|
let spent = SpentVecs::forced_import(&db, version)?;
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ impl Vecs {
|
|||||||
indexes: &indexes::Vecs,
|
indexes: &indexes::Vecs,
|
||||||
cached_starts: &CachedWindowStarts,
|
cached_starts: &CachedWindowStarts,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let db = open_db(parent_path, DB_NAME, 1_000_000)?;
|
let db = open_db(parent_path, DB_NAME, 100_000)?;
|
||||||
let pools = pools();
|
let pools = pools();
|
||||||
|
|
||||||
let version = parent_version + Version::new(3) + Version::new(pools.len() as u32);
|
let version = parent_version + Version::new(3) + Version::new(pools.len() as u32);
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ impl Vecs {
|
|||||||
version: Version,
|
version: Version,
|
||||||
indexes: &indexes::Vecs,
|
indexes: &indexes::Vecs,
|
||||||
) -> brk_error::Result<Self> {
|
) -> brk_error::Result<Self> {
|
||||||
let db = open_db(parent, DB_NAME, 1_000_000)?;
|
let db = open_db(parent, DB_NAME, 100_000)?;
|
||||||
let this = Self::forced_import_inner(&db, version, indexes)?;
|
let this = Self::forced_import_inner(&db, version, indexes)?;
|
||||||
finalize_db(&this.db, &this)?;
|
finalize_db(&this.db, &this)?;
|
||||||
Ok(this)
|
Ok(this)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ impl Vecs {
|
|||||||
indexes: &indexes::Vecs,
|
indexes: &indexes::Vecs,
|
||||||
cached_starts: &CachedWindowStarts,
|
cached_starts: &CachedWindowStarts,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let db = open_db(parent_path, super::DB_NAME, 50_000_000)?;
|
let db = open_db(parent_path, super::DB_NAME, 1_000_000)?;
|
||||||
let version = parent_version;
|
let version = parent_version;
|
||||||
|
|
||||||
let count = CountVecs::forced_import(&db, version, indexes, cached_starts)?;
|
let count = CountVecs::forced_import(&db, version, indexes, cached_starts)?;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ impl Vecs {
|
|||||||
cointime: &cointime::Vecs,
|
cointime: &cointime::Vecs,
|
||||||
cached_starts: &CachedWindowStarts,
|
cached_starts: &CachedWindowStarts,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let db = open_db(parent, super::DB_NAME, 10_000_000)?;
|
let db = open_db(parent, super::DB_NAME, 1_000_000)?;
|
||||||
|
|
||||||
let version = parent_version + VERSION;
|
let version = parent_version + VERSION;
|
||||||
let supply_metrics = &distribution.utxo_cohorts.all.metrics.supply;
|
let supply_metrics = &distribution.utxo_cohorts.all.metrics.supply;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ impl Vecs {
|
|||||||
indexes: &indexes::Vecs,
|
indexes: &indexes::Vecs,
|
||||||
cached_starts: &CachedWindowStarts,
|
cached_starts: &CachedWindowStarts,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let db = open_db(parent_path, super::DB_NAME, 50_000_000)?;
|
let db = open_db(parent_path, super::DB_NAME, 10_000_000)?;
|
||||||
let version = parent_version;
|
let version = parent_version;
|
||||||
|
|
||||||
let count = CountVecs::forced_import(&db, version, indexer, indexes, cached_starts)?;
|
let count = CountVecs::forced_import(&db, version, indexer, indexes, cached_starts)?;
|
||||||
|
|||||||
@@ -110,6 +110,12 @@ impl Indexer {
|
|||||||
debug!("Starting indexing...");
|
debug!("Starting indexing...");
|
||||||
|
|
||||||
let last_blockhash = self.vecs.blocks.blockhash.collect_last();
|
let last_blockhash = self.vecs.blocks.blockhash.collect_last();
|
||||||
|
// Rollback sim
|
||||||
|
// let last_blockhash = self
|
||||||
|
// .vecs
|
||||||
|
// .blocks
|
||||||
|
// .blockhash
|
||||||
|
// .collect_one_at(self.vecs.blocks.blockhash.len() - 2);
|
||||||
debug!("Last block hash found.");
|
debug!("Last block hash found.");
|
||||||
|
|
||||||
let (starting_indexes, prev_hash) = if let Some(hash) = last_blockhash {
|
let (starting_indexes, prev_hash) = if let Some(hash) = last_blockhash {
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ impl Vecs {
|
|||||||
tracing::debug!("Opening vecs database...");
|
tracing::debug!("Opening vecs database...");
|
||||||
let db = Database::open(&parent.join("vecs"))?;
|
let db = Database::open(&parent.join("vecs"))?;
|
||||||
tracing::debug!("Setting min len...");
|
tracing::debug!("Setting min len...");
|
||||||
db.set_min_len(PAGE_SIZE * 50_000_000)?;
|
db.set_min_len(PAGE_SIZE * 60_000_000)?;
|
||||||
|
|
||||||
let (blocks, transactions, inputs, outputs, addrs, scripts) = parallel_import! {
|
let (blocks, transactions, inputs, outputs, addrs, scripts) = parallel_import! {
|
||||||
blocks = BlocksVecs::forced_import(&db, version),
|
blocks = BlocksVecs::forced_import(&db, version),
|
||||||
|
|||||||
@@ -63,7 +63,8 @@ impl Query {
|
|||||||
|
|
||||||
/// Current computed height (series)
|
/// Current computed height (series)
|
||||||
pub fn computed_height(&self) -> Height {
|
pub fn computed_height(&self) -> Height {
|
||||||
Height::from(self.computer().distribution.supply_state.len())
|
let len = self.computer().distribution.supply_state.len();
|
||||||
|
Height::from(len.saturating_sub(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Minimum of indexed and computed heights
|
/// Minimum of indexed and computed heights
|
||||||
|
|||||||
@@ -107,14 +107,6 @@ All errors return structured JSON with a consistent format:
|
|||||||
),
|
),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Tag {
|
|
||||||
name: "Metrics".to_string(),
|
|
||||||
description: Some(
|
|
||||||
"Deprecated — use Series".to_string(),
|
|
||||||
),
|
|
||||||
extensions: [("x-deprecated".to_string(), serde_json::Value::Bool(true))].into(),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
Tag {
|
Tag {
|
||||||
name: "Blocks".to_string(),
|
name: "Blocks".to_string(),
|
||||||
description: Some(
|
description: Some(
|
||||||
@@ -165,6 +157,14 @@ All errors return structured JSON with a consistent format:
|
|||||||
),
|
),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
Tag {
|
||||||
|
name: "Metrics".to_string(),
|
||||||
|
description: Some(
|
||||||
|
"Deprecated — use Series".to_string(),
|
||||||
|
),
|
||||||
|
extensions: [("deprecated".to_string(), serde_json::Value::Bool(true))].into(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
OpenApi {
|
OpenApi {
|
||||||
|
|||||||
BIN
website/assets/fonts/InstrumentSerif-Italic.woff2
Normal file
BIN
website/assets/fonts/InstrumentSerif-Italic.woff2
Normal file
Binary file not shown.
BIN
website/assets/fonts/InstrumentSerif-Regular.woff2
Normal file
BIN
website/assets/fonts/InstrumentSerif-Regular.woff2
Normal file
Binary file not shown.
BIN
website/assets/fonts/Satoshi-Variable.woff2
Normal file
BIN
website/assets/fonts/Satoshi-Variable.woff2
Normal file
Binary file not shown.
@@ -403,7 +403,7 @@ export function createChart({ parent, brk, fitContent }) {
|
|||||||
if (!pane) return;
|
if (!pane) return;
|
||||||
if (this.isAllHidden(paneIndex)) {
|
if (this.isAllHidden(paneIndex)) {
|
||||||
const chartHeight = ichart.chartElement().clientHeight;
|
const chartHeight = ichart.chartElement().clientHeight;
|
||||||
pane.setStretchFactor(chartHeight > 0 ? 32 / (chartHeight - 32) : 0);
|
pane.setStretchFactor(chartHeight > 0 ? 48 / (chartHeight - 48) : 0);
|
||||||
} else {
|
} else {
|
||||||
pane.setStretchFactor(1);
|
pane.setStretchFactor(1);
|
||||||
}
|
}
|
||||||
@@ -1445,7 +1445,7 @@ export function createChart({ parent, brk, fitContent }) {
|
|||||||
|
|
||||||
const lastTd = ichart
|
const lastTd = ichart
|
||||||
.chartElement()
|
.chartElement()
|
||||||
.querySelector("table > tr:last-child > td:nth-child(2)");
|
.querySelector("table > tr:last-child > td:last-child");
|
||||||
|
|
||||||
const chart = {
|
const chart = {
|
||||||
get panes() {
|
get panes() {
|
||||||
@@ -1474,9 +1474,6 @@ export function createChart({ parent, brk, fitContent }) {
|
|||||||
groups,
|
groups,
|
||||||
id: "index",
|
id: "index",
|
||||||
});
|
});
|
||||||
const sep = document.createElement("span");
|
|
||||||
sep.textContent = "|";
|
|
||||||
indexField.append(sep);
|
|
||||||
if (lastTd) lastTd.append(indexField);
|
if (lastTd) lastTd.append(indexField);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -308,6 +308,12 @@ export function createSelect({
|
|||||||
arrow.textContent = "↓";
|
arrow.textContent = "↓";
|
||||||
field.append(arrow);
|
field.append(arrow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
field.addEventListener("click", (e) => {
|
||||||
|
if (e.target !== select) {
|
||||||
|
select.showPicker();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return field;
|
return field;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
.chart {
|
.chart {
|
||||||
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
@@ -138,6 +139,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
gap: 0.375rem;
|
gap: 0.375rem;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
table > tr {
|
table > tr {
|
||||||
@@ -203,15 +205,18 @@
|
|||||||
|
|
||||||
td:last-child > .field {
|
td:last-child > .field {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
|
||||||
z-index: 50;
|
z-index: 50;
|
||||||
display: flex;
|
display: inline-flex;
|
||||||
font-size: var(--font-size-xs);
|
font-size: var(--font-size-xs);
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:not(:last-child) > td:last-child > .field {
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
gap: 0.375rem;
|
gap: 0.375rem;
|
||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
align-items: center;
|
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
padding-left: 0.625rem;
|
padding-left: 0.625rem;
|
||||||
padding-top: 0.35rem;
|
padding-top: 0.35rem;
|
||||||
@@ -232,10 +237,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tr:last-child > td:last-child > .field {
|
||||||
|
bottom: 2.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
button.capture {
|
button.capture {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0.5rem;
|
top: -0.75rem;
|
||||||
right: 0.5rem;
|
right: -0.75rem;
|
||||||
z-index: 50;
|
z-index: 50;
|
||||||
font-size: var(--font-size-xs);
|
font-size: var(--font-size-xs);
|
||||||
line-height: var(--line-height-xs);
|
line-height: var(--line-height-xs);
|
||||||
|
|||||||
@@ -95,7 +95,6 @@ button {
|
|||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
text-transform: uppercase;
|
|
||||||
font-size: var(--font-size-xl);
|
font-size: var(--font-size-xl);
|
||||||
line-height: var(--line-height-xl);
|
line-height: var(--line-height-xl);
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
@@ -242,7 +241,6 @@ summary {
|
|||||||
&::-webkit-details-marker {
|
&::-webkit-details-marker {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:is(a, button, summary) {
|
:is(a, button, summary) {
|
||||||
|
|||||||
@@ -14,8 +14,34 @@
|
|||||||
font-display: block;
|
font-display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: Instrument;
|
||||||
|
src: url("/assets/fonts/InstrumentSerif-Regular.woff2") format("woff2");
|
||||||
|
font-style: normal;
|
||||||
|
font-display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: Instrument;
|
||||||
|
src: url("/assets/fonts/InstrumentSerif-Italic.woff2") format("woff2");
|
||||||
|
font-style: italic;
|
||||||
|
font-display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: Satoshi;
|
||||||
|
src: url("/assets/fonts/Satoshi-Variable.woff2") format("woff2");
|
||||||
|
font-weight: 100 900;
|
||||||
|
font-display: block;
|
||||||
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
font-family:
|
font-family:
|
||||||
"Lilex", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
"Lilex", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
||||||
"Liberation Mono", "Courier New", monospace;
|
"Liberation Mono", "Courier New", monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family:
|
||||||
|
Instrument, Charter, "Bitstream Charter", "Sitka Text", Cambria, serif;
|
||||||
|
}
|
||||||
|
|||||||
@@ -44,14 +44,14 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
padding-bottom: 1rem;
|
padding-bottom: 1rem;
|
||||||
margin-bottom: -1rem;
|
margin-bottom: -0.75rem;
|
||||||
padding-left: var(--main-padding);
|
padding-left: var(--main-padding);
|
||||||
margin-left: var(--negative-main-padding);
|
margin-left: var(--negative-main-padding);
|
||||||
padding-right: var(--main-padding);
|
padding-right: var(--main-padding);
|
||||||
margin-right: var(--negative-main-padding);
|
margin-right: var(--negative-main-padding);
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 1.375rem;
|
font-size: 2rem;
|
||||||
letter-spacing: 0.075rem;
|
letter-spacing: 0.075rem;
|
||||||
text-wrap: nowrap;
|
text-wrap: nowrap;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user