mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-04 15:53:40 -07:00
137 lines
4.2 KiB
Rust
137 lines
4.2 KiB
Rust
use brk_cohort::ByAddrType;
|
|
use brk_error::Result;
|
|
use brk_types::{
|
|
AnyAddrDataIndexEnum, EmptyAddrData, FundedAddrData, OutputType, TxIndex, TypeIndex,
|
|
};
|
|
use smallvec::SmallVec;
|
|
|
|
use crate::distribution::{
|
|
addr::{AddrTypeToTypeIndexMap, AddrsDataVecs, AnyAddrIndexesVecs},
|
|
compute::VecsReaders,
|
|
};
|
|
|
|
use super::super::cohort::{WithAddrDataSource, update_tx_counts};
|
|
use super::lookup::AddrLookup;
|
|
|
|
/// Cache for address data within a flush interval.
|
|
pub struct AddrCache {
|
|
/// Addrs with non-zero balance
|
|
funded: AddrTypeToTypeIndexMap<WithAddrDataSource<FundedAddrData>>,
|
|
/// Addrs that became empty (zero balance)
|
|
empty: AddrTypeToTypeIndexMap<WithAddrDataSource<EmptyAddrData>>,
|
|
}
|
|
|
|
impl Default for AddrCache {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl AddrCache {
|
|
pub(crate) fn new() -> Self {
|
|
Self {
|
|
funded: AddrTypeToTypeIndexMap::default(),
|
|
empty: AddrTypeToTypeIndexMap::default(),
|
|
}
|
|
}
|
|
|
|
/// Check if address is in cache (either funded or empty).
|
|
#[inline]
|
|
pub(crate) fn contains(&self, addr_type: OutputType, type_index: TypeIndex) -> bool {
|
|
self.funded
|
|
.get(addr_type)
|
|
.is_some_and(|m| m.contains_key(&type_index))
|
|
|| self
|
|
.empty
|
|
.get(addr_type)
|
|
.is_some_and(|m| m.contains_key(&type_index))
|
|
}
|
|
|
|
/// Merge address data into funded cache.
|
|
#[inline]
|
|
pub(crate) fn merge_funded(
|
|
&mut self,
|
|
data: AddrTypeToTypeIndexMap<WithAddrDataSource<FundedAddrData>>,
|
|
) {
|
|
self.funded.merge_mut(data);
|
|
}
|
|
|
|
/// Create an AddrLookup view into this cache.
|
|
#[inline]
|
|
pub(crate) fn as_lookup(&mut self) -> AddrLookup<'_> {
|
|
AddrLookup {
|
|
funded: &mut self.funded,
|
|
empty: &mut self.empty,
|
|
}
|
|
}
|
|
|
|
/// Update transaction counts for addresses.
|
|
pub(crate) fn update_tx_counts(
|
|
&mut self,
|
|
tx_index_vecs: AddrTypeToTypeIndexMap<SmallVec<[TxIndex; 4]>>,
|
|
) {
|
|
update_tx_counts(&mut self.funded, &mut self.empty, tx_index_vecs);
|
|
}
|
|
|
|
/// Take the cache contents for flushing, leaving empty caches.
|
|
pub(crate) fn take(
|
|
&mut self,
|
|
) -> (
|
|
AddrTypeToTypeIndexMap<WithAddrDataSource<EmptyAddrData>>,
|
|
AddrTypeToTypeIndexMap<WithAddrDataSource<FundedAddrData>>,
|
|
) {
|
|
(
|
|
std::mem::take(&mut self.empty),
|
|
std::mem::take(&mut self.funded),
|
|
)
|
|
}
|
|
}
|
|
|
|
/// Load address data from storage or create new.
|
|
///
|
|
/// Returns None if address is already in cache (funded or empty).
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub(crate) fn load_uncached_addr_data(
|
|
addr_type: OutputType,
|
|
type_index: TypeIndex,
|
|
first_addr_indexes: &ByAddrType<TypeIndex>,
|
|
cache: &AddrCache,
|
|
vr: &VecsReaders,
|
|
any_addr_indexes: &AnyAddrIndexesVecs,
|
|
addrs_data: &AddrsDataVecs,
|
|
) -> Result<Option<WithAddrDataSource<FundedAddrData>>> {
|
|
// Check if this is a new address (type_index >= first for this height)
|
|
let first = *first_addr_indexes.get(addr_type).unwrap();
|
|
if first <= type_index {
|
|
return Ok(Some(WithAddrDataSource::New(FundedAddrData::default())));
|
|
}
|
|
|
|
// Skip if already in cache
|
|
if cache.contains(addr_type, type_index) {
|
|
return Ok(None);
|
|
}
|
|
|
|
// Read from storage
|
|
let reader = vr.addr_reader(addr_type);
|
|
let any_addr_index = any_addr_indexes.get(addr_type, type_index, reader)?;
|
|
|
|
Ok(Some(match any_addr_index.to_enum() {
|
|
AnyAddrDataIndexEnum::Funded(funded_index) => {
|
|
let reader = &vr.any_addr_index_to_any_addr_data.funded;
|
|
let funded_data = addrs_data
|
|
.funded
|
|
.get_any_or_read_at(funded_index.into(), reader)?
|
|
.unwrap();
|
|
WithAddrDataSource::FromFunded(funded_index, funded_data)
|
|
}
|
|
AnyAddrDataIndexEnum::Empty(empty_index) => {
|
|
let reader = &vr.any_addr_index_to_any_addr_data.empty;
|
|
let empty_data = addrs_data
|
|
.empty
|
|
.get_any_or_read_at(empty_index.into(), reader)?
|
|
.unwrap();
|
|
WithAddrDataSource::FromEmpty(empty_index, empty_data.into())
|
|
}
|
|
}))
|
|
}
|