mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-13 20:18:12 -07:00
global: address -> addr rename
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user