global: address -> addr rename

This commit is contained in:
nym21
2026-03-17 11:01:21 +01:00
parent 5609e6c010
commit f62943199c
141 changed files with 3788 additions and 3754 deletions
@@ -0,0 +1,303 @@
//! Address activity tracking - per-block counts of address behaviors.
//!
//! Tracks global and per-address-type activity metrics:
//!
//! | Metric | Description |
//! |--------|-------------|
//! | `receiving` | Unique addresses that received this block |
//! | `sending` | Unique addresses that sent this block |
//! | `reactivated` | Addresses that were empty and now have funds |
//! | `both` | Addresses that both sent AND received same block |
use brk_cohort::ByAddrType;
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, StoredU32, Version};
use derive_more::{Deref, DerefMut};
use rayon::prelude::*;
use vecdb::{AnyStoredVec, AnyVec, Database, Exit, Rw, StorageMode, WritableVec};
use crate::{
indexes,
internal::{CachedWindowStarts, PerBlockRollingAverage},
};
/// Per-block activity counts - reset each block.
#[derive(Debug, Default, Clone)]
pub struct BlockActivityCounts {
pub reactivated: u32,
pub sending: u32,
pub receiving: u32,
pub both: u32,
}
impl BlockActivityCounts {
/// Reset all counts to zero.
#[inline]
pub(crate) fn reset(&mut self) {
*self = Self::default();
}
}
/// Per-address-type activity counts - aggregated during block processing.
#[derive(Debug, Default, Deref, DerefMut)]
pub struct AddrTypeToActivityCounts(pub ByAddrType<BlockActivityCounts>);
impl AddrTypeToActivityCounts {
/// Reset all per-type counts.
pub(crate) fn reset(&mut self) {
self.0.values_mut().for_each(|v| v.reset());
}
/// Sum all types to get totals.
pub(crate) fn totals(&self) -> BlockActivityCounts {
let mut total = BlockActivityCounts::default();
for counts in self.0.values() {
total.reactivated += counts.reactivated;
total.sending += counts.sending;
total.receiving += counts.receiving;
total.both += counts.both;
}
total
}
}
/// Activity count vectors for a single category (e.g., one address type or "all").
#[derive(Traversable)]
pub struct ActivityCountVecs<M: StorageMode = Rw> {
pub reactivated: PerBlockRollingAverage<StoredU32, M>,
pub sending: PerBlockRollingAverage<StoredU32, M>,
pub receiving: PerBlockRollingAverage<StoredU32, M>,
pub both: PerBlockRollingAverage<StoredU32, M>,
}
impl ActivityCountVecs {
pub(crate) fn forced_import(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
cached_starts: &CachedWindowStarts,
) -> Result<Self> {
Ok(Self {
reactivated: PerBlockRollingAverage::forced_import(
db,
&format!("{name}_reactivated"),
version,
indexes,
cached_starts,
)?,
sending: PerBlockRollingAverage::forced_import(
db,
&format!("{name}_sending"),
version,
indexes,
cached_starts,
)?,
receiving: PerBlockRollingAverage::forced_import(
db,
&format!("{name}_receiving"),
version,
indexes,
cached_starts,
)?,
both: PerBlockRollingAverage::forced_import(
db,
&format!("{name}_both"),
version,
indexes,
cached_starts,
)?,
})
}
pub(crate) fn min_stateful_len(&self) -> usize {
self.reactivated
.base
.len()
.min(self.sending.base.len())
.min(self.receiving.base.len())
.min(self.both.base.len())
}
pub(crate) fn par_iter_height_mut(
&mut self,
) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
[
&mut self.reactivated.base as &mut dyn AnyStoredVec,
&mut self.sending.base as &mut dyn AnyStoredVec,
&mut self.receiving.base as &mut dyn AnyStoredVec,
&mut self.both.base as &mut dyn AnyStoredVec,
]
.into_par_iter()
}
pub(crate) fn reset_height(&mut self) -> Result<()> {
self.reactivated.base.reset()?;
self.sending.base.reset()?;
self.receiving.base.reset()?;
self.both.base.reset()?;
Ok(())
}
#[inline(always)]
pub(crate) fn push_height(&mut self, counts: &BlockActivityCounts) {
self.reactivated.base.push(counts.reactivated.into());
self.sending.base.push(counts.sending.into());
self.receiving.base.push(counts.receiving.into());
self.both.base.push(counts.both.into());
}
pub(crate) fn compute_rest(
&mut self,
max_from: Height,
exit: &Exit,
) -> Result<()> {
self.reactivated.compute_rest(max_from, exit)?;
self.sending.compute_rest(max_from, exit)?;
self.receiving.compute_rest(max_from, exit)?;
self.both.compute_rest(max_from, exit)?;
Ok(())
}
}
/// Per-address-type activity count vecs.
#[derive(Deref, DerefMut, Traversable)]
pub struct AddrTypeToActivityCountVecs<M: StorageMode = Rw>(ByAddrType<ActivityCountVecs<M>>);
impl From<ByAddrType<ActivityCountVecs>> for AddrTypeToActivityCountVecs {
#[inline]
fn from(value: ByAddrType<ActivityCountVecs>) -> Self {
Self(value)
}
}
impl AddrTypeToActivityCountVecs {
pub(crate) fn forced_import(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
cached_starts: &CachedWindowStarts,
) -> Result<Self> {
Ok(Self::from(
ByAddrType::<ActivityCountVecs>::new_with_name(|type_name| {
ActivityCountVecs::forced_import(
db,
&format!("{type_name}_{name}"),
version,
indexes,
cached_starts,
)
})?,
))
}
pub(crate) fn min_stateful_len(&self) -> usize {
self.0
.values()
.map(|v| v.min_stateful_len())
.min()
.unwrap_or(0)
}
pub(crate) fn par_iter_height_mut(
&mut self,
) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
let mut vecs: Vec<&mut dyn AnyStoredVec> = Vec::new();
for type_vecs in self.0.values_mut() {
vecs.push(&mut type_vecs.reactivated.base);
vecs.push(&mut type_vecs.sending.base);
vecs.push(&mut type_vecs.receiving.base);
vecs.push(&mut type_vecs.both.base);
}
vecs.into_par_iter()
}
pub(crate) fn reset_height(&mut self) -> Result<()> {
for v in self.0.values_mut() {
v.reset_height()?;
}
Ok(())
}
pub(crate) fn compute_rest(
&mut self,
max_from: Height,
exit: &Exit,
) -> Result<()> {
for type_vecs in self.0.values_mut() {
type_vecs.compute_rest(max_from, exit)?;
}
Ok(())
}
#[inline(always)]
pub(crate) fn push_height(&mut self, counts: &AddrTypeToActivityCounts) {
for (vecs, c) in self.0.values_mut().zip(counts.0.values()) {
vecs.push_height(c);
}
}
}
/// Storage for activity metrics (global + per type).
#[derive(Traversable)]
pub struct AddrActivityVecs<M: StorageMode = Rw> {
pub all: ActivityCountVecs<M>,
#[traversable(flatten)]
pub by_addr_type: AddrTypeToActivityCountVecs<M>,
}
impl AddrActivityVecs {
pub(crate) fn forced_import(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
cached_starts: &CachedWindowStarts,
) -> Result<Self> {
Ok(Self {
all: ActivityCountVecs::forced_import(db, name, version, indexes, cached_starts)?,
by_addr_type: AddrTypeToActivityCountVecs::forced_import(
db, name, version, indexes, cached_starts,
)?,
})
}
pub(crate) fn min_stateful_len(&self) -> usize {
self.all
.min_stateful_len()
.min(self.by_addr_type.min_stateful_len())
}
pub(crate) fn par_iter_height_mut(
&mut self,
) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
self.all
.par_iter_height_mut()
.chain(self.by_addr_type.par_iter_height_mut())
}
pub(crate) fn reset_height(&mut self) -> Result<()> {
self.all.reset_height()?;
self.by_addr_type.reset_height()?;
Ok(())
}
pub(crate) fn compute_rest(
&mut self,
max_from: Height,
exit: &Exit,
) -> Result<()> {
self.all.compute_rest(max_from, exit)?;
self.by_addr_type.compute_rest(max_from, exit)?;
Ok(())
}
#[inline(always)]
pub(crate) fn push_height(&mut self, counts: &AddrTypeToActivityCounts) {
let totals = counts.totals();
self.all.push_height(&totals);
self.by_addr_type.push_height(counts);
}
}
@@ -0,0 +1,213 @@
use brk_cohort::ByAddrType;
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, Indexes, StoredU64, Version};
use derive_more::{Deref, DerefMut};
use rayon::prelude::*;
use vecdb::{
AnyStoredVec, AnyVec, Database, EagerVec, Exit, PcoVec, ReadableVec, Rw, StorageMode,
WritableVec,
};
use crate::{indexes, internal::PerBlock};
#[derive(Deref, DerefMut, Traversable)]
pub struct AddrCountVecs<M: StorageMode = Rw>(
#[traversable(flatten)] pub PerBlock<StoredU64, M>,
);
impl AddrCountVecs {
pub(crate) fn forced_import(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
Ok(Self(PerBlock::forced_import(
db, name, version, indexes,
)?))
}
}
/// Address count per address type (runtime state).
#[derive(Debug, Default, Deref, DerefMut)]
pub struct AddrTypeToAddrCount(ByAddrType<u64>);
impl AddrTypeToAddrCount {
#[inline]
pub(crate) fn sum(&self) -> u64 {
self.0.values().sum()
}
}
impl From<(&AddrTypeToAddrCountVecs, Height)> for AddrTypeToAddrCount {
#[inline]
fn from((groups, starting_height): (&AddrTypeToAddrCountVecs, Height)) -> Self {
if let Some(prev_height) = starting_height.decremented() {
Self(ByAddrType {
p2pk65: groups
.p2pk65
.height
.collect_one(prev_height)
.unwrap()
.into(),
p2pk33: groups
.p2pk33
.height
.collect_one(prev_height)
.unwrap()
.into(),
p2pkh: groups
.p2pkh
.height
.collect_one(prev_height)
.unwrap()
.into(),
p2sh: groups
.p2sh
.height
.collect_one(prev_height)
.unwrap()
.into(),
p2wpkh: groups
.p2wpkh
.height
.collect_one(prev_height)
.unwrap()
.into(),
p2wsh: groups
.p2wsh
.height
.collect_one(prev_height)
.unwrap()
.into(),
p2tr: groups
.p2tr
.height
.collect_one(prev_height)
.unwrap()
.into(),
p2a: groups
.p2a
.height
.collect_one(prev_height)
.unwrap()
.into(),
})
} else {
Default::default()
}
}
}
/// Address count per address type, with height + derived indexes.
#[derive(Deref, DerefMut, Traversable)]
pub struct AddrTypeToAddrCountVecs<M: StorageMode = Rw>(ByAddrType<AddrCountVecs<M>>);
impl From<ByAddrType<AddrCountVecs>> for AddrTypeToAddrCountVecs {
#[inline]
fn from(value: ByAddrType<AddrCountVecs>) -> Self {
Self(value)
}
}
impl AddrTypeToAddrCountVecs {
pub(crate) fn forced_import(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
Ok(Self::from(ByAddrType::<AddrCountVecs>::new_with_name(
|type_name| {
AddrCountVecs::forced_import(db, &format!("{type_name}_{name}"), version, indexes)
},
)?))
}
pub(crate) fn min_stateful_len(&self) -> usize {
self.0.values().map(|v| v.height.len()).min().unwrap()
}
pub(crate) fn par_iter_height_mut(
&mut self,
) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
self.0
.par_values_mut()
.map(|v| &mut v.height as &mut dyn AnyStoredVec)
}
#[inline(always)]
pub(crate) fn push_height(&mut self, addr_counts: &AddrTypeToAddrCount) {
for (vecs, &count) in self.0.values_mut().zip(addr_counts.values()) {
vecs.height.push(count.into());
}
}
pub(crate) fn reset_height(&mut self) -> Result<()> {
for v in self.0.values_mut() {
v.height.reset()?;
}
Ok(())
}
pub(crate) fn by_height(&self) -> Vec<&EagerVec<PcoVec<Height, StoredU64>>> {
self.0.values().map(|v| &v.height).collect()
}
}
#[derive(Traversable)]
pub struct AddrCountsVecs<M: StorageMode = Rw> {
pub all: AddrCountVecs<M>,
#[traversable(flatten)]
pub by_addr_type: AddrTypeToAddrCountVecs<M>,
}
impl AddrCountsVecs {
pub(crate) fn forced_import(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
Ok(Self {
all: AddrCountVecs::forced_import(db, name, version, indexes)?,
by_addr_type: AddrTypeToAddrCountVecs::forced_import(db, name, version, indexes)?,
})
}
pub(crate) fn min_stateful_len(&self) -> usize {
self.all.height.len().min(self.by_addr_type.min_stateful_len())
}
pub(crate) fn par_iter_height_mut(
&mut self,
) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
rayon::iter::once(&mut self.all.height as &mut dyn AnyStoredVec)
.chain(self.by_addr_type.par_iter_height_mut())
}
pub(crate) fn reset_height(&mut self) -> Result<()> {
self.all.height.reset()?;
self.by_addr_type.reset_height()?;
Ok(())
}
#[inline(always)]
pub(crate) fn push_height(&mut self, total: u64, addr_counts: &AddrTypeToAddrCount) {
self.all.height.push(total.into());
self.by_addr_type.push_height(addr_counts);
}
pub(crate) fn compute_rest(
&mut self,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
let sources = self.by_addr_type.by_height();
self.all
.height
.compute_sum_of_others(starting_indexes.height, &sources, exit)?;
Ok(())
}
}
@@ -0,0 +1,47 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{
EmptyAddrData, EmptyAddrIndex, FundedAddrData, FundedAddrIndex, Height,
};
use rayon::prelude::*;
use vecdb::{AnyStoredVec, BytesVec, Rw, Stamp, StorageMode, WritableVec};
/// Storage for both funded and empty address data.
#[derive(Traversable)]
pub struct AddrsDataVecs<M: StorageMode = Rw> {
pub funded: M::Stored<BytesVec<FundedAddrIndex, FundedAddrData>>,
pub empty: M::Stored<BytesVec<EmptyAddrIndex, EmptyAddrData>>,
}
impl AddrsDataVecs {
/// Get minimum stamped height across funded and empty data.
pub(crate) fn min_stamped_len(&self) -> Height {
Height::from(self.funded.stamp())
.incremented()
.min(Height::from(self.empty.stamp()).incremented())
}
/// Rollback both funded and empty data to before the given stamp.
pub(crate) fn rollback_before(&mut self, stamp: Stamp) -> Result<[Stamp; 2]> {
Ok([
self.funded.rollback_before(stamp)?,
self.empty.rollback_before(stamp)?,
])
}
/// Reset both funded and empty data.
pub(crate) fn reset(&mut self) -> Result<()> {
self.funded.reset()?;
self.empty.reset()?;
Ok(())
}
/// Returns a parallel iterator over all vecs for parallel writing.
pub(crate) fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
vec![
&mut self.funded as &mut dyn AnyStoredVec,
&mut self.empty as &mut dyn AnyStoredVec,
]
.into_par_iter()
}
}
@@ -0,0 +1,53 @@
use brk_cohort::ByAddrType;
use brk_traversable::Traversable;
use brk_types::{BasisPointsSigned32, StoredI64, StoredU64, Version};
use crate::{
indexes,
internal::{CachedWindowStarts, LazyRollingDeltasFromHeight},
};
use super::AddrCountsVecs;
type AddrDelta = LazyRollingDeltasFromHeight<StoredU64, StoredI64, BasisPointsSigned32>;
#[derive(Clone, Traversable)]
pub struct DeltaVecs {
pub all: AddrDelta,
#[traversable(flatten)]
pub by_addr_type: ByAddrType<AddrDelta>,
}
impl DeltaVecs {
pub(crate) fn new(
version: Version,
addr_count: &AddrCountsVecs,
cached_starts: &CachedWindowStarts,
indexes: &indexes::Vecs,
) -> Self {
let version = version + Version::TWO;
let all = LazyRollingDeltasFromHeight::new(
"addr_count",
version,
&addr_count.all.0.height,
cached_starts,
indexes,
);
let by_addr_type = addr_count.by_addr_type.map_with_name(|name, addr| {
LazyRollingDeltasFromHeight::new(
&format!("{name}_addr_count"),
version,
&addr.0.height,
cached_starts,
indexes,
)
});
Self {
all,
by_addr_type,
}
}
}
@@ -0,0 +1,198 @@
use std::thread;
use brk_cohort::ByAddrType;
use brk_error::{Error, Result};
use brk_traversable::Traversable;
use brk_types::{
AnyAddrIndex, Height, OutputType, P2AAddrIndex, P2PK33AddrIndex, P2PK65AddrIndex,
P2PKHAddrIndex, P2SHAddrIndex, P2TRAddrIndex, P2WPKHAddrIndex, P2WSHAddrIndex,
TypeIndex, Version,
};
use rayon::prelude::*;
use rustc_hash::FxHashMap;
use vecdb::{
AnyStoredVec, AnyVec, BytesVec, Database, ImportOptions, ImportableVec, ReadableVec, Reader,
Rw, Stamp, StorageMode, WritableVec,
};
use super::super::AddrTypeToTypeIndexMap;
const SAVED_STAMPED_CHANGES: u16 = 10;
/// Macro to define AnyAddrIndexesVecs and its methods.
macro_rules! define_any_addr_indexes_vecs {
($(($field:ident, $variant:ident, $index:ty)),* $(,)?) => {
#[derive(Traversable)]
pub struct AnyAddrIndexesVecs<M: StorageMode = Rw> {
$(pub $field: M::Stored<BytesVec<$index, AnyAddrIndex>>,)*
}
impl AnyAddrIndexesVecs {
/// Import from database.
pub(crate) fn forced_import(db: &Database, version: Version) -> Result<Self> {
Ok(Self {
$($field: BytesVec::forced_import_with(
ImportOptions::new(db, "any_addr_index", version)
.with_saved_stamped_changes(SAVED_STAMPED_CHANGES),
)?,)*
})
}
/// Get minimum stamped height across all address types.
pub(crate) fn min_stamped_len(&self) -> Height {
[$(Height::from(self.$field.stamp()).incremented()),*]
.into_iter()
.min()
.unwrap_or_default()
}
/// Rollback all address types to before the given stamp.
pub(crate) fn rollback_before(&mut self, stamp: Stamp) -> Result<Vec<Stamp>> {
Ok(vec![$(self.$field.rollback_before(stamp)?),*])
}
/// Reset all address types.
pub(crate) fn reset(&mut self) -> Result<()> {
$(self.$field.reset()?;)*
Ok(())
}
/// Get address index for a given type and type_index.
/// Uses get_any_or_read_at to check updated layer (needed after rollback).
pub(crate) fn get(&self, addr_type: OutputType, type_index: TypeIndex, reader: &Reader) -> Result<AnyAddrIndex> {
match addr_type {
$(OutputType::$variant => Ok(self.$field.get_any_or_read_at(type_index.into(), reader)?.unwrap()),)*
_ => unreachable!("Invalid addr type: {:?}", addr_type),
}
}
/// Returns a parallel iterator over all vecs for parallel writing.
pub(crate) fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
vec![$(&mut self.$field as &mut dyn AnyStoredVec),*].into_par_iter()
}
}
impl<M: StorageMode> AnyAddrIndexesVecs<M> {
/// Get address index with single read (no caching).
pub fn get_once(&self, addr_type: OutputType, type_index: TypeIndex) -> Result<AnyAddrIndex> {
match addr_type {
$(OutputType::$variant => self.$field
.collect_one(<$index>::from(usize::from(type_index)))
.ok_or_else(|| Error::UnsupportedType(addr_type.to_string())),)*
_ => Err(Error::UnsupportedType(addr_type.to_string())),
}
}
}
};
}
// Generate the struct and methods
define_any_addr_indexes_vecs!(
(p2a, P2A, P2AAddrIndex),
(p2pk33, P2PK33, P2PK33AddrIndex),
(p2pk65, P2PK65, P2PK65AddrIndex),
(p2pkh, P2PKH, P2PKHAddrIndex),
(p2sh, P2SH, P2SHAddrIndex),
(p2tr, P2TR, P2TRAddrIndex),
(p2wpkh, P2WPKH, P2WPKHAddrIndex),
(p2wsh, P2WSH, P2WSHAddrIndex),
);
impl AnyAddrIndexesVecs {
/// Process index updates in parallel by address type.
/// Accepts two maps (e.g. from empty and funded processing) and merges per-thread.
/// Updates existing entries and pushes new ones (sorted).
/// Returns (update_count, push_count).
pub(crate) fn par_batch_update(
&mut self,
updates1: AddrTypeToTypeIndexMap<AnyAddrIndex>,
updates2: AddrTypeToTypeIndexMap<AnyAddrIndex>,
) -> Result<(usize, usize)> {
let ByAddrType {
p2a: u1_p2a,
p2pk33: u1_p2pk33,
p2pk65: u1_p2pk65,
p2pkh: u1_p2pkh,
p2sh: u1_p2sh,
p2tr: u1_p2tr,
p2wpkh: u1_p2wpkh,
p2wsh: u1_p2wsh,
} = updates1.into_inner();
let ByAddrType {
p2a: u2_p2a,
p2pk33: u2_p2pk33,
p2pk65: u2_p2pk65,
p2pkh: u2_p2pkh,
p2sh: u2_p2sh,
p2tr: u2_p2tr,
p2wpkh: u2_p2wpkh,
p2wsh: u2_p2wsh,
} = updates2.into_inner();
let Self {
p2a,
p2pk33,
p2pk65,
p2pkh,
p2sh,
p2tr,
p2wpkh,
p2wsh,
} = self;
thread::scope(|s| {
let h_p2a = s.spawn(|| process_single_type_merged(p2a, u1_p2a, u2_p2a));
let h_p2pk33 = s.spawn(|| process_single_type_merged(p2pk33, u1_p2pk33, u2_p2pk33));
let h_p2pk65 = s.spawn(|| process_single_type_merged(p2pk65, u1_p2pk65, u2_p2pk65));
let h_p2pkh = s.spawn(|| process_single_type_merged(p2pkh, u1_p2pkh, u2_p2pkh));
let h_p2sh = s.spawn(|| process_single_type_merged(p2sh, u1_p2sh, u2_p2sh));
let h_p2tr = s.spawn(|| process_single_type_merged(p2tr, u1_p2tr, u2_p2tr));
let h_p2wpkh = s.spawn(|| process_single_type_merged(p2wpkh, u1_p2wpkh, u2_p2wpkh));
let h_p2wsh = s.spawn(|| process_single_type_merged(p2wsh, u1_p2wsh, u2_p2wsh));
let mut total_updates = 0usize;
let mut total_pushes = 0usize;
for h in [
h_p2a, h_p2pk33, h_p2pk65, h_p2pkh, h_p2sh, h_p2tr, h_p2wpkh, h_p2wsh,
] {
let (updates, pushes) = h.join().unwrap()?;
total_updates += updates;
total_pushes += pushes;
}
Ok((total_updates, total_pushes))
})
}
}
/// Process updates for a single address type's BytesVec, merging two maps.
fn process_single_type_merged<I: vecdb::VecIndex>(
vec: &mut BytesVec<I, AnyAddrIndex>,
map1: FxHashMap<TypeIndex, AnyAddrIndex>,
map2: FxHashMap<TypeIndex, AnyAddrIndex>,
) -> Result<(usize, usize)> {
let current_len = vec.len();
let mut pushes = Vec::with_capacity(map1.len() + map2.len());
let mut update_count = 0usize;
for (type_index, any_index) in map1.into_iter().chain(map2) {
if usize::from(type_index) < current_len {
vec.update(I::from(usize::from(type_index)), any_index)?;
update_count += 1;
} else {
pushes.push((type_index, any_index));
}
}
let push_count = pushes.len();
if !pushes.is_empty() {
pushes.sort_unstable_by_key(|(type_index, _)| *type_index);
for (_, any_index) in pushes {
vec.push(any_index);
}
}
Ok((update_count, push_count))
}
@@ -0,0 +1,3 @@
mod any;
pub use any::*;
@@ -0,0 +1,17 @@
mod activity;
mod addr_count;
mod data;
mod delta;
mod indexes;
mod new_addr_count;
mod total_addr_count;
mod type_map;
pub use activity::{AddrActivityVecs, AddrTypeToActivityCounts};
pub use addr_count::{AddrCountsVecs, AddrTypeToAddrCount};
pub use data::AddrsDataVecs;
pub use delta::DeltaVecs;
pub use indexes::AnyAddrIndexesVecs;
pub use new_addr_count::NewAddrCountVecs;
pub use total_addr_count::TotalAddrCountVecs;
pub use type_map::{AddrTypeToTypeIndexMap, AddrTypeToVec, HeightToAddrTypeToVec};
@@ -0,0 +1,75 @@
use brk_cohort::ByAddrType;
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, StoredU64, Version};
use vecdb::{Database, Exit, Rw, StorageMode};
use crate::{
indexes,
internal::{CachedWindowStarts, PerBlockCumulativeWithSums},
};
use super::TotalAddrCountVecs;
/// New address count per block (global + per-type)
#[derive(Traversable)]
pub struct NewAddrCountVecs<M: StorageMode = Rw> {
pub all: PerBlockCumulativeWithSums<StoredU64, StoredU64, M>,
#[traversable(flatten)]
pub by_addr_type: ByAddrType<PerBlockCumulativeWithSums<StoredU64, StoredU64, M>>,
}
impl NewAddrCountVecs {
pub(crate) fn forced_import(
db: &Database,
version: Version,
indexes: &indexes::Vecs,
cached_starts: &CachedWindowStarts,
) -> Result<Self> {
let all = PerBlockCumulativeWithSums::forced_import(
db,
"new_addr_count",
version,
indexes,
cached_starts,
)?;
let by_addr_type = ByAddrType::new_with_name(|name| {
PerBlockCumulativeWithSums::forced_import(
db,
&format!("{name}_new_addr_count"),
version,
indexes,
cached_starts,
)
})?;
Ok(Self {
all,
by_addr_type,
})
}
pub(crate) fn compute(
&mut self,
max_from: Height,
total_addr_count: &TotalAddrCountVecs,
exit: &Exit,
) -> Result<()> {
self.all.compute(max_from, exit, |height_vec| {
Ok(height_vec.compute_change(max_from, &total_addr_count.all.height, 1, exit)?)
})?;
for ((_, new), (_, total)) in self
.by_addr_type
.iter_mut()
.zip(total_addr_count.by_addr_type.iter())
{
new.compute(max_from, exit, |height_vec| {
Ok(height_vec.compute_change(max_from, &total.height, 1, exit)?)
})?;
}
Ok(())
}
}
@@ -0,0 +1,71 @@
use brk_cohort::ByAddrType;
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, StoredU64, Version};
use vecdb::{Database, Exit, Rw, StorageMode};
use crate::{indexes, internal::PerBlock};
use super::AddrCountsVecs;
/// Total address count (global + per-type) with all derived indexes
#[derive(Traversable)]
pub struct TotalAddrCountVecs<M: StorageMode = Rw> {
pub all: PerBlock<StoredU64, M>,
#[traversable(flatten)]
pub by_addr_type: ByAddrType<PerBlock<StoredU64, M>>,
}
impl TotalAddrCountVecs {
pub(crate) fn forced_import(
db: &Database,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
let all = PerBlock::forced_import(db, "total_addr_count", version, indexes)?;
let by_addr_type: ByAddrType<PerBlock<StoredU64>> =
ByAddrType::new_with_name(|name| {
PerBlock::forced_import(
db,
&format!("{name}_total_addr_count"),
version,
indexes,
)
})?;
Ok(Self {
all,
by_addr_type,
})
}
/// Eagerly compute total = addr_count + empty_addr_count.
pub(crate) fn compute(
&mut self,
max_from: Height,
addr_count: &AddrCountsVecs,
empty_addr_count: &AddrCountsVecs,
exit: &Exit,
) -> Result<()> {
self.all.height.compute_add(
max_from,
&addr_count.all.height,
&empty_addr_count.all.height,
exit,
)?;
for ((_, total), ((_, addr), (_, empty))) in self.by_addr_type.iter_mut().zip(
addr_count
.by_addr_type
.iter()
.zip(empty_addr_count.by_addr_type.iter()),
) {
total
.height
.compute_add(max_from, &addr.height, &empty.height, exit)?;
}
Ok(())
}
}
@@ -0,0 +1,26 @@
use brk_types::Height;
use derive_more::{Deref, DerefMut};
use rustc_hash::FxHashMap;
use super::vec::AddrTypeToVec;
/// Hashmap from Height to AddrTypeToVec.
#[derive(Debug, Default, Deref, DerefMut)]
pub struct HeightToAddrTypeToVec<T>(FxHashMap<Height, AddrTypeToVec<T>>);
impl<T> HeightToAddrTypeToVec<T> {
/// Create with pre-allocated capacity for unique heights.
pub(crate) fn with_capacity(capacity: usize) -> Self {
Self(FxHashMap::with_capacity_and_hasher(
capacity,
Default::default(),
))
}
}
impl<T> HeightToAddrTypeToVec<T> {
/// Consume and iterate over (Height, AddrTypeToVec) pairs.
pub(crate) fn into_iter(self) -> impl Iterator<Item = (Height, AddrTypeToVec<T>)> {
self.0.into_iter()
}
}
@@ -0,0 +1,116 @@
use std::{collections::hash_map::Entry, mem};
use brk_cohort::ByAddrType;
use brk_types::{OutputType, TypeIndex};
use derive_more::{Deref, DerefMut};
use rustc_hash::FxHashMap;
use smallvec::{Array, SmallVec};
/// A hashmap for each address type, keyed by TypeIndex.
#[derive(Debug, Deref, DerefMut)]
pub struct AddrTypeToTypeIndexMap<T>(ByAddrType<FxHashMap<TypeIndex, T>>);
impl<T> Default for AddrTypeToTypeIndexMap<T> {
fn default() -> Self {
Self(ByAddrType {
p2a: FxHashMap::default(),
p2pk33: FxHashMap::default(),
p2pk65: FxHashMap::default(),
p2pkh: FxHashMap::default(),
p2sh: FxHashMap::default(),
p2tr: FxHashMap::default(),
p2wpkh: FxHashMap::default(),
p2wsh: FxHashMap::default(),
})
}
}
impl<T> AddrTypeToTypeIndexMap<T> {
/// Create with pre-allocated capacity per address type.
pub(crate) fn with_capacity(capacity: usize) -> Self {
Self(ByAddrType {
p2a: FxHashMap::with_capacity_and_hasher(capacity, Default::default()),
p2pk33: FxHashMap::with_capacity_and_hasher(capacity, Default::default()),
p2pk65: FxHashMap::with_capacity_and_hasher(capacity, Default::default()),
p2pkh: FxHashMap::with_capacity_and_hasher(capacity, Default::default()),
p2sh: FxHashMap::with_capacity_and_hasher(capacity, Default::default()),
p2tr: FxHashMap::with_capacity_and_hasher(capacity, Default::default()),
p2wpkh: FxHashMap::with_capacity_and_hasher(capacity, Default::default()),
p2wsh: FxHashMap::with_capacity_and_hasher(capacity, Default::default()),
})
}
fn merge_single(own: &mut FxHashMap<TypeIndex, T>, other: &mut FxHashMap<TypeIndex, T>) {
if own.len() < other.len() {
mem::swap(own, other);
}
own.extend(other.drain());
}
/// Merge another map into self, consuming other.
pub(crate) fn merge_mut(&mut self, mut other: Self) {
Self::merge_single(&mut self.p2a, &mut other.p2a);
Self::merge_single(&mut self.p2pk33, &mut other.p2pk33);
Self::merge_single(&mut self.p2pk65, &mut other.p2pk65);
Self::merge_single(&mut self.p2pkh, &mut other.p2pkh);
Self::merge_single(&mut self.p2sh, &mut other.p2sh);
Self::merge_single(&mut self.p2tr, &mut other.p2tr);
Self::merge_single(&mut self.p2wpkh, &mut other.p2wpkh);
Self::merge_single(&mut self.p2wsh, &mut other.p2wsh);
}
/// Insert a value for a specific address type and type_index.
pub(crate) fn insert_for_type(
&mut self,
addr_type: OutputType,
type_index: TypeIndex,
value: T,
) {
self.get_mut(addr_type).unwrap().insert(type_index, value);
}
/// Consume and iterate over entries by address type.
#[allow(clippy::should_implement_trait)]
pub(crate) fn into_iter(self) -> impl Iterator<Item = (OutputType, FxHashMap<TypeIndex, T>)> {
self.0.into_iter()
}
/// Consume and return the inner ByAddrType.
pub(crate) fn into_inner(self) -> ByAddrType<FxHashMap<TypeIndex, T>> {
self.0
}
/// Iterate mutably over entries by address type.
pub(crate) fn iter_mut(
&mut self,
) -> impl Iterator<Item = (OutputType, &mut FxHashMap<TypeIndex, T>)> {
self.0.iter_mut()
}
}
impl<T> AddrTypeToTypeIndexMap<SmallVec<T>>
where
T: Array,
{
/// Merge two maps of SmallVec values, concatenating vectors.
pub(crate) fn merge_vec(mut self, other: Self) -> Self {
for (addr_type, other_map) in other.0.into_iter() {
let self_map = self.0.get_mut_unwrap(addr_type);
for (type_index, mut other_vec) in other_map {
match self_map.entry(type_index) {
Entry::Occupied(mut entry) => {
let self_vec = entry.get_mut();
if other_vec.len() > self_vec.len() {
mem::swap(self_vec, &mut other_vec);
}
self_vec.extend(other_vec);
}
Entry::Vacant(entry) => {
entry.insert(other_vec);
}
}
}
}
self
}
}
@@ -0,0 +1,7 @@
mod height_vec;
mod index_map;
mod vec;
pub use height_vec::*;
pub use index_map::*;
pub use vec::*;
@@ -0,0 +1,44 @@
use brk_cohort::ByAddrType;
use derive_more::{Deref, DerefMut};
/// A vector for each address type.
#[derive(Debug, Deref, DerefMut)]
pub struct AddrTypeToVec<T>(ByAddrType<Vec<T>>);
impl<T> Default for AddrTypeToVec<T> {
fn default() -> Self {
Self(ByAddrType {
p2a: vec![],
p2pk33: vec![],
p2pk65: vec![],
p2pkh: vec![],
p2sh: vec![],
p2tr: vec![],
p2wpkh: vec![],
p2wsh: vec![],
})
}
}
impl<T> AddrTypeToVec<T> {
/// Create with pre-allocated capacity per address type.
pub(crate) fn with_capacity(capacity: usize) -> Self {
Self(ByAddrType {
p2a: Vec::with_capacity(capacity),
p2pk33: Vec::with_capacity(capacity),
p2pk65: Vec::with_capacity(capacity),
p2pkh: Vec::with_capacity(capacity),
p2sh: Vec::with_capacity(capacity),
p2tr: Vec::with_capacity(capacity),
p2wpkh: Vec::with_capacity(capacity),
p2wsh: Vec::with_capacity(capacity),
})
}
}
impl<T> AddrTypeToVec<T> {
/// Unwrap the inner ByAddrType.
pub(crate) fn unwrap(self) -> ByAddrType<Vec<T>> {
self.0
}
}