mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-22 00:18:10 -07:00
global: address -> addr rename
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
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())
|
||||
}
|
||||
}))
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
use brk_cohort::ByAddressType;
|
||||
use brk_error::Result;
|
||||
use brk_types::{
|
||||
AnyAddressDataIndexEnum, EmptyAddressData, FundedAddressData, OutputType, TxIndex, TypeIndex,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::distribution::{
|
||||
address::{AddressTypeToTypeIndexMap, AddressesDataVecs, AnyAddressIndexesVecs},
|
||||
compute::VecsReaders,
|
||||
};
|
||||
|
||||
use super::super::cohort::{WithAddressDataSource, update_tx_counts};
|
||||
use super::lookup::AddressLookup;
|
||||
|
||||
/// Cache for address data within a flush interval.
|
||||
pub struct AddressCache {
|
||||
/// Addresses with non-zero balance
|
||||
funded: AddressTypeToTypeIndexMap<WithAddressDataSource<FundedAddressData>>,
|
||||
/// Addresses that became empty (zero balance)
|
||||
empty: AddressTypeToTypeIndexMap<WithAddressDataSource<EmptyAddressData>>,
|
||||
}
|
||||
|
||||
impl Default for AddressCache {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl AddressCache {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
funded: AddressTypeToTypeIndexMap::default(),
|
||||
empty: AddressTypeToTypeIndexMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if address is in cache (either funded or empty).
|
||||
#[inline]
|
||||
pub(crate) fn contains(&self, address_type: OutputType, type_index: TypeIndex) -> bool {
|
||||
self.funded
|
||||
.get(address_type)
|
||||
.is_some_and(|m| m.contains_key(&type_index))
|
||||
|| self
|
||||
.empty
|
||||
.get(address_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: AddressTypeToTypeIndexMap<WithAddressDataSource<FundedAddressData>>,
|
||||
) {
|
||||
self.funded.merge_mut(data);
|
||||
}
|
||||
|
||||
/// Create an AddressLookup view into this cache.
|
||||
#[inline]
|
||||
pub(crate) fn as_lookup(&mut self) -> AddressLookup<'_> {
|
||||
AddressLookup {
|
||||
funded: &mut self.funded,
|
||||
empty: &mut self.empty,
|
||||
}
|
||||
}
|
||||
|
||||
/// Update transaction counts for addresses.
|
||||
pub(crate) fn update_tx_counts(
|
||||
&mut self,
|
||||
tx_index_vecs: AddressTypeToTypeIndexMap<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,
|
||||
) -> (
|
||||
AddressTypeToTypeIndexMap<WithAddressDataSource<EmptyAddressData>>,
|
||||
AddressTypeToTypeIndexMap<WithAddressDataSource<FundedAddressData>>,
|
||||
) {
|
||||
(
|
||||
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_address_data(
|
||||
address_type: OutputType,
|
||||
type_index: TypeIndex,
|
||||
first_address_indexes: &ByAddressType<TypeIndex>,
|
||||
cache: &AddressCache,
|
||||
vr: &VecsReaders,
|
||||
any_address_indexes: &AnyAddressIndexesVecs,
|
||||
addresses_data: &AddressesDataVecs,
|
||||
) -> Result<Option<WithAddressDataSource<FundedAddressData>>> {
|
||||
// Check if this is a new address (type_index >= first for this height)
|
||||
let first = *first_address_indexes.get(address_type).unwrap();
|
||||
if first <= type_index {
|
||||
return Ok(Some(WithAddressDataSource::New(
|
||||
FundedAddressData::default(),
|
||||
)));
|
||||
}
|
||||
|
||||
// Skip if already in cache
|
||||
if cache.contains(address_type, type_index) {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// Read from storage
|
||||
let reader = vr.address_reader(address_type);
|
||||
let any_address_index = any_address_indexes.get(address_type, type_index, reader)?;
|
||||
|
||||
Ok(Some(match any_address_index.to_enum() {
|
||||
AnyAddressDataIndexEnum::Funded(funded_index) => {
|
||||
let reader = &vr.any_address_index_to_any_address_data.funded;
|
||||
let funded_data = addresses_data
|
||||
.funded
|
||||
.get_any_or_read_at(funded_index.into(), reader)?
|
||||
.unwrap();
|
||||
WithAddressDataSource::FromFunded(funded_index, funded_data)
|
||||
}
|
||||
AnyAddressDataIndexEnum::Empty(empty_index) => {
|
||||
let reader = &vr.any_address_index_to_any_address_data.empty;
|
||||
let empty_data = addresses_data
|
||||
.empty
|
||||
.get_any_or_read_at(empty_index.into(), reader)?
|
||||
.unwrap();
|
||||
WithAddressDataSource::FromEmpty(empty_index, empty_data.into())
|
||||
}
|
||||
}))
|
||||
}
|
||||
+15
-15
@@ -1,8 +1,8 @@
|
||||
use brk_types::{EmptyAddressData, FundedAddressData, OutputType, TypeIndex};
|
||||
use brk_types::{EmptyAddrData, FundedAddrData, OutputType, TypeIndex};
|
||||
|
||||
use crate::distribution::address::AddressTypeToTypeIndexMap;
|
||||
use crate::distribution::addr::AddrTypeToTypeIndexMap;
|
||||
|
||||
use super::super::cohort::WithAddressDataSource;
|
||||
use super::super::cohort::WithAddrDataSource;
|
||||
|
||||
/// Tracking status of an address - determines cohort update strategy.
|
||||
#[derive(Clone, Copy)]
|
||||
@@ -16,18 +16,18 @@ pub enum TrackingStatus {
|
||||
}
|
||||
|
||||
/// Context for looking up and storing address data during block processing.
|
||||
pub struct AddressLookup<'a> {
|
||||
pub funded: &'a mut AddressTypeToTypeIndexMap<WithAddressDataSource<FundedAddressData>>,
|
||||
pub empty: &'a mut AddressTypeToTypeIndexMap<WithAddressDataSource<EmptyAddressData>>,
|
||||
pub struct AddrLookup<'a> {
|
||||
pub funded: &'a mut AddrTypeToTypeIndexMap<WithAddrDataSource<FundedAddrData>>,
|
||||
pub empty: &'a mut AddrTypeToTypeIndexMap<WithAddrDataSource<EmptyAddrData>>,
|
||||
}
|
||||
|
||||
impl<'a> AddressLookup<'a> {
|
||||
impl<'a> AddrLookup<'a> {
|
||||
pub(crate) fn get_or_create_for_receive(
|
||||
&mut self,
|
||||
output_type: OutputType,
|
||||
type_index: TypeIndex,
|
||||
) -> (
|
||||
&mut WithAddressDataSource<FundedAddressData>,
|
||||
&mut WithAddrDataSource<FundedAddrData>,
|
||||
TrackingStatus,
|
||||
) {
|
||||
use std::collections::hash_map::Entry;
|
||||
@@ -36,7 +36,7 @@ impl<'a> AddressLookup<'a> {
|
||||
|
||||
match map.entry(type_index) {
|
||||
Entry::Occupied(entry) => {
|
||||
// Address is in cache. Need to determine if it's been processed
|
||||
// Addr is in cache. Need to determine if it's been processed
|
||||
// by process_received (added to a cohort) or just funded this block.
|
||||
//
|
||||
// - If wrapper is New AND funded_txo_count == 0: hasn't received yet,
|
||||
@@ -47,15 +47,15 @@ impl<'a> AddressLookup<'a> {
|
||||
// - If wrapper is FromEmpty AND utxo_count == 0: still empty → WasEmpty
|
||||
// - If wrapper is FromEmpty AND utxo_count > 0: already received → Tracked
|
||||
let status = match entry.get() {
|
||||
WithAddressDataSource::New(data) => {
|
||||
WithAddrDataSource::New(data) => {
|
||||
if data.funded_txo_count == 0 {
|
||||
TrackingStatus::New
|
||||
} else {
|
||||
TrackingStatus::Tracked
|
||||
}
|
||||
}
|
||||
WithAddressDataSource::FromFunded(..) => TrackingStatus::Tracked,
|
||||
WithAddressDataSource::FromEmpty(_, data) => {
|
||||
WithAddrDataSource::FromFunded(..) => TrackingStatus::Tracked,
|
||||
WithAddrDataSource::FromEmpty(_, data) => {
|
||||
if data.utxo_count() == 0 {
|
||||
TrackingStatus::WasEmpty
|
||||
} else {
|
||||
@@ -72,7 +72,7 @@ impl<'a> AddressLookup<'a> {
|
||||
return (entry.insert(empty_data.into()), TrackingStatus::WasEmpty);
|
||||
}
|
||||
(
|
||||
entry.insert(WithAddressDataSource::New(FundedAddressData::default())),
|
||||
entry.insert(WithAddrDataSource::New(FundedAddrData::default())),
|
||||
TrackingStatus::New,
|
||||
)
|
||||
}
|
||||
@@ -84,12 +84,12 @@ impl<'a> AddressLookup<'a> {
|
||||
&mut self,
|
||||
output_type: OutputType,
|
||||
type_index: TypeIndex,
|
||||
) -> &mut WithAddressDataSource<FundedAddressData> {
|
||||
) -> &mut WithAddrDataSource<FundedAddrData> {
|
||||
self.funded
|
||||
.get_mut(output_type)
|
||||
.unwrap()
|
||||
.get_mut(&type_index)
|
||||
.expect("Address must exist for send")
|
||||
.expect("Addr must exist for send")
|
||||
}
|
||||
|
||||
/// Move address from funded to empty set.
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
mod address;
|
||||
mod addr;
|
||||
mod lookup;
|
||||
|
||||
pub use address::*;
|
||||
pub use addr::*;
|
||||
pub use lookup::*;
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
use brk_error::Result;
|
||||
use brk_types::{
|
||||
AnyAddrIndex, EmptyAddrData, EmptyAddrIndex, FundedAddrData, FundedAddrIndex,
|
||||
OutputType, TypeIndex,
|
||||
};
|
||||
use vecdb::AnyVec;
|
||||
|
||||
use crate::distribution::{AddrTypeToTypeIndexMap, AddrsDataVecs};
|
||||
|
||||
use super::with_source::WithAddrDataSource;
|
||||
|
||||
/// Process funded address data updates.
|
||||
///
|
||||
/// Handles:
|
||||
/// - New funded address: push to funded storage
|
||||
/// - Updated funded address (was funded): update in place
|
||||
/// - Transition empty -> funded: delete from empty, push to funded
|
||||
pub(crate) fn process_funded_addrs(
|
||||
addrs_data: &mut AddrsDataVecs,
|
||||
funded_updates: AddrTypeToTypeIndexMap<WithAddrDataSource<FundedAddrData>>,
|
||||
) -> Result<AddrTypeToTypeIndexMap<AnyAddrIndex>> {
|
||||
let total: usize = funded_updates.iter().map(|(_, m)| m.len()).sum();
|
||||
|
||||
let mut updates: Vec<(FundedAddrIndex, FundedAddrData)> = Vec::with_capacity(total);
|
||||
let mut deletes: Vec<EmptyAddrIndex> = Vec::with_capacity(total);
|
||||
let mut pushes: Vec<(OutputType, TypeIndex, FundedAddrData)> = Vec::with_capacity(total);
|
||||
|
||||
for (addr_type, items) in funded_updates.into_iter() {
|
||||
for (type_index, source) in items {
|
||||
match source {
|
||||
WithAddrDataSource::New(data) => {
|
||||
pushes.push((addr_type, type_index, data));
|
||||
}
|
||||
WithAddrDataSource::FromFunded(index, data) => {
|
||||
updates.push((index, data));
|
||||
}
|
||||
WithAddrDataSource::FromEmpty(empty_index, data) => {
|
||||
deletes.push(empty_index);
|
||||
pushes.push((addr_type, type_index, data));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 1: Deletes (creates holes)
|
||||
for empty_index in deletes {
|
||||
addrs_data.empty.delete(empty_index);
|
||||
}
|
||||
|
||||
// Phase 2: Updates (in-place)
|
||||
for (index, data) in updates {
|
||||
addrs_data.funded.update(index, data)?;
|
||||
}
|
||||
|
||||
// Phase 3: Pushes (fill holes first, then pure pushes)
|
||||
let mut result = AddrTypeToTypeIndexMap::with_capacity(pushes.len() / 4);
|
||||
let holes_count = addrs_data.funded.holes().len();
|
||||
let mut pushes_iter = pushes.into_iter();
|
||||
|
||||
for (addr_type, type_index, data) in pushes_iter.by_ref().take(holes_count) {
|
||||
let index = addrs_data.funded.fill_first_hole_or_push(data)?;
|
||||
result
|
||||
.get_mut(addr_type)
|
||||
.unwrap()
|
||||
.insert(type_index, AnyAddrIndex::from(index));
|
||||
}
|
||||
|
||||
// Pure pushes - no holes remain
|
||||
addrs_data.funded.reserve_pushed(pushes_iter.len());
|
||||
let mut next_index = addrs_data.funded.len();
|
||||
for (addr_type, type_index, data) in pushes_iter {
|
||||
addrs_data.funded.push(data);
|
||||
result.get_mut(addr_type).unwrap().insert(
|
||||
type_index,
|
||||
AnyAddrIndex::from(FundedAddrIndex::from(next_index)),
|
||||
);
|
||||
next_index += 1;
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Process empty address data updates.
|
||||
///
|
||||
/// Handles:
|
||||
/// - New empty address: push to empty storage
|
||||
/// - Updated empty address (was empty): update in place
|
||||
/// - Transition funded -> empty: delete from funded, push to empty
|
||||
pub(crate) fn process_empty_addrs(
|
||||
addrs_data: &mut AddrsDataVecs,
|
||||
empty_updates: AddrTypeToTypeIndexMap<WithAddrDataSource<EmptyAddrData>>,
|
||||
) -> Result<AddrTypeToTypeIndexMap<AnyAddrIndex>> {
|
||||
let total: usize = empty_updates.iter().map(|(_, m)| m.len()).sum();
|
||||
|
||||
let mut updates: Vec<(EmptyAddrIndex, EmptyAddrData)> = Vec::with_capacity(total);
|
||||
let mut deletes: Vec<FundedAddrIndex> = Vec::with_capacity(total);
|
||||
let mut pushes: Vec<(OutputType, TypeIndex, EmptyAddrData)> = Vec::with_capacity(total);
|
||||
|
||||
for (addr_type, items) in empty_updates.into_iter() {
|
||||
for (type_index, source) in items {
|
||||
match source {
|
||||
WithAddrDataSource::New(data) => {
|
||||
pushes.push((addr_type, type_index, data));
|
||||
}
|
||||
WithAddrDataSource::FromEmpty(index, data) => {
|
||||
updates.push((index, data));
|
||||
}
|
||||
WithAddrDataSource::FromFunded(funded_index, data) => {
|
||||
deletes.push(funded_index);
|
||||
pushes.push((addr_type, type_index, data));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 1: Deletes (creates holes)
|
||||
for funded_index in deletes {
|
||||
addrs_data.funded.delete(funded_index);
|
||||
}
|
||||
|
||||
// Phase 2: Updates (in-place)
|
||||
for (index, data) in updates {
|
||||
addrs_data.empty.update(index, data)?;
|
||||
}
|
||||
|
||||
// Phase 3: Pushes (fill holes first, then pure pushes)
|
||||
let mut result = AddrTypeToTypeIndexMap::with_capacity(pushes.len() / 4);
|
||||
let holes_count = addrs_data.empty.holes().len();
|
||||
let mut pushes_iter = pushes.into_iter();
|
||||
|
||||
for (addr_type, type_index, data) in pushes_iter.by_ref().take(holes_count) {
|
||||
let index = addrs_data.empty.fill_first_hole_or_push(data)?;
|
||||
result
|
||||
.get_mut(addr_type)
|
||||
.unwrap()
|
||||
.insert(type_index, AnyAddrIndex::from(index));
|
||||
}
|
||||
|
||||
// Pure pushes - no holes remain
|
||||
addrs_data.empty.reserve_pushed(pushes_iter.len());
|
||||
let mut next_index = addrs_data.empty.len();
|
||||
for (addr_type, type_index, data) in pushes_iter {
|
||||
addrs_data.empty.push(data);
|
||||
result.get_mut(addr_type).unwrap().insert(
|
||||
type_index,
|
||||
AnyAddrIndex::from(EmptyAddrIndex::from(next_index)),
|
||||
);
|
||||
next_index += 1;
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
use brk_error::Result;
|
||||
use brk_types::{
|
||||
AnyAddressIndex, EmptyAddressData, EmptyAddressIndex, FundedAddressData, FundedAddressIndex,
|
||||
OutputType, TypeIndex,
|
||||
};
|
||||
use vecdb::AnyVec;
|
||||
|
||||
use crate::distribution::{AddressTypeToTypeIndexMap, AddressesDataVecs};
|
||||
|
||||
use super::with_source::WithAddressDataSource;
|
||||
|
||||
/// Process funded address data updates.
|
||||
///
|
||||
/// Handles:
|
||||
/// - New funded address: push to funded storage
|
||||
/// - Updated funded address (was funded): update in place
|
||||
/// - Transition empty -> funded: delete from empty, push to funded
|
||||
pub(crate) fn process_funded_addresses(
|
||||
addresses_data: &mut AddressesDataVecs,
|
||||
funded_updates: AddressTypeToTypeIndexMap<WithAddressDataSource<FundedAddressData>>,
|
||||
) -> Result<AddressTypeToTypeIndexMap<AnyAddressIndex>> {
|
||||
let total: usize = funded_updates.iter().map(|(_, m)| m.len()).sum();
|
||||
|
||||
let mut updates: Vec<(FundedAddressIndex, FundedAddressData)> = Vec::with_capacity(total);
|
||||
let mut deletes: Vec<EmptyAddressIndex> = Vec::with_capacity(total);
|
||||
let mut pushes: Vec<(OutputType, TypeIndex, FundedAddressData)> = Vec::with_capacity(total);
|
||||
|
||||
for (address_type, items) in funded_updates.into_iter() {
|
||||
for (type_index, source) in items {
|
||||
match source {
|
||||
WithAddressDataSource::New(data) => {
|
||||
pushes.push((address_type, type_index, data));
|
||||
}
|
||||
WithAddressDataSource::FromFunded(index, data) => {
|
||||
updates.push((index, data));
|
||||
}
|
||||
WithAddressDataSource::FromEmpty(empty_index, data) => {
|
||||
deletes.push(empty_index);
|
||||
pushes.push((address_type, type_index, data));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 1: Deletes (creates holes)
|
||||
for empty_index in deletes {
|
||||
addresses_data.empty.delete(empty_index);
|
||||
}
|
||||
|
||||
// Phase 2: Updates (in-place)
|
||||
for (index, data) in updates {
|
||||
addresses_data.funded.update(index, data)?;
|
||||
}
|
||||
|
||||
// Phase 3: Pushes (fill holes first, then pure pushes)
|
||||
let mut result = AddressTypeToTypeIndexMap::with_capacity(pushes.len() / 4);
|
||||
let holes_count = addresses_data.funded.holes().len();
|
||||
let mut pushes_iter = pushes.into_iter();
|
||||
|
||||
for (address_type, type_index, data) in pushes_iter.by_ref().take(holes_count) {
|
||||
let index = addresses_data.funded.fill_first_hole_or_push(data)?;
|
||||
result
|
||||
.get_mut(address_type)
|
||||
.unwrap()
|
||||
.insert(type_index, AnyAddressIndex::from(index));
|
||||
}
|
||||
|
||||
// Pure pushes - no holes remain
|
||||
addresses_data.funded.reserve_pushed(pushes_iter.len());
|
||||
let mut next_index = addresses_data.funded.len();
|
||||
for (address_type, type_index, data) in pushes_iter {
|
||||
addresses_data.funded.push(data);
|
||||
result.get_mut(address_type).unwrap().insert(
|
||||
type_index,
|
||||
AnyAddressIndex::from(FundedAddressIndex::from(next_index)),
|
||||
);
|
||||
next_index += 1;
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Process empty address data updates.
|
||||
///
|
||||
/// Handles:
|
||||
/// - New empty address: push to empty storage
|
||||
/// - Updated empty address (was empty): update in place
|
||||
/// - Transition funded -> empty: delete from funded, push to empty
|
||||
pub(crate) fn process_empty_addresses(
|
||||
addresses_data: &mut AddressesDataVecs,
|
||||
empty_updates: AddressTypeToTypeIndexMap<WithAddressDataSource<EmptyAddressData>>,
|
||||
) -> Result<AddressTypeToTypeIndexMap<AnyAddressIndex>> {
|
||||
let total: usize = empty_updates.iter().map(|(_, m)| m.len()).sum();
|
||||
|
||||
let mut updates: Vec<(EmptyAddressIndex, EmptyAddressData)> = Vec::with_capacity(total);
|
||||
let mut deletes: Vec<FundedAddressIndex> = Vec::with_capacity(total);
|
||||
let mut pushes: Vec<(OutputType, TypeIndex, EmptyAddressData)> = Vec::with_capacity(total);
|
||||
|
||||
for (address_type, items) in empty_updates.into_iter() {
|
||||
for (type_index, source) in items {
|
||||
match source {
|
||||
WithAddressDataSource::New(data) => {
|
||||
pushes.push((address_type, type_index, data));
|
||||
}
|
||||
WithAddressDataSource::FromEmpty(index, data) => {
|
||||
updates.push((index, data));
|
||||
}
|
||||
WithAddressDataSource::FromFunded(funded_index, data) => {
|
||||
deletes.push(funded_index);
|
||||
pushes.push((address_type, type_index, data));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 1: Deletes (creates holes)
|
||||
for funded_index in deletes {
|
||||
addresses_data.funded.delete(funded_index);
|
||||
}
|
||||
|
||||
// Phase 2: Updates (in-place)
|
||||
for (index, data) in updates {
|
||||
addresses_data.empty.update(index, data)?;
|
||||
}
|
||||
|
||||
// Phase 3: Pushes (fill holes first, then pure pushes)
|
||||
let mut result = AddressTypeToTypeIndexMap::with_capacity(pushes.len() / 4);
|
||||
let holes_count = addresses_data.empty.holes().len();
|
||||
let mut pushes_iter = pushes.into_iter();
|
||||
|
||||
for (address_type, type_index, data) in pushes_iter.by_ref().take(holes_count) {
|
||||
let index = addresses_data.empty.fill_first_hole_or_push(data)?;
|
||||
result
|
||||
.get_mut(address_type)
|
||||
.unwrap()
|
||||
.insert(type_index, AnyAddressIndex::from(index));
|
||||
}
|
||||
|
||||
// Pure pushes - no holes remain
|
||||
addresses_data.empty.reserve_pushed(pushes_iter.len());
|
||||
let mut next_index = addresses_data.empty.len();
|
||||
for (address_type, type_index, data) in pushes_iter {
|
||||
addresses_data.empty.push(data);
|
||||
result.get_mut(address_type).unwrap().insert(
|
||||
type_index,
|
||||
AnyAddressIndex::from(EmptyAddressIndex::from(next_index)),
|
||||
);
|
||||
next_index += 1;
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
mod address_updates;
|
||||
mod addr_updates;
|
||||
mod received;
|
||||
mod sent;
|
||||
mod tx_counts;
|
||||
mod with_source;
|
||||
|
||||
pub(crate) use address_updates::*;
|
||||
pub(crate) use addr_updates::*;
|
||||
pub(crate) use received::*;
|
||||
pub(crate) use sent::*;
|
||||
pub(crate) use tx_counts::*;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use brk_cohort::{AmountBucket, ByAddressType};
|
||||
use brk_cohort::{AmountBucket, ByAddrType};
|
||||
use brk_types::{Cents, Sats, TypeIndex};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::distribution::{
|
||||
address::{AddressTypeToActivityCounts, AddressTypeToVec},
|
||||
cohorts::AddressCohorts,
|
||||
addr::{AddrTypeToActivityCounts, AddrTypeToVec},
|
||||
cohorts::AddrCohorts,
|
||||
};
|
||||
|
||||
use super::super::cache::{AddressLookup, TrackingStatus};
|
||||
use super::super::cache::{AddrLookup, TrackingStatus};
|
||||
|
||||
/// Aggregated receive data for a single address within a block.
|
||||
#[derive(Default)]
|
||||
@@ -18,13 +18,13 @@ struct AggregatedReceive {
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn process_received(
|
||||
received_data: AddressTypeToVec<(TypeIndex, Sats)>,
|
||||
cohorts: &mut AddressCohorts,
|
||||
lookup: &mut AddressLookup<'_>,
|
||||
received_data: AddrTypeToVec<(TypeIndex, Sats)>,
|
||||
cohorts: &mut AddrCohorts,
|
||||
lookup: &mut AddrLookup<'_>,
|
||||
price: Cents,
|
||||
address_count: &mut ByAddressType<u64>,
|
||||
empty_address_count: &mut ByAddressType<u64>,
|
||||
activity_counts: &mut AddressTypeToActivityCounts,
|
||||
addr_count: &mut ByAddrType<u64>,
|
||||
empty_addr_count: &mut ByAddrType<u64>,
|
||||
activity_counts: &mut AddrTypeToActivityCounts,
|
||||
) {
|
||||
let max_type_len = received_data.iter().map(|(_, v)| v.len()).max().unwrap_or(0);
|
||||
let mut aggregated: FxHashMap<TypeIndex, AggregatedReceive> =
|
||||
@@ -36,8 +36,8 @@ pub(crate) fn process_received(
|
||||
}
|
||||
|
||||
// Cache mutable refs for this address type
|
||||
let type_address_count = address_count.get_mut(output_type).unwrap();
|
||||
let type_empty_count = empty_address_count.get_mut(output_type).unwrap();
|
||||
let type_addr_count = addr_count.get_mut(output_type).unwrap();
|
||||
let type_empty_count = empty_addr_count.get_mut(output_type).unwrap();
|
||||
let type_activity = activity_counts.get_mut_unwrap(output_type);
|
||||
|
||||
// Aggregate receives by address - each address processed exactly once
|
||||
@@ -55,10 +55,10 @@ pub(crate) fn process_received(
|
||||
|
||||
match status {
|
||||
TrackingStatus::New => {
|
||||
*type_address_count += 1;
|
||||
*type_addr_count += 1;
|
||||
}
|
||||
TrackingStatus::WasEmpty => {
|
||||
*type_address_count += 1;
|
||||
*type_addr_count += 1;
|
||||
*type_empty_count -= 1;
|
||||
// Reactivated - was empty, now has funds
|
||||
type_activity.reactivated += 1;
|
||||
@@ -100,7 +100,7 @@ pub(crate) fn process_received(
|
||||
"process_received: cohort underflow detected!\n\
|
||||
output_type={:?}, type_index={:?}\n\
|
||||
prev_balance={}, new_balance={}, total_value={}\n\
|
||||
Address: {:?}",
|
||||
Addr: {:?}",
|
||||
output_type,
|
||||
type_index,
|
||||
prev_balance,
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
use brk_cohort::{AmountBucket, ByAddressType};
|
||||
use brk_cohort::{AmountBucket, ByAddrType};
|
||||
use brk_error::Result;
|
||||
use brk_types::{Age, Cents, CheckedSub, Height, Sats, Timestamp, TypeIndex};
|
||||
use rustc_hash::FxHashSet;
|
||||
use vecdb::VecIndex;
|
||||
|
||||
use crate::distribution::{
|
||||
address::{AddressTypeToActivityCounts, HeightToAddressTypeToVec},
|
||||
cohorts::AddressCohorts,
|
||||
addr::{AddrTypeToActivityCounts, HeightToAddrTypeToVec},
|
||||
cohorts::AddrCohorts,
|
||||
compute::PriceRangeMax,
|
||||
};
|
||||
|
||||
use super::super::cache::AddressLookup;
|
||||
use super::super::cache::AddrLookup;
|
||||
|
||||
/// Process sent outputs for address cohorts.
|
||||
///
|
||||
@@ -27,20 +27,20 @@ use super::super::cache::AddressLookup;
|
||||
/// for accurate peak regret calculation.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn process_sent(
|
||||
sent_data: HeightToAddressTypeToVec<(TypeIndex, Sats)>,
|
||||
cohorts: &mut AddressCohorts,
|
||||
lookup: &mut AddressLookup<'_>,
|
||||
sent_data: HeightToAddrTypeToVec<(TypeIndex, Sats)>,
|
||||
cohorts: &mut AddrCohorts,
|
||||
lookup: &mut AddrLookup<'_>,
|
||||
current_price: Cents,
|
||||
price_range_max: &PriceRangeMax,
|
||||
address_count: &mut ByAddressType<u64>,
|
||||
empty_address_count: &mut ByAddressType<u64>,
|
||||
activity_counts: &mut AddressTypeToActivityCounts,
|
||||
received_addresses: &ByAddressType<FxHashSet<TypeIndex>>,
|
||||
addr_count: &mut ByAddrType<u64>,
|
||||
empty_addr_count: &mut ByAddrType<u64>,
|
||||
activity_counts: &mut AddrTypeToActivityCounts,
|
||||
received_addrs: &ByAddrType<FxHashSet<TypeIndex>>,
|
||||
height_to_price: &[Cents],
|
||||
height_to_timestamp: &[Timestamp],
|
||||
current_height: Height,
|
||||
current_timestamp: Timestamp,
|
||||
seen_senders: &mut ByAddressType<FxHashSet<TypeIndex>>,
|
||||
seen_senders: &mut ByAddrType<FxHashSet<TypeIndex>>,
|
||||
) -> Result<()> {
|
||||
seen_senders.values_mut().for_each(|set| set.clear());
|
||||
|
||||
@@ -54,10 +54,10 @@ pub(crate) fn process_sent(
|
||||
|
||||
for (output_type, vec) in by_type.unwrap().into_iter() {
|
||||
// Cache mutable refs for this address type
|
||||
let type_address_count = address_count.get_mut(output_type).unwrap();
|
||||
let type_empty_count = empty_address_count.get_mut(output_type).unwrap();
|
||||
let type_addr_count = addr_count.get_mut(output_type).unwrap();
|
||||
let type_empty_count = empty_addr_count.get_mut(output_type).unwrap();
|
||||
let type_activity = activity_counts.get_mut_unwrap(output_type);
|
||||
let type_received = received_addresses.get(output_type);
|
||||
let type_received = received_addrs.get(output_type);
|
||||
let type_seen = seen_senders.get_mut_unwrap(output_type);
|
||||
|
||||
for (type_index, value) in vec {
|
||||
@@ -99,7 +99,7 @@ pub(crate) fn process_sent(
|
||||
|
||||
// Migrate address to new bucket or mark as empty
|
||||
if will_be_empty {
|
||||
*type_address_count -= 1;
|
||||
*type_addr_count -= 1;
|
||||
*type_empty_count += 1;
|
||||
lookup.move_to_empty(output_type, type_index);
|
||||
} else if crossing_boundary {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use brk_types::{EmptyAddressData, FundedAddressData, TxIndex};
|
||||
use brk_types::{EmptyAddrData, FundedAddrData, TxIndex};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::distribution::address::AddressTypeToTypeIndexMap;
|
||||
use crate::distribution::addr::AddrTypeToTypeIndexMap;
|
||||
|
||||
use super::with_source::WithAddressDataSource;
|
||||
use super::with_source::WithAddrDataSource;
|
||||
|
||||
/// Update tx_count for addresses based on unique transactions they participated in.
|
||||
///
|
||||
@@ -14,9 +14,9 @@ use super::with_source::WithAddressDataSource;
|
||||
/// Addresses are looked up in funded_cache first, then empty_cache.
|
||||
/// NOTE: This should be called AFTER merging parallel-fetched address data into funded_cache.
|
||||
pub(crate) fn update_tx_counts(
|
||||
funded_cache: &mut AddressTypeToTypeIndexMap<WithAddressDataSource<FundedAddressData>>,
|
||||
empty_cache: &mut AddressTypeToTypeIndexMap<WithAddressDataSource<EmptyAddressData>>,
|
||||
mut tx_index_vecs: AddressTypeToTypeIndexMap<SmallVec<[TxIndex; 4]>>,
|
||||
funded_cache: &mut AddrTypeToTypeIndexMap<WithAddrDataSource<FundedAddrData>>,
|
||||
empty_cache: &mut AddrTypeToTypeIndexMap<WithAddrDataSource<EmptyAddrData>>,
|
||||
mut tx_index_vecs: AddrTypeToTypeIndexMap<SmallVec<[TxIndex; 4]>>,
|
||||
) {
|
||||
// First, deduplicate tx_index_vecs for addresses that appear multiple times in a block
|
||||
for (_, map) in tx_index_vecs.iter_mut() {
|
||||
@@ -29,20 +29,20 @@ pub(crate) fn update_tx_counts(
|
||||
}
|
||||
|
||||
// Update tx_count on address data
|
||||
for (address_type, type_index, tx_index_vec) in tx_index_vecs
|
||||
for (addr_type, type_index, tx_index_vec) in tx_index_vecs
|
||||
.into_iter()
|
||||
.flat_map(|(t, m)| m.into_iter().map(move |(i, v)| (t, i, v)))
|
||||
{
|
||||
let tx_count = tx_index_vec.len() as u32;
|
||||
|
||||
if let Some(addr_data) = funded_cache
|
||||
.get_mut(address_type)
|
||||
.get_mut(addr_type)
|
||||
.unwrap()
|
||||
.get_mut(&type_index)
|
||||
{
|
||||
addr_data.tx_count += tx_count;
|
||||
} else if let Some(addr_data) = empty_cache
|
||||
.get_mut(address_type)
|
||||
.get_mut(addr_type)
|
||||
.unwrap()
|
||||
.get_mut(&type_index)
|
||||
{
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
use brk_types::{EmptyAddressData, EmptyAddressIndex, FundedAddressData, FundedAddressIndex};
|
||||
use brk_types::{EmptyAddrData, EmptyAddrIndex, FundedAddrData, FundedAddrIndex};
|
||||
|
||||
/// Address data wrapped with its source location for flush operations.
|
||||
///
|
||||
/// This enum tracks where the data came from so it can be correctly
|
||||
/// updated or created during the flush phase.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum WithAddressDataSource<T> {
|
||||
pub enum WithAddrDataSource<T> {
|
||||
/// Brand new address (never seen before)
|
||||
New(T),
|
||||
/// Funded from funded address storage (with original index)
|
||||
FromFunded(FundedAddressIndex, T),
|
||||
FromFunded(FundedAddrIndex, T),
|
||||
/// Funded from empty address storage (with original index)
|
||||
FromEmpty(EmptyAddressIndex, T),
|
||||
FromEmpty(EmptyAddrIndex, T),
|
||||
}
|
||||
|
||||
impl<T> std::ops::Deref for WithAddressDataSource<T> {
|
||||
impl<T> std::ops::Deref for WithAddrDataSource<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
@@ -24,7 +24,7 @@ impl<T> std::ops::Deref for WithAddressDataSource<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::DerefMut for WithAddressDataSource<T> {
|
||||
impl<T> std::ops::DerefMut for WithAddrDataSource<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
match self {
|
||||
Self::New(v) | Self::FromFunded(_, v) | Self::FromEmpty(_, v) => v,
|
||||
@@ -32,24 +32,24 @@ impl<T> std::ops::DerefMut for WithAddressDataSource<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WithAddressDataSource<EmptyAddressData>> for WithAddressDataSource<FundedAddressData> {
|
||||
impl From<WithAddrDataSource<EmptyAddrData>> for WithAddrDataSource<FundedAddrData> {
|
||||
#[inline]
|
||||
fn from(value: WithAddressDataSource<EmptyAddressData>) -> Self {
|
||||
fn from(value: WithAddrDataSource<EmptyAddrData>) -> Self {
|
||||
match value {
|
||||
WithAddressDataSource::New(v) => Self::New(v.into()),
|
||||
WithAddressDataSource::FromFunded(i, v) => Self::FromFunded(i, v.into()),
|
||||
WithAddressDataSource::FromEmpty(i, v) => Self::FromEmpty(i, v.into()),
|
||||
WithAddrDataSource::New(v) => Self::New(v.into()),
|
||||
WithAddrDataSource::FromFunded(i, v) => Self::FromFunded(i, v.into()),
|
||||
WithAddrDataSource::FromEmpty(i, v) => Self::FromEmpty(i, v.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WithAddressDataSource<FundedAddressData>> for WithAddressDataSource<EmptyAddressData> {
|
||||
impl From<WithAddrDataSource<FundedAddrData>> for WithAddrDataSource<EmptyAddrData> {
|
||||
#[inline]
|
||||
fn from(value: WithAddressDataSource<FundedAddressData>) -> Self {
|
||||
fn from(value: WithAddrDataSource<FundedAddrData>) -> Self {
|
||||
match value {
|
||||
WithAddressDataSource::New(v) => Self::New(v.into()),
|
||||
WithAddressDataSource::FromFunded(i, v) => Self::FromFunded(i, v.into()),
|
||||
WithAddressDataSource::FromEmpty(i, v) => Self::FromEmpty(i, v.into()),
|
||||
WithAddrDataSource::New(v) => Self::New(v.into()),
|
||||
WithAddrDataSource::FromFunded(i, v) => Self::FromFunded(i, v.into()),
|
||||
WithAddrDataSource::FromEmpty(i, v) => Self::FromEmpty(i, v.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
use brk_cohort::ByAddressType;
|
||||
use brk_cohort::ByAddrType;
|
||||
use brk_error::Result;
|
||||
use brk_types::{FundedAddressData, Height, OutputType, Sats, TxIndex, TypeIndex};
|
||||
use brk_types::{FundedAddrData, Height, OutputType, Sats, TxIndex, TypeIndex};
|
||||
use rayon::prelude::*;
|
||||
use rustc_hash::FxHashMap;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::distribution::{
|
||||
address::{AddressTypeToTypeIndexMap, AddressesDataVecs, AnyAddressIndexesVecs},
|
||||
addr::{AddrTypeToTypeIndexMap, AddrsDataVecs, AnyAddrIndexesVecs},
|
||||
compute::VecsReaders,
|
||||
state::Transacted,
|
||||
};
|
||||
|
||||
use crate::distribution::address::HeightToAddressTypeToVec;
|
||||
use crate::distribution::addr::HeightToAddrTypeToVec;
|
||||
|
||||
use super::super::{
|
||||
cache::{AddressCache, load_uncached_address_data},
|
||||
cohort::WithAddressDataSource,
|
||||
cache::{AddrCache, load_uncached_addr_data},
|
||||
cohort::WithAddrDataSource,
|
||||
};
|
||||
|
||||
/// Result of processing inputs for a block.
|
||||
@@ -23,11 +23,11 @@ pub struct InputsResult {
|
||||
/// Map from UTXO creation height -> aggregated sent supply.
|
||||
pub height_to_sent: FxHashMap<Height, Transacted>,
|
||||
/// Per-height, per-address-type sent data: (type_index, value) for each address.
|
||||
pub sent_data: HeightToAddressTypeToVec<(TypeIndex, Sats)>,
|
||||
/// Address data looked up during processing, keyed by (address_type, type_index).
|
||||
pub address_data: AddressTypeToTypeIndexMap<WithAddressDataSource<FundedAddressData>>,
|
||||
pub sent_data: HeightToAddrTypeToVec<(TypeIndex, Sats)>,
|
||||
/// Address data looked up during processing, keyed by (addr_type, type_index).
|
||||
pub addr_data: AddrTypeToTypeIndexMap<WithAddrDataSource<FundedAddrData>>,
|
||||
/// Transaction indexes per address for tx_count tracking.
|
||||
pub tx_index_vecs: AddressTypeToTypeIndexMap<SmallVec<[TxIndex; 4]>>,
|
||||
pub tx_index_vecs: AddrTypeToTypeIndexMap<SmallVec<[TxIndex; 4]>>,
|
||||
}
|
||||
|
||||
/// Process inputs (spent UTXOs) for a block.
|
||||
@@ -51,11 +51,11 @@ pub(crate) fn process_inputs(
|
||||
txin_index_to_output_type: &[OutputType],
|
||||
txin_index_to_type_index: &[TypeIndex],
|
||||
txin_index_to_prev_height: &[Height],
|
||||
first_address_indexes: &ByAddressType<TypeIndex>,
|
||||
cache: &AddressCache,
|
||||
first_addr_indexes: &ByAddrType<TypeIndex>,
|
||||
cache: &AddrCache,
|
||||
vr: &VecsReaders,
|
||||
any_address_indexes: &AnyAddressIndexesVecs,
|
||||
addresses_data: &AddressesDataVecs,
|
||||
any_addr_indexes: &AnyAddrIndexesVecs,
|
||||
addrs_data: &AddrsDataVecs,
|
||||
) -> Result<InputsResult> {
|
||||
let map_fn = |local_idx: usize| -> Result<_> {
|
||||
let tx_index = txin_index_to_tx_index[local_idx];
|
||||
@@ -64,21 +64,21 @@ pub(crate) fn process_inputs(
|
||||
let value = txin_index_to_value[local_idx];
|
||||
let input_type = txin_index_to_output_type[local_idx];
|
||||
|
||||
if input_type.is_not_address() {
|
||||
if input_type.is_not_addr() {
|
||||
return Ok((prev_height, value, input_type, None));
|
||||
}
|
||||
|
||||
let type_index = txin_index_to_type_index[local_idx];
|
||||
|
||||
// Look up address data
|
||||
let addr_data_opt = load_uncached_address_data(
|
||||
let addr_data_opt = load_uncached_addr_data(
|
||||
input_type,
|
||||
type_index,
|
||||
first_address_indexes,
|
||||
first_addr_indexes,
|
||||
cache,
|
||||
vr,
|
||||
any_address_indexes,
|
||||
addresses_data,
|
||||
any_addr_indexes,
|
||||
addrs_data,
|
||||
)?;
|
||||
|
||||
Ok((
|
||||
@@ -108,13 +108,13 @@ pub(crate) fn process_inputs(
|
||||
estimated_unique_heights,
|
||||
Default::default(),
|
||||
);
|
||||
let mut sent_data = HeightToAddressTypeToVec::with_capacity(estimated_unique_heights);
|
||||
let mut address_data =
|
||||
AddressTypeToTypeIndexMap::<WithAddressDataSource<FundedAddressData>>::with_capacity(
|
||||
let mut sent_data = HeightToAddrTypeToVec::with_capacity(estimated_unique_heights);
|
||||
let mut addr_data =
|
||||
AddrTypeToTypeIndexMap::<WithAddrDataSource<FundedAddrData>>::with_capacity(
|
||||
estimated_per_type,
|
||||
);
|
||||
let mut tx_index_vecs =
|
||||
AddressTypeToTypeIndexMap::<SmallVec<[TxIndex; 4]>>::with_capacity(estimated_per_type);
|
||||
AddrTypeToTypeIndexMap::<SmallVec<[TxIndex; 4]>>::with_capacity(estimated_per_type);
|
||||
|
||||
for (prev_height, value, output_type, addr_info) in items {
|
||||
height_to_sent
|
||||
@@ -130,8 +130,8 @@ pub(crate) fn process_inputs(
|
||||
.unwrap()
|
||||
.push((type_index, value));
|
||||
|
||||
if let Some(addr_data) = addr_data_opt {
|
||||
address_data.insert_for_type(output_type, type_index, addr_data);
|
||||
if let Some(source) = addr_data_opt {
|
||||
addr_data.insert_for_type(output_type, type_index, source);
|
||||
}
|
||||
|
||||
tx_index_vecs
|
||||
@@ -146,7 +146,7 @@ pub(crate) fn process_inputs(
|
||||
Ok(InputsResult {
|
||||
height_to_sent,
|
||||
sent_data,
|
||||
address_data,
|
||||
addr_data,
|
||||
tx_index_vecs,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
use brk_cohort::ByAddressType;
|
||||
use brk_cohort::ByAddrType;
|
||||
use brk_error::Result;
|
||||
use brk_types::{FundedAddressData, Sats, TxIndex, TypeIndex};
|
||||
use brk_types::{FundedAddrData, Sats, TxIndex, TypeIndex};
|
||||
use rayon::prelude::*;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::distribution::{
|
||||
address::{
|
||||
AddressTypeToTypeIndexMap, AddressTypeToVec, AddressesDataVecs, AnyAddressIndexesVecs,
|
||||
addr::{
|
||||
AddrTypeToTypeIndexMap, AddrTypeToVec, AddrsDataVecs, AnyAddrIndexesVecs,
|
||||
},
|
||||
compute::{TxOutData, VecsReaders},
|
||||
state::Transacted,
|
||||
};
|
||||
|
||||
use super::super::{
|
||||
cache::{AddressCache, load_uncached_address_data},
|
||||
cohort::WithAddressDataSource,
|
||||
cache::{AddrCache, load_uncached_addr_data},
|
||||
cohort::WithAddrDataSource,
|
||||
};
|
||||
|
||||
/// Result of processing outputs for a block.
|
||||
@@ -22,11 +22,11 @@ pub struct OutputsResult {
|
||||
/// Aggregated supply transacted in this block.
|
||||
pub transacted: Transacted,
|
||||
/// Per-address-type received data: (type_index, value) for each address.
|
||||
pub received_data: AddressTypeToVec<(TypeIndex, Sats)>,
|
||||
/// Address data looked up during processing, keyed by (address_type, type_index).
|
||||
pub address_data: AddressTypeToTypeIndexMap<WithAddressDataSource<FundedAddressData>>,
|
||||
pub received_data: AddrTypeToVec<(TypeIndex, Sats)>,
|
||||
/// Address data looked up during processing, keyed by (addr_type, type_index).
|
||||
pub addr_data: AddrTypeToTypeIndexMap<WithAddrDataSource<FundedAddrData>>,
|
||||
/// Transaction indexes per address for tx_count tracking.
|
||||
pub tx_index_vecs: AddressTypeToTypeIndexMap<SmallVec<[TxIndex; 4]>>,
|
||||
pub tx_index_vecs: AddrTypeToTypeIndexMap<SmallVec<[TxIndex; 4]>>,
|
||||
}
|
||||
|
||||
/// Process outputs (new UTXOs) for a block.
|
||||
@@ -40,35 +40,35 @@ pub struct OutputsResult {
|
||||
pub(crate) fn process_outputs(
|
||||
txout_index_to_tx_index: &[TxIndex],
|
||||
txout_data_vec: &[TxOutData],
|
||||
first_address_indexes: &ByAddressType<TypeIndex>,
|
||||
cache: &AddressCache,
|
||||
first_addr_indexes: &ByAddrType<TypeIndex>,
|
||||
cache: &AddrCache,
|
||||
vr: &VecsReaders,
|
||||
any_address_indexes: &AnyAddressIndexesVecs,
|
||||
addresses_data: &AddressesDataVecs,
|
||||
any_addr_indexes: &AnyAddrIndexesVecs,
|
||||
addrs_data: &AddrsDataVecs,
|
||||
) -> Result<OutputsResult> {
|
||||
let output_count = txout_data_vec.len();
|
||||
|
||||
// Phase 1: Address lookups (mmap reads) — parallel for large blocks, sequential for small
|
||||
// Phase 1: Addr lookups (mmap reads) — parallel for large blocks, sequential for small
|
||||
let map_fn = |local_idx: usize| -> Result<_> {
|
||||
let txout_data = &txout_data_vec[local_idx];
|
||||
let value = txout_data.value;
|
||||
let output_type = txout_data.output_type;
|
||||
|
||||
if output_type.is_not_address() {
|
||||
if output_type.is_not_addr() {
|
||||
return Ok((value, output_type, None));
|
||||
}
|
||||
|
||||
let type_index = txout_data.type_index;
|
||||
let tx_index = txout_index_to_tx_index[local_idx];
|
||||
|
||||
let addr_data_opt = load_uncached_address_data(
|
||||
let addr_data_opt = load_uncached_addr_data(
|
||||
output_type,
|
||||
type_index,
|
||||
first_address_indexes,
|
||||
first_addr_indexes,
|
||||
cache,
|
||||
vr,
|
||||
any_address_indexes,
|
||||
addresses_data,
|
||||
any_addr_indexes,
|
||||
addrs_data,
|
||||
)?;
|
||||
|
||||
Ok((
|
||||
@@ -92,13 +92,13 @@ pub(crate) fn process_outputs(
|
||||
// Phase 2: Sequential accumulation
|
||||
let estimated_per_type = (output_count / 8).max(8);
|
||||
let mut transacted = Transacted::default();
|
||||
let mut received_data = AddressTypeToVec::with_capacity(estimated_per_type);
|
||||
let mut address_data =
|
||||
AddressTypeToTypeIndexMap::<WithAddressDataSource<FundedAddressData>>::with_capacity(
|
||||
let mut received_data = AddrTypeToVec::with_capacity(estimated_per_type);
|
||||
let mut addr_data =
|
||||
AddrTypeToTypeIndexMap::<WithAddrDataSource<FundedAddrData>>::with_capacity(
|
||||
estimated_per_type,
|
||||
);
|
||||
let mut tx_index_vecs =
|
||||
AddressTypeToTypeIndexMap::<SmallVec<[TxIndex; 4]>>::with_capacity(estimated_per_type);
|
||||
AddrTypeToTypeIndexMap::<SmallVec<[TxIndex; 4]>>::with_capacity(estimated_per_type);
|
||||
|
||||
for (value, output_type, addr_info) in items {
|
||||
transacted.iterate(value, output_type);
|
||||
@@ -109,8 +109,8 @@ pub(crate) fn process_outputs(
|
||||
.unwrap()
|
||||
.push((type_index, value));
|
||||
|
||||
if let Some(addr_data) = addr_data_opt {
|
||||
address_data.insert_for_type(output_type, type_index, addr_data);
|
||||
if let Some(source) = addr_data_opt {
|
||||
addr_data.insert_for_type(output_type, type_index, source);
|
||||
}
|
||||
|
||||
tx_index_vecs
|
||||
@@ -125,7 +125,7 @@ pub(crate) fn process_outputs(
|
||||
Ok(OutputsResult {
|
||||
transacted,
|
||||
received_data,
|
||||
address_data,
|
||||
addr_data,
|
||||
tx_index_vecs,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user