mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-18 14:38:10 -07:00
global: renames part 2
This commit is contained in:
@@ -259,7 +259,7 @@ impl AddressTypeToActivityCountVecs {
|
||||
pub struct AddressActivityVecs<M: StorageMode = Rw> {
|
||||
pub all: ActivityCountVecs<M>,
|
||||
#[traversable(flatten)]
|
||||
pub by_addresstype: AddressTypeToActivityCountVecs<M>,
|
||||
pub by_address_type: AddressTypeToActivityCountVecs<M>,
|
||||
}
|
||||
|
||||
impl AddressActivityVecs {
|
||||
@@ -272,7 +272,7 @@ impl AddressActivityVecs {
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
all: ActivityCountVecs::forced_import(db, name, version, indexes, cached_starts)?,
|
||||
by_addresstype: AddressTypeToActivityCountVecs::forced_import(
|
||||
by_address_type: AddressTypeToActivityCountVecs::forced_import(
|
||||
db, name, version, indexes, cached_starts,
|
||||
)?,
|
||||
})
|
||||
@@ -281,7 +281,7 @@ impl AddressActivityVecs {
|
||||
pub(crate) fn min_stateful_len(&self) -> usize {
|
||||
self.all
|
||||
.min_stateful_len()
|
||||
.min(self.by_addresstype.min_stateful_len())
|
||||
.min(self.by_address_type.min_stateful_len())
|
||||
}
|
||||
|
||||
pub(crate) fn par_iter_height_mut(
|
||||
@@ -289,12 +289,12 @@ impl AddressActivityVecs {
|
||||
) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
self.all
|
||||
.par_iter_height_mut()
|
||||
.chain(self.by_addresstype.par_iter_height_mut())
|
||||
.chain(self.by_address_type.par_iter_height_mut())
|
||||
}
|
||||
|
||||
pub(crate) fn reset_height(&mut self) -> Result<()> {
|
||||
self.all.reset_height()?;
|
||||
self.by_addresstype.reset_height()?;
|
||||
self.by_address_type.reset_height()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -304,7 +304,7 @@ impl AddressActivityVecs {
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.all.compute_rest(max_from, exit)?;
|
||||
self.by_addresstype.compute_rest(max_from, exit)?;
|
||||
self.by_address_type.compute_rest(max_from, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -315,7 +315,7 @@ impl AddressActivityVecs {
|
||||
) -> Result<()> {
|
||||
let totals = counts.totals();
|
||||
self.all.truncate_push_height(height, &totals)?;
|
||||
self.by_addresstype.truncate_push_height(height, counts)?;
|
||||
self.by_address_type.truncate_push_height(height, counts)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ impl AddressTypeToAddressCountVecs {
|
||||
pub struct AddressCountsVecs<M: StorageMode = Rw> {
|
||||
pub all: AddressCountVecs<M>,
|
||||
#[traversable(flatten)]
|
||||
pub by_addresstype: AddressTypeToAddressCountVecs<M>,
|
||||
pub by_address_type: AddressTypeToAddressCountVecs<M>,
|
||||
}
|
||||
|
||||
impl AddressCountsVecs {
|
||||
@@ -177,24 +177,24 @@ impl AddressCountsVecs {
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
all: AddressCountVecs::forced_import(db, name, version, indexes)?,
|
||||
by_addresstype: AddressTypeToAddressCountVecs::forced_import(db, name, version, indexes)?,
|
||||
by_address_type: AddressTypeToAddressCountVecs::forced_import(db, name, version, indexes)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn min_stateful_len(&self) -> usize {
|
||||
self.all.height.len().min(self.by_addresstype.min_stateful_len())
|
||||
self.all.height.len().min(self.by_address_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_addresstype.par_iter_height_mut())
|
||||
.chain(self.by_address_type.par_iter_height_mut())
|
||||
}
|
||||
|
||||
pub(crate) fn reset_height(&mut self) -> Result<()> {
|
||||
self.all.height.reset()?;
|
||||
self.by_addresstype.reset_height()?;
|
||||
self.by_address_type.reset_height()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ impl AddressCountsVecs {
|
||||
address_counts: &AddressTypeToAddressCount,
|
||||
) -> Result<()> {
|
||||
self.all.height.truncate_push(height, total.into())?;
|
||||
self.by_addresstype
|
||||
self.by_address_type
|
||||
.truncate_push_height(height, address_counts)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -215,7 +215,7 @@ impl AddressCountsVecs {
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let sources = self.by_addresstype.by_height();
|
||||
let sources = self.by_address_type.by_height();
|
||||
self.all
|
||||
.height
|
||||
.compute_sum_of_others(starting_indexes.height, &sources, exit)?;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use brk_cohort::ByAddressType;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{BasisPoints32, StoredI64, StoredU64, Version};
|
||||
use brk_types::{BasisPointsSigned32, StoredI64, StoredU64, Version};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
@@ -9,13 +9,13 @@ use crate::{
|
||||
|
||||
use super::AddressCountsVecs;
|
||||
|
||||
type AddrDelta = LazyRollingDeltasFromHeight<StoredU64, StoredI64, BasisPoints32>;
|
||||
type AddrDelta = LazyRollingDeltasFromHeight<StoredU64, StoredI64, BasisPointsSigned32>;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct DeltaVecs {
|
||||
pub all: AddrDelta,
|
||||
#[traversable(flatten)]
|
||||
pub by_addresstype: ByAddressType<AddrDelta>,
|
||||
pub by_address_type: ByAddressType<AddrDelta>,
|
||||
}
|
||||
|
||||
impl DeltaVecs {
|
||||
@@ -35,7 +35,7 @@ impl DeltaVecs {
|
||||
indexes,
|
||||
);
|
||||
|
||||
let by_addresstype = address_count.by_addresstype.map_with_name(|name, addr| {
|
||||
let by_address_type = address_count.by_address_type.map_with_name(|name, addr| {
|
||||
LazyRollingDeltasFromHeight::new(
|
||||
&format!("{name}_address_count"),
|
||||
version,
|
||||
@@ -47,7 +47,7 @@ impl DeltaVecs {
|
||||
|
||||
Self {
|
||||
all,
|
||||
by_addresstype,
|
||||
by_address_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ macro_rules! define_any_address_indexes_vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
$($field: BytesVec::forced_import_with(
|
||||
ImportOptions::new(db, "anyaddressindex", version)
|
||||
ImportOptions::new(db, "any_address_index", version)
|
||||
.with_saved_stamped_changes(SAVED_STAMPED_CHANGES),
|
||||
)?,)*
|
||||
})
|
||||
@@ -57,11 +57,11 @@ macro_rules! define_any_address_indexes_vecs {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get address index for a given type and typeindex.
|
||||
/// 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, address_type: OutputType, typeindex: TypeIndex, reader: &Reader) -> Result<AnyAddressIndex> {
|
||||
pub(crate) fn get(&self, address_type: OutputType, type_index: TypeIndex, reader: &Reader) -> Result<AnyAddressIndex> {
|
||||
match address_type {
|
||||
$(OutputType::$variant => Ok(self.$field.get_any_or_read_at(typeindex.into(), reader)?.unwrap()),)*
|
||||
$(OutputType::$variant => Ok(self.$field.get_any_or_read_at(type_index.into(), reader)?.unwrap()),)*
|
||||
_ => unreachable!("Invalid address type: {:?}", address_type),
|
||||
}
|
||||
}
|
||||
@@ -74,10 +74,10 @@ macro_rules! define_any_address_indexes_vecs {
|
||||
|
||||
impl<M: StorageMode> AnyAddressIndexesVecs<M> {
|
||||
/// Get address index with single read (no caching).
|
||||
pub fn get_once(&self, address_type: OutputType, typeindex: TypeIndex) -> Result<AnyAddressIndex> {
|
||||
pub fn get_once(&self, address_type: OutputType, type_index: TypeIndex) -> Result<AnyAddressIndex> {
|
||||
match address_type {
|
||||
$(OutputType::$variant => self.$field
|
||||
.collect_one(<$index>::from(usize::from(typeindex)))
|
||||
.collect_one(<$index>::from(usize::from(type_index)))
|
||||
.ok_or_else(|| Error::UnsupportedType(address_type.to_string())),)*
|
||||
_ => Err(Error::UnsupportedType(address_type.to_string())),
|
||||
}
|
||||
@@ -177,18 +177,18 @@ fn process_single_type_merged<I: vecdb::VecIndex>(
|
||||
let mut pushes = Vec::with_capacity(map1.len() + map2.len());
|
||||
let mut update_count = 0usize;
|
||||
|
||||
for (typeindex, any_index) in map1.into_iter().chain(map2) {
|
||||
if usize::from(typeindex) < current_len {
|
||||
vec.update(I::from(usize::from(typeindex)), any_index)?;
|
||||
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((typeindex, any_index));
|
||||
pushes.push((type_index, any_index));
|
||||
}
|
||||
}
|
||||
|
||||
let push_count = pushes.len();
|
||||
if !pushes.is_empty() {
|
||||
pushes.sort_unstable_by_key(|(typeindex, _)| *typeindex);
|
||||
pushes.sort_unstable_by_key(|(type_index, _)| *type_index);
|
||||
for (_, any_index) in pushes {
|
||||
vec.push(any_index);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ use super::TotalAddressCountVecs;
|
||||
pub struct NewAddressCountVecs<M: StorageMode = Rw> {
|
||||
pub all: PerBlockCumulativeWithSums<StoredU64, StoredU64, M>,
|
||||
#[traversable(flatten)]
|
||||
pub by_addresstype: ByAddressType<PerBlockCumulativeWithSums<StoredU64, StoredU64, M>>,
|
||||
pub by_address_type: ByAddressType<PerBlockCumulativeWithSums<StoredU64, StoredU64, M>>,
|
||||
}
|
||||
|
||||
impl NewAddressCountVecs {
|
||||
@@ -34,7 +34,7 @@ impl NewAddressCountVecs {
|
||||
cached_starts,
|
||||
)?;
|
||||
|
||||
let by_addresstype = ByAddressType::new_with_name(|name| {
|
||||
let by_address_type = ByAddressType::new_with_name(|name| {
|
||||
PerBlockCumulativeWithSums::forced_import(
|
||||
db,
|
||||
&format!("{name}_new_address_count"),
|
||||
@@ -46,7 +46,7 @@ impl NewAddressCountVecs {
|
||||
|
||||
Ok(Self {
|
||||
all,
|
||||
by_addresstype,
|
||||
by_address_type,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -61,9 +61,9 @@ impl NewAddressCountVecs {
|
||||
})?;
|
||||
|
||||
for ((_, new), (_, total)) in self
|
||||
.by_addresstype
|
||||
.by_address_type
|
||||
.iter_mut()
|
||||
.zip(total_address_count.by_addresstype.iter())
|
||||
.zip(total_address_count.by_address_type.iter())
|
||||
{
|
||||
new.compute(max_from, exit, |height_vec| {
|
||||
Ok(height_vec.compute_change(max_from, &total.height, 1, exit)?)
|
||||
|
||||
@@ -13,7 +13,7 @@ use super::AddressCountsVecs;
|
||||
pub struct TotalAddressCountVecs<M: StorageMode = Rw> {
|
||||
pub all: PerBlock<StoredU64, M>,
|
||||
#[traversable(flatten)]
|
||||
pub by_addresstype: ByAddressType<PerBlock<StoredU64, M>>,
|
||||
pub by_address_type: ByAddressType<PerBlock<StoredU64, M>>,
|
||||
}
|
||||
|
||||
impl TotalAddressCountVecs {
|
||||
@@ -24,7 +24,7 @@ impl TotalAddressCountVecs {
|
||||
) -> Result<Self> {
|
||||
let all = PerBlock::forced_import(db, "total_address_count", version, indexes)?;
|
||||
|
||||
let by_addresstype: ByAddressType<PerBlock<StoredU64>> =
|
||||
let by_address_type: ByAddressType<PerBlock<StoredU64>> =
|
||||
ByAddressType::new_with_name(|name| {
|
||||
PerBlock::forced_import(
|
||||
db,
|
||||
@@ -36,7 +36,7 @@ impl TotalAddressCountVecs {
|
||||
|
||||
Ok(Self {
|
||||
all,
|
||||
by_addresstype,
|
||||
by_address_type,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -55,11 +55,11 @@ impl TotalAddressCountVecs {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
for ((_, total), ((_, addr), (_, empty))) in self.by_addresstype.iter_mut().zip(
|
||||
for ((_, total), ((_, addr), (_, empty))) in self.by_address_type.iter_mut().zip(
|
||||
address_count
|
||||
.by_addresstype
|
||||
.by_address_type
|
||||
.iter()
|
||||
.zip(empty_address_count.by_addresstype.iter()),
|
||||
.zip(empty_address_count.by_address_type.iter()),
|
||||
) {
|
||||
total
|
||||
.height
|
||||
|
||||
@@ -59,14 +59,14 @@ impl<T> AddressTypeToTypeIndexMap<T> {
|
||||
Self::merge_single(&mut self.p2wsh, &mut other.p2wsh);
|
||||
}
|
||||
|
||||
/// Insert a value for a specific address type and typeindex.
|
||||
/// Insert a value for a specific address type and type_index.
|
||||
pub(crate) fn insert_for_type(
|
||||
&mut self,
|
||||
address_type: OutputType,
|
||||
typeindex: TypeIndex,
|
||||
type_index: TypeIndex,
|
||||
value: T,
|
||||
) {
|
||||
self.get_mut(address_type).unwrap().insert(typeindex, value);
|
||||
self.get_mut(address_type).unwrap().insert(type_index, value);
|
||||
}
|
||||
|
||||
/// Consume and iterate over entries by address type.
|
||||
@@ -96,8 +96,8 @@ where
|
||||
pub(crate) fn merge_vec(mut self, other: Self) -> Self {
|
||||
for (address_type, other_map) in other.0.into_iter() {
|
||||
let self_map = self.0.get_mut_unwrap(address_type);
|
||||
for (typeindex, mut other_vec) in other_map {
|
||||
match self_map.entry(typeindex) {
|
||||
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() {
|
||||
|
||||
+15
-15
@@ -37,14 +37,14 @@ impl AddressCache {
|
||||
|
||||
/// Check if address is in cache (either funded or empty).
|
||||
#[inline]
|
||||
pub(crate) fn contains(&self, address_type: OutputType, typeindex: TypeIndex) -> bool {
|
||||
pub(crate) fn contains(&self, address_type: OutputType, type_index: TypeIndex) -> bool {
|
||||
self.funded
|
||||
.get(address_type)
|
||||
.is_some_and(|m| m.contains_key(&typeindex))
|
||||
.is_some_and(|m| m.contains_key(&type_index))
|
||||
|| self
|
||||
.empty
|
||||
.get(address_type)
|
||||
.is_some_and(|m| m.contains_key(&typeindex))
|
||||
.is_some_and(|m| m.contains_key(&type_index))
|
||||
}
|
||||
|
||||
/// Merge address data into funded cache.
|
||||
@@ -68,9 +68,9 @@ impl AddressCache {
|
||||
/// Update transaction counts for addresses.
|
||||
pub(crate) fn update_tx_counts(
|
||||
&mut self,
|
||||
txindex_vecs: AddressTypeToTypeIndexMap<SmallVec<[TxIndex; 4]>>,
|
||||
tx_index_vecs: AddressTypeToTypeIndexMap<SmallVec<[TxIndex; 4]>>,
|
||||
) {
|
||||
update_tx_counts(&mut self.funded, &mut self.empty, txindex_vecs);
|
||||
update_tx_counts(&mut self.funded, &mut self.empty, tx_index_vecs);
|
||||
}
|
||||
|
||||
/// Take the cache contents for flushing, leaving empty caches.
|
||||
@@ -93,33 +93,33 @@ impl AddressCache {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn load_uncached_address_data(
|
||||
address_type: OutputType,
|
||||
typeindex: TypeIndex,
|
||||
first_addressindexes: &ByAddressType<TypeIndex>,
|
||||
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 (typeindex >= first for this height)
|
||||
let first = *first_addressindexes.get(address_type).unwrap();
|
||||
if first <= typeindex {
|
||||
// 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, typeindex) {
|
||||
if cache.contains(address_type, type_index) {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// Read from storage
|
||||
let reader = vr.address_reader(address_type);
|
||||
let anyaddressindex = any_address_indexes.get(address_type, typeindex, reader)?;
|
||||
let any_address_index = any_address_indexes.get(address_type, type_index, reader)?;
|
||||
|
||||
Ok(Some(match anyaddressindex.to_enum() {
|
||||
Ok(Some(match any_address_index.to_enum() {
|
||||
AnyAddressDataIndexEnum::Funded(funded_index) => {
|
||||
let reader = &vr.anyaddressindex_to_anyaddressdata.funded;
|
||||
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)?
|
||||
@@ -127,7 +127,7 @@ pub(crate) fn load_uncached_address_data(
|
||||
WithAddressDataSource::FromFunded(funded_index, funded_data)
|
||||
}
|
||||
AnyAddressDataIndexEnum::Empty(empty_index) => {
|
||||
let reader = &vr.anyaddressindex_to_anyaddressdata.empty;
|
||||
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)?
|
||||
|
||||
@@ -26,17 +26,17 @@ pub(crate) fn process_funded_addresses(
|
||||
let mut pushes: Vec<(OutputType, TypeIndex, FundedAddressData)> = Vec::with_capacity(total);
|
||||
|
||||
for (address_type, items) in funded_updates.into_iter() {
|
||||
for (typeindex, source) in items {
|
||||
for (type_index, source) in items {
|
||||
match source {
|
||||
WithAddressDataSource::New(data) => {
|
||||
pushes.push((address_type, typeindex, 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, typeindex, data));
|
||||
pushes.push((address_type, type_index, data));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,21 +57,21 @@ pub(crate) fn process_funded_addresses(
|
||||
let holes_count = addresses_data.funded.holes().len();
|
||||
let mut pushes_iter = pushes.into_iter();
|
||||
|
||||
for (address_type, typeindex, data) in pushes_iter.by_ref().take(holes_count) {
|
||||
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(typeindex, AnyAddressIndex::from(index));
|
||||
.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, typeindex, data) in pushes_iter {
|
||||
for (address_type, type_index, data) in pushes_iter {
|
||||
addresses_data.funded.push(data);
|
||||
result.get_mut(address_type).unwrap().insert(
|
||||
typeindex,
|
||||
type_index,
|
||||
AnyAddressIndex::from(FundedAddressIndex::from(next_index)),
|
||||
);
|
||||
next_index += 1;
|
||||
@@ -97,17 +97,17 @@ pub(crate) fn process_empty_addresses(
|
||||
let mut pushes: Vec<(OutputType, TypeIndex, EmptyAddressData)> = Vec::with_capacity(total);
|
||||
|
||||
for (address_type, items) in empty_updates.into_iter() {
|
||||
for (typeindex, source) in items {
|
||||
for (type_index, source) in items {
|
||||
match source {
|
||||
WithAddressDataSource::New(data) => {
|
||||
pushes.push((address_type, typeindex, 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, typeindex, data));
|
||||
pushes.push((address_type, type_index, data));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,21 +128,21 @@ pub(crate) fn process_empty_addresses(
|
||||
let holes_count = addresses_data.empty.holes().len();
|
||||
let mut pushes_iter = pushes.into_iter();
|
||||
|
||||
for (address_type, typeindex, data) in pushes_iter.by_ref().take(holes_count) {
|
||||
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(typeindex, AnyAddressIndex::from(index));
|
||||
.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, typeindex, data) in pushes_iter {
|
||||
for (address_type, type_index, data) in pushes_iter {
|
||||
addresses_data.empty.push(data);
|
||||
result.get_mut(address_type).unwrap().insert(
|
||||
typeindex,
|
||||
type_index,
|
||||
AnyAddressIndex::from(EmptyAddressIndex::from(next_index)),
|
||||
);
|
||||
next_index += 1;
|
||||
|
||||
@@ -16,35 +16,35 @@ use super::with_source::WithAddressDataSource;
|
||||
pub(crate) fn update_tx_counts(
|
||||
funded_cache: &mut AddressTypeToTypeIndexMap<WithAddressDataSource<FundedAddressData>>,
|
||||
empty_cache: &mut AddressTypeToTypeIndexMap<WithAddressDataSource<EmptyAddressData>>,
|
||||
mut txindex_vecs: AddressTypeToTypeIndexMap<SmallVec<[TxIndex; 4]>>,
|
||||
mut tx_index_vecs: AddressTypeToTypeIndexMap<SmallVec<[TxIndex; 4]>>,
|
||||
) {
|
||||
// First, deduplicate txindex_vecs for addresses that appear multiple times in a block
|
||||
for (_, map) in txindex_vecs.iter_mut() {
|
||||
for (_, txindex_vec) in map.iter_mut() {
|
||||
if txindex_vec.len() > 1 {
|
||||
txindex_vec.sort_unstable();
|
||||
txindex_vec.dedup();
|
||||
// First, deduplicate tx_index_vecs for addresses that appear multiple times in a block
|
||||
for (_, map) in tx_index_vecs.iter_mut() {
|
||||
for (_, tx_index_vec) in map.iter_mut() {
|
||||
if tx_index_vec.len() > 1 {
|
||||
tx_index_vec.sort_unstable();
|
||||
tx_index_vec.dedup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update tx_count on address data
|
||||
for (address_type, typeindex, txindex_vec) in txindex_vecs
|
||||
for (address_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 = txindex_vec.len() as u32;
|
||||
let tx_count = tx_index_vec.len() as u32;
|
||||
|
||||
if let Some(addr_data) = funded_cache
|
||||
.get_mut(address_type)
|
||||
.unwrap()
|
||||
.get_mut(&typeindex)
|
||||
.get_mut(&type_index)
|
||||
{
|
||||
addr_data.tx_count += tx_count;
|
||||
} else if let Some(addr_data) = empty_cache
|
||||
.get_mut(address_type)
|
||||
.unwrap()
|
||||
.get_mut(&typeindex)
|
||||
.get_mut(&type_index)
|
||||
{
|
||||
addr_data.tx_count += tx_count;
|
||||
}
|
||||
|
||||
@@ -22,20 +22,20 @@ use super::super::{
|
||||
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: (typeindex, value) for each address.
|
||||
/// 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, typeindex).
|
||||
/// Address data looked up during processing, keyed by (address_type, type_index).
|
||||
pub address_data: AddressTypeToTypeIndexMap<WithAddressDataSource<FundedAddressData>>,
|
||||
/// Transaction indexes per address for tx_count tracking.
|
||||
pub txindex_vecs: AddressTypeToTypeIndexMap<SmallVec<[TxIndex; 4]>>,
|
||||
pub tx_index_vecs: AddressTypeToTypeIndexMap<SmallVec<[TxIndex; 4]>>,
|
||||
}
|
||||
|
||||
/// Process inputs (spent UTXOs) for a block.
|
||||
///
|
||||
/// For each input:
|
||||
/// 1. Use pre-collected outpoint (from reusable iterator, avoids PcoVec re-decompression)
|
||||
/// 2. Resolve outpoint to txoutindex
|
||||
/// 3. Get the creation height from txoutindex_to_height map
|
||||
/// 2. Resolve outpoint to txout_index
|
||||
/// 3. Get the creation height from txout_index_to_height map
|
||||
/// 4. Read value and type from the referenced output (random access via mmap)
|
||||
/// 5. Look up address data if input references an address type
|
||||
/// 6. Accumulate into height_to_sent map
|
||||
@@ -46,35 +46,35 @@ pub struct InputsResult {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn process_inputs(
|
||||
input_count: usize,
|
||||
txinindex_to_txindex: &[TxIndex],
|
||||
txinindex_to_value: &[Sats],
|
||||
txinindex_to_outputtype: &[OutputType],
|
||||
txinindex_to_typeindex: &[TypeIndex],
|
||||
txinindex_to_prev_height: &[Height],
|
||||
first_addressindexes: &ByAddressType<TypeIndex>,
|
||||
txin_index_to_tx_index: &[TxIndex],
|
||||
txin_index_to_value: &[Sats],
|
||||
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,
|
||||
vr: &VecsReaders,
|
||||
any_address_indexes: &AnyAddressIndexesVecs,
|
||||
addresses_data: &AddressesDataVecs,
|
||||
) -> Result<InputsResult> {
|
||||
let map_fn = |local_idx: usize| -> Result<_> {
|
||||
let txindex = txinindex_to_txindex[local_idx];
|
||||
let tx_index = txin_index_to_tx_index[local_idx];
|
||||
|
||||
let prev_height = txinindex_to_prev_height[local_idx];
|
||||
let value = txinindex_to_value[local_idx];
|
||||
let input_type = txinindex_to_outputtype[local_idx];
|
||||
let prev_height = txin_index_to_prev_height[local_idx];
|
||||
let value = txin_index_to_value[local_idx];
|
||||
let input_type = txin_index_to_output_type[local_idx];
|
||||
|
||||
if input_type.is_not_address() {
|
||||
return Ok((prev_height, value, input_type, None));
|
||||
}
|
||||
|
||||
let typeindex = txinindex_to_typeindex[local_idx];
|
||||
let type_index = txin_index_to_type_index[local_idx];
|
||||
|
||||
// Look up address data
|
||||
let addr_data_opt = load_uncached_address_data(
|
||||
input_type,
|
||||
typeindex,
|
||||
first_addressindexes,
|
||||
type_index,
|
||||
first_address_indexes,
|
||||
cache,
|
||||
vr,
|
||||
any_address_indexes,
|
||||
@@ -85,7 +85,7 @@ pub(crate) fn process_inputs(
|
||||
prev_height,
|
||||
value,
|
||||
input_type,
|
||||
Some((typeindex, txindex, value, addr_data_opt)),
|
||||
Some((type_index, tx_index, value, addr_data_opt)),
|
||||
))
|
||||
};
|
||||
|
||||
@@ -113,7 +113,7 @@ pub(crate) fn process_inputs(
|
||||
AddressTypeToTypeIndexMap::<WithAddressDataSource<FundedAddressData>>::with_capacity(
|
||||
estimated_per_type,
|
||||
);
|
||||
let mut txindex_vecs =
|
||||
let mut tx_index_vecs =
|
||||
AddressTypeToTypeIndexMap::<SmallVec<[TxIndex; 4]>>::with_capacity(estimated_per_type);
|
||||
|
||||
for (prev_height, value, output_type, addr_info) in items {
|
||||
@@ -122,24 +122,24 @@ pub(crate) fn process_inputs(
|
||||
.or_default()
|
||||
.iterate(value, output_type);
|
||||
|
||||
if let Some((typeindex, txindex, value, addr_data_opt)) = addr_info {
|
||||
if let Some((type_index, tx_index, value, addr_data_opt)) = addr_info {
|
||||
sent_data
|
||||
.entry(prev_height)
|
||||
.or_default()
|
||||
.get_mut(output_type)
|
||||
.unwrap()
|
||||
.push((typeindex, value));
|
||||
.push((type_index, value));
|
||||
|
||||
if let Some(addr_data) = addr_data_opt {
|
||||
address_data.insert_for_type(output_type, typeindex, addr_data);
|
||||
address_data.insert_for_type(output_type, type_index, addr_data);
|
||||
}
|
||||
|
||||
txindex_vecs
|
||||
tx_index_vecs
|
||||
.get_mut(output_type)
|
||||
.unwrap()
|
||||
.entry(typeindex)
|
||||
.entry(type_index)
|
||||
.or_default()
|
||||
.push(txindex);
|
||||
.push(tx_index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,6 +147,6 @@ pub(crate) fn process_inputs(
|
||||
height_to_sent,
|
||||
sent_data,
|
||||
address_data,
|
||||
txindex_vecs,
|
||||
tx_index_vecs,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -21,50 +21,50 @@ use super::super::{
|
||||
pub struct OutputsResult {
|
||||
/// Aggregated supply transacted in this block.
|
||||
pub transacted: Transacted,
|
||||
/// Per-address-type received data: (typeindex, value) for each address.
|
||||
/// 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, typeindex).
|
||||
/// Address data looked up during processing, keyed by (address_type, type_index).
|
||||
pub address_data: AddressTypeToTypeIndexMap<WithAddressDataSource<FundedAddressData>>,
|
||||
/// Transaction indexes per address for tx_count tracking.
|
||||
pub txindex_vecs: AddressTypeToTypeIndexMap<SmallVec<[TxIndex; 4]>>,
|
||||
pub tx_index_vecs: AddressTypeToTypeIndexMap<SmallVec<[TxIndex; 4]>>,
|
||||
}
|
||||
|
||||
/// Process outputs (new UTXOs) for a block.
|
||||
///
|
||||
/// For each output:
|
||||
/// 1. Read pre-collected value, output type, and typeindex
|
||||
/// 1. Read pre-collected value, output type, and type_index
|
||||
/// 2. Accumulate into Transacted by type and amount
|
||||
/// 3. Look up address data if output is an address type
|
||||
/// 4. Track address-specific data for address cohort processing
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn process_outputs(
|
||||
txoutindex_to_txindex: &[TxIndex],
|
||||
txoutdata_vec: &[TxOutData],
|
||||
first_addressindexes: &ByAddressType<TypeIndex>,
|
||||
txout_index_to_tx_index: &[TxIndex],
|
||||
txout_data_vec: &[TxOutData],
|
||||
first_address_indexes: &ByAddressType<TypeIndex>,
|
||||
cache: &AddressCache,
|
||||
vr: &VecsReaders,
|
||||
any_address_indexes: &AnyAddressIndexesVecs,
|
||||
addresses_data: &AddressesDataVecs,
|
||||
) -> Result<OutputsResult> {
|
||||
let output_count = txoutdata_vec.len();
|
||||
let output_count = txout_data_vec.len();
|
||||
|
||||
// Phase 1: Address lookups (mmap reads) — parallel for large blocks, sequential for small
|
||||
let map_fn = |local_idx: usize| -> Result<_> {
|
||||
let txoutdata = &txoutdata_vec[local_idx];
|
||||
let value = txoutdata.value;
|
||||
let output_type = txoutdata.outputtype;
|
||||
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() {
|
||||
return Ok((value, output_type, None));
|
||||
}
|
||||
|
||||
let typeindex = txoutdata.typeindex;
|
||||
let txindex = txoutindex_to_txindex[local_idx];
|
||||
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(
|
||||
output_type,
|
||||
typeindex,
|
||||
first_addressindexes,
|
||||
type_index,
|
||||
first_address_indexes,
|
||||
cache,
|
||||
vr,
|
||||
any_address_indexes,
|
||||
@@ -74,7 +74,7 @@ pub(crate) fn process_outputs(
|
||||
Ok((
|
||||
value,
|
||||
output_type,
|
||||
Some((typeindex, txindex, value, addr_data_opt)),
|
||||
Some((type_index, tx_index, value, addr_data_opt)),
|
||||
))
|
||||
};
|
||||
|
||||
@@ -97,28 +97,28 @@ pub(crate) fn process_outputs(
|
||||
AddressTypeToTypeIndexMap::<WithAddressDataSource<FundedAddressData>>::with_capacity(
|
||||
estimated_per_type,
|
||||
);
|
||||
let mut txindex_vecs =
|
||||
let mut tx_index_vecs =
|
||||
AddressTypeToTypeIndexMap::<SmallVec<[TxIndex; 4]>>::with_capacity(estimated_per_type);
|
||||
|
||||
for (value, output_type, addr_info) in items {
|
||||
transacted.iterate(value, output_type);
|
||||
|
||||
if let Some((typeindex, txindex, value, addr_data_opt)) = addr_info {
|
||||
if let Some((type_index, tx_index, value, addr_data_opt)) = addr_info {
|
||||
received_data
|
||||
.get_mut(output_type)
|
||||
.unwrap()
|
||||
.push((typeindex, value));
|
||||
.push((type_index, value));
|
||||
|
||||
if let Some(addr_data) = addr_data_opt {
|
||||
address_data.insert_for_type(output_type, typeindex, addr_data);
|
||||
address_data.insert_for_type(output_type, type_index, addr_data);
|
||||
}
|
||||
|
||||
txindex_vecs
|
||||
tx_index_vecs
|
||||
.get_mut(output_type)
|
||||
.unwrap()
|
||||
.entry(typeindex)
|
||||
.entry(type_index)
|
||||
.or_default()
|
||||
.push(txindex);
|
||||
.push(tx_index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +126,6 @@ pub(crate) fn process_outputs(
|
||||
transacted,
|
||||
received_data,
|
||||
address_data,
|
||||
txindex_vecs,
|
||||
tx_index_vecs,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ pub(crate) fn process_blocks(
|
||||
starting_height: Height,
|
||||
last_height: Height,
|
||||
chain_state: &mut Vec<BlockState>,
|
||||
txindex_to_height: &mut RangeMap<TxIndex, Height>,
|
||||
tx_index_to_height: &mut RangeMap<TxIndex, Height>,
|
||||
cached_prices: &[Cents],
|
||||
cached_timestamps: &[Timestamp],
|
||||
cached_price_range_max: &PriceRangeMax,
|
||||
@@ -63,14 +63,14 @@ pub(crate) fn process_blocks(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let height_to_first_txindex = &indexer.vecs.transactions.first_txindex;
|
||||
let height_to_first_txoutindex = &indexer.vecs.outputs.first_txoutindex;
|
||||
let height_to_first_txinindex = &indexer.vecs.inputs.first_txinindex;
|
||||
let height_to_first_tx_index = &indexer.vecs.transactions.first_tx_index;
|
||||
let height_to_first_txout_index = &indexer.vecs.outputs.first_txout_index;
|
||||
let height_to_first_txin_index = &indexer.vecs.inputs.first_txin_index;
|
||||
let height_to_tx_count = &transactions.count.total.base.height;
|
||||
let height_to_output_count = &outputs.count.total.full.sum;
|
||||
let height_to_input_count = &inputs.count.full.sum;
|
||||
let txindex_to_output_count = &indexes.txindex.output_count;
|
||||
let txindex_to_input_count = &indexes.txindex.input_count;
|
||||
let tx_index_to_output_count = &indexes.tx_index.output_count;
|
||||
let tx_index_to_input_count = &indexes.tx_index.input_count;
|
||||
|
||||
let height_to_price_vec = cached_prices;
|
||||
let height_to_timestamp_vec = cached_timestamps;
|
||||
@@ -78,12 +78,12 @@ pub(crate) fn process_blocks(
|
||||
let start_usize = starting_height.to_usize();
|
||||
let end_usize = last_height.to_usize() + 1;
|
||||
|
||||
let height_to_first_txindex_vec: Vec<TxIndex> =
|
||||
height_to_first_txindex.collect_range_at(start_usize, end_usize);
|
||||
let height_to_first_txoutindex_vec: Vec<_> =
|
||||
height_to_first_txoutindex.collect_range_at(start_usize, end_usize);
|
||||
let height_to_first_txinindex_vec: Vec<_> =
|
||||
height_to_first_txinindex.collect_range_at(start_usize, end_usize);
|
||||
let height_to_first_tx_index_vec: Vec<TxIndex> =
|
||||
height_to_first_tx_index.collect_range_at(start_usize, end_usize);
|
||||
let height_to_first_txout_index_vec: Vec<_> =
|
||||
height_to_first_txout_index.collect_range_at(start_usize, end_usize);
|
||||
let height_to_first_txin_index_vec: Vec<_> =
|
||||
height_to_first_txin_index.collect_range_at(start_usize, end_usize);
|
||||
let height_to_tx_count_vec: Vec<_> =
|
||||
height_to_tx_count.collect_range_at(start_usize, end_usize);
|
||||
let height_to_output_count_vec: Vec<_> =
|
||||
@@ -106,39 +106,39 @@ pub(crate) fn process_blocks(
|
||||
let mut vr = VecsReaders::new(&vecs.any_address_indexes, &vecs.addresses_data);
|
||||
debug!("VecsReaders created");
|
||||
|
||||
// Extend txindex_to_height RangeMap with new entries (incremental, O(new_blocks))
|
||||
let target_len = indexer.vecs.transactions.first_txindex.len();
|
||||
let current_len = txindex_to_height.len();
|
||||
// Extend tx_index_to_height RangeMap with new entries (incremental, O(new_blocks))
|
||||
let target_len = indexer.vecs.transactions.first_tx_index.len();
|
||||
let current_len = tx_index_to_height.len();
|
||||
if current_len < target_len {
|
||||
debug!(
|
||||
"extending txindex_to_height RangeMap from {} to {}",
|
||||
"extending tx_index_to_height RangeMap from {} to {}",
|
||||
current_len, target_len
|
||||
);
|
||||
let new_entries: Vec<TxIndex> = indexer
|
||||
.vecs
|
||||
.transactions
|
||||
.first_txindex
|
||||
.first_tx_index
|
||||
.collect_range_at(current_len, target_len);
|
||||
for first_txindex in new_entries {
|
||||
txindex_to_height.push(first_txindex);
|
||||
for first_tx_index in new_entries {
|
||||
tx_index_to_height.push(first_tx_index);
|
||||
}
|
||||
} else if current_len > target_len {
|
||||
debug!(
|
||||
"truncating txindex_to_height RangeMap from {} to {}",
|
||||
"truncating tx_index_to_height RangeMap from {} to {}",
|
||||
current_len, target_len
|
||||
);
|
||||
txindex_to_height.truncate(target_len);
|
||||
tx_index_to_height.truncate(target_len);
|
||||
}
|
||||
debug!(
|
||||
"txindex_to_height RangeMap ready ({} entries)",
|
||||
txindex_to_height.len()
|
||||
"tx_index_to_height RangeMap ready ({} entries)",
|
||||
tx_index_to_height.len()
|
||||
);
|
||||
|
||||
// Create reusable iterators and buffers for per-block reads
|
||||
let mut txout_iters = TxOutReaders::new(indexer);
|
||||
let mut txin_iters = TxInReaders::new(indexer, inputs, txindex_to_height);
|
||||
let mut txout_to_txindex_buf = IndexToTxIndexBuf::new();
|
||||
let mut txin_to_txindex_buf = IndexToTxIndexBuf::new();
|
||||
let mut txin_iters = TxInReaders::new(indexer, inputs, tx_index_to_height);
|
||||
let mut txout_to_tx_index_buf = IndexToTxIndexBuf::new();
|
||||
let mut txin_to_tx_index_buf = IndexToTxIndexBuf::new();
|
||||
|
||||
// Pre-collect first address indexes per type for the block range
|
||||
let first_p2a_vec = indexer
|
||||
@@ -186,9 +186,9 @@ pub(crate) fn process_blocks(
|
||||
debug!("recovering address_counts from height {}", starting_height);
|
||||
let (mut address_counts, mut empty_address_counts) = if starting_height > Height::ZERO {
|
||||
let address_counts =
|
||||
AddressTypeToAddressCount::from((&vecs.addresses.funded.by_addresstype, starting_height));
|
||||
AddressTypeToAddressCount::from((&vecs.addresses.funded.by_address_type, starting_height));
|
||||
let empty_address_counts = AddressTypeToAddressCount::from((
|
||||
&vecs.addresses.empty.by_addresstype,
|
||||
&vecs.addresses.empty.by_address_type,
|
||||
starting_height,
|
||||
));
|
||||
(address_counts, empty_address_counts)
|
||||
@@ -225,11 +225,11 @@ pub(crate) fn process_blocks(
|
||||
|
||||
// Get block metadata from pre-collected vecs
|
||||
let offset = height.to_usize() - start_usize;
|
||||
let first_txindex = height_to_first_txindex_vec[offset];
|
||||
let first_tx_index = height_to_first_tx_index_vec[offset];
|
||||
let tx_count = u64::from(height_to_tx_count_vec[offset]);
|
||||
let first_txoutindex = height_to_first_txoutindex_vec[offset].to_usize();
|
||||
let first_txout_index = height_to_first_txout_index_vec[offset].to_usize();
|
||||
let output_count = u64::from(height_to_output_count_vec[offset]) as usize;
|
||||
let first_txinindex = height_to_first_txinindex_vec[offset].to_usize();
|
||||
let first_txin_index = height_to_first_txin_index_vec[offset].to_usize();
|
||||
let input_count = u64::from(height_to_input_count_vec[offset]) as usize;
|
||||
let timestamp = height_to_timestamp_collected[offset];
|
||||
let block_price = height_to_price_collected[offset];
|
||||
@@ -239,7 +239,7 @@ pub(crate) fn process_blocks(
|
||||
debug_assert_eq!(ctx.price_at(height), block_price);
|
||||
|
||||
// Get first address indexes for this height from pre-collected vecs
|
||||
let first_addressindexes = ByAddressType {
|
||||
let first_address_indexes = ByAddressType {
|
||||
p2a: TypeIndex::from(first_p2a_vec[offset].to_usize()),
|
||||
p2pk33: TypeIndex::from(first_p2pk33_vec[offset].to_usize()),
|
||||
p2pk65: TypeIndex::from(first_p2pk65_vec[offset].to_usize()),
|
||||
@@ -254,7 +254,7 @@ pub(crate) fn process_blocks(
|
||||
activity_counts.reset();
|
||||
|
||||
// Process outputs, inputs, and tick-tock in parallel via rayon::join.
|
||||
// Collection (build txindex mappings + bulk mmap reads) is merged into the
|
||||
// Collection (build tx_index mappings + bulk mmap reads) is merged into the
|
||||
// processing closures so outputs and inputs collection overlap each other
|
||||
// and tick-tock, instead of running sequentially before the join.
|
||||
let (matured, oi_result) = rayon::join(
|
||||
@@ -262,14 +262,14 @@ pub(crate) fn process_blocks(
|
||||
|| -> Result<_> {
|
||||
let (outputs_result, inputs_result) = rayon::join(
|
||||
|| {
|
||||
let txoutindex_to_txindex = txout_to_txindex_buf
|
||||
.build(first_txindex, tx_count, txindex_to_output_count);
|
||||
let txoutdata_vec =
|
||||
txout_iters.collect_block_outputs(first_txoutindex, output_count);
|
||||
let txout_index_to_tx_index = txout_to_tx_index_buf
|
||||
.build(first_tx_index, tx_count, tx_index_to_output_count);
|
||||
let txout_data_vec =
|
||||
txout_iters.collect_block_outputs(first_txout_index, output_count);
|
||||
process_outputs(
|
||||
txoutindex_to_txindex,
|
||||
txoutdata_vec,
|
||||
&first_addressindexes,
|
||||
txout_index_to_tx_index,
|
||||
txout_data_vec,
|
||||
&first_address_indexes,
|
||||
&cache,
|
||||
&vr,
|
||||
&vecs.any_address_indexes,
|
||||
@@ -278,22 +278,22 @@ pub(crate) fn process_blocks(
|
||||
},
|
||||
|| -> Result<_> {
|
||||
if input_count > 1 {
|
||||
let txinindex_to_txindex = txin_to_txindex_buf
|
||||
.build(first_txindex, tx_count, txindex_to_input_count);
|
||||
let (input_values, input_prev_heights, input_outputtypes, input_typeindexes) =
|
||||
let txin_index_to_tx_index = txin_to_tx_index_buf
|
||||
.build(first_tx_index, tx_count, tx_index_to_input_count);
|
||||
let (input_values, input_prev_heights, input_output_types, input_type_indexes) =
|
||||
txin_iters.collect_block_inputs(
|
||||
first_txinindex + 1,
|
||||
first_txin_index + 1,
|
||||
input_count - 1,
|
||||
height,
|
||||
);
|
||||
process_inputs(
|
||||
input_count - 1,
|
||||
&txinindex_to_txindex[1..],
|
||||
&txin_index_to_tx_index[1..],
|
||||
input_values,
|
||||
input_outputtypes,
|
||||
input_typeindexes,
|
||||
input_output_types,
|
||||
input_type_indexes,
|
||||
input_prev_heights,
|
||||
&first_addressindexes,
|
||||
&first_address_indexes,
|
||||
&cache,
|
||||
&vr,
|
||||
&vecs.any_address_indexes,
|
||||
@@ -304,7 +304,7 @@ pub(crate) fn process_blocks(
|
||||
height_to_sent: Default::default(),
|
||||
sent_data: Default::default(),
|
||||
address_data: Default::default(),
|
||||
txindex_vecs: Default::default(),
|
||||
tx_index_vecs: Default::default(),
|
||||
})
|
||||
}
|
||||
},
|
||||
@@ -318,11 +318,11 @@ pub(crate) fn process_blocks(
|
||||
cache.merge_funded(outputs_result.address_data);
|
||||
cache.merge_funded(inputs_result.address_data);
|
||||
|
||||
// Combine txindex_vecs from outputs and inputs, then update tx_count
|
||||
let combined_txindex_vecs = outputs_result
|
||||
.txindex_vecs
|
||||
.merge_vec(inputs_result.txindex_vecs);
|
||||
cache.update_tx_counts(combined_txindex_vecs);
|
||||
// Combine tx_index_vecs from outputs and inputs, then update tx_count
|
||||
let combined_tx_index_vecs = outputs_result
|
||||
.tx_index_vecs
|
||||
.merge_vec(inputs_result.tx_index_vecs);
|
||||
cache.update_tx_counts(combined_tx_index_vecs);
|
||||
|
||||
let mut transacted = outputs_result.transacted;
|
||||
let mut height_to_sent = inputs_result.height_to_sent;
|
||||
|
||||
@@ -15,17 +15,17 @@ use crate::{
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct TxOutData {
|
||||
pub value: Sats,
|
||||
pub outputtype: OutputType,
|
||||
pub typeindex: TypeIndex,
|
||||
pub output_type: OutputType,
|
||||
pub type_index: TypeIndex,
|
||||
}
|
||||
|
||||
/// Readers for txout vectors. Reuses internal buffers across blocks.
|
||||
pub struct TxOutReaders<'a> {
|
||||
indexer: &'a Indexer,
|
||||
values_buf: Vec<Sats>,
|
||||
outputtypes_buf: Vec<OutputType>,
|
||||
typeindexes_buf: Vec<TypeIndex>,
|
||||
txoutdata_buf: Vec<TxOutData>,
|
||||
output_types_buf: Vec<OutputType>,
|
||||
type_indexes_buf: Vec<TypeIndex>,
|
||||
txout_data_buf: Vec<TxOutData>,
|
||||
}
|
||||
|
||||
impl<'a> TxOutReaders<'a> {
|
||||
@@ -33,48 +33,48 @@ impl<'a> TxOutReaders<'a> {
|
||||
Self {
|
||||
indexer,
|
||||
values_buf: Vec::new(),
|
||||
outputtypes_buf: Vec::new(),
|
||||
typeindexes_buf: Vec::new(),
|
||||
txoutdata_buf: Vec::new(),
|
||||
output_types_buf: Vec::new(),
|
||||
type_indexes_buf: Vec::new(),
|
||||
txout_data_buf: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect output data for a block range using bulk reads with buffer reuse.
|
||||
pub(crate) fn collect_block_outputs(
|
||||
&mut self,
|
||||
first_txoutindex: usize,
|
||||
first_txout_index: usize,
|
||||
output_count: usize,
|
||||
) -> &[TxOutData] {
|
||||
let end = first_txoutindex + output_count;
|
||||
let end = first_txout_index + output_count;
|
||||
self.indexer.vecs.outputs.value.collect_range_into_at(
|
||||
first_txoutindex,
|
||||
first_txout_index,
|
||||
end,
|
||||
&mut self.values_buf,
|
||||
);
|
||||
self.indexer.vecs.outputs.outputtype.collect_range_into_at(
|
||||
first_txoutindex,
|
||||
self.indexer.vecs.outputs.output_type.collect_range_into_at(
|
||||
first_txout_index,
|
||||
end,
|
||||
&mut self.outputtypes_buf,
|
||||
&mut self.output_types_buf,
|
||||
);
|
||||
self.indexer.vecs.outputs.typeindex.collect_range_into_at(
|
||||
first_txoutindex,
|
||||
self.indexer.vecs.outputs.type_index.collect_range_into_at(
|
||||
first_txout_index,
|
||||
end,
|
||||
&mut self.typeindexes_buf,
|
||||
&mut self.type_indexes_buf,
|
||||
);
|
||||
|
||||
self.txoutdata_buf.clear();
|
||||
self.txoutdata_buf.extend(
|
||||
self.txout_data_buf.clear();
|
||||
self.txout_data_buf.extend(
|
||||
self.values_buf
|
||||
.iter()
|
||||
.zip(&self.outputtypes_buf)
|
||||
.zip(&self.typeindexes_buf)
|
||||
.map(|((&value, &outputtype), &typeindex)| TxOutData {
|
||||
.zip(&self.output_types_buf)
|
||||
.zip(&self.type_indexes_buf)
|
||||
.map(|((&value, &output_type), &type_index)| TxOutData {
|
||||
value,
|
||||
outputtype,
|
||||
typeindex,
|
||||
output_type,
|
||||
type_index,
|
||||
}),
|
||||
);
|
||||
&self.txoutdata_buf
|
||||
&self.txout_data_buf
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,59 +82,59 @@ impl<'a> TxOutReaders<'a> {
|
||||
pub struct TxInReaders<'a> {
|
||||
indexer: &'a Indexer,
|
||||
txins: &'a inputs::Vecs,
|
||||
txindex_to_height: &'a mut RangeMap<TxIndex, Height>,
|
||||
tx_index_to_height: &'a mut RangeMap<TxIndex, Height>,
|
||||
outpoints_buf: Vec<OutPoint>,
|
||||
values_buf: Vec<Sats>,
|
||||
prev_heights_buf: Vec<Height>,
|
||||
outputtypes_buf: Vec<OutputType>,
|
||||
typeindexes_buf: Vec<TypeIndex>,
|
||||
output_types_buf: Vec<OutputType>,
|
||||
type_indexes_buf: Vec<TypeIndex>,
|
||||
}
|
||||
|
||||
impl<'a> TxInReaders<'a> {
|
||||
pub(crate) fn new(
|
||||
indexer: &'a Indexer,
|
||||
txins: &'a inputs::Vecs,
|
||||
txindex_to_height: &'a mut RangeMap<TxIndex, Height>,
|
||||
tx_index_to_height: &'a mut RangeMap<TxIndex, Height>,
|
||||
) -> Self {
|
||||
Self {
|
||||
indexer,
|
||||
txins,
|
||||
txindex_to_height,
|
||||
tx_index_to_height,
|
||||
outpoints_buf: Vec::new(),
|
||||
values_buf: Vec::new(),
|
||||
prev_heights_buf: Vec::new(),
|
||||
outputtypes_buf: Vec::new(),
|
||||
typeindexes_buf: Vec::new(),
|
||||
output_types_buf: Vec::new(),
|
||||
type_indexes_buf: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect input data for a block range using bulk reads with buffer reuse.
|
||||
pub(crate) fn collect_block_inputs(
|
||||
&mut self,
|
||||
first_txinindex: usize,
|
||||
first_txin_index: usize,
|
||||
input_count: usize,
|
||||
current_height: Height,
|
||||
) -> (&[Sats], &[Height], &[OutputType], &[TypeIndex]) {
|
||||
let end = first_txinindex + input_count;
|
||||
let end = first_txin_index + input_count;
|
||||
self.txins.spent.value.collect_range_into_at(
|
||||
first_txinindex,
|
||||
first_txin_index,
|
||||
end,
|
||||
&mut self.values_buf,
|
||||
);
|
||||
self.indexer.vecs.inputs.outpoint.collect_range_into_at(
|
||||
first_txinindex,
|
||||
first_txin_index,
|
||||
end,
|
||||
&mut self.outpoints_buf,
|
||||
);
|
||||
self.indexer.vecs.inputs.outputtype.collect_range_into_at(
|
||||
first_txinindex,
|
||||
self.indexer.vecs.inputs.output_type.collect_range_into_at(
|
||||
first_txin_index,
|
||||
end,
|
||||
&mut self.outputtypes_buf,
|
||||
&mut self.output_types_buf,
|
||||
);
|
||||
self.indexer.vecs.inputs.typeindex.collect_range_into_at(
|
||||
first_txinindex,
|
||||
self.indexer.vecs.inputs.type_index.collect_range_into_at(
|
||||
first_txin_index,
|
||||
end,
|
||||
&mut self.typeindexes_buf,
|
||||
&mut self.type_indexes_buf,
|
||||
);
|
||||
|
||||
self.prev_heights_buf.clear();
|
||||
@@ -143,8 +143,8 @@ impl<'a> TxInReaders<'a> {
|
||||
if outpoint.is_coinbase() {
|
||||
current_height
|
||||
} else {
|
||||
self.txindex_to_height
|
||||
.get(outpoint.txindex())
|
||||
self.tx_index_to_height
|
||||
.get(outpoint.tx_index())
|
||||
.unwrap_or(current_height)
|
||||
}
|
||||
}),
|
||||
@@ -153,16 +153,16 @@ impl<'a> TxInReaders<'a> {
|
||||
(
|
||||
&self.values_buf,
|
||||
&self.prev_heights_buf,
|
||||
&self.outputtypes_buf,
|
||||
&self.typeindexes_buf,
|
||||
&self.output_types_buf,
|
||||
&self.type_indexes_buf,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Cached readers for stateful vectors.
|
||||
pub struct VecsReaders {
|
||||
pub addresstypeindex_to_anyaddressindex: ByAddressType<Reader>,
|
||||
pub anyaddressindex_to_anyaddressdata: ByAnyAddress<Reader>,
|
||||
pub address_type_index_to_any_address_index: ByAddressType<Reader>,
|
||||
pub any_address_index_to_any_address_data: ByAnyAddress<Reader>,
|
||||
}
|
||||
|
||||
impl VecsReaders {
|
||||
@@ -171,7 +171,7 @@ impl VecsReaders {
|
||||
addresses_data: &AddressesDataVecs,
|
||||
) -> Self {
|
||||
Self {
|
||||
addresstypeindex_to_anyaddressindex: ByAddressType {
|
||||
address_type_index_to_any_address_index: ByAddressType {
|
||||
p2a: any_address_indexes.p2a.create_reader(),
|
||||
p2pk33: any_address_indexes.p2pk33.create_reader(),
|
||||
p2pk65: any_address_indexes.p2pk65.create_reader(),
|
||||
@@ -181,7 +181,7 @@ impl VecsReaders {
|
||||
p2wpkh: any_address_indexes.p2wpkh.create_reader(),
|
||||
p2wsh: any_address_indexes.p2wsh.create_reader(),
|
||||
},
|
||||
anyaddressindex_to_anyaddressdata: ByAnyAddress {
|
||||
any_address_index_to_any_address_data: ByAnyAddress {
|
||||
funded: addresses_data.funded.create_reader(),
|
||||
empty: addresses_data.empty.create_reader(),
|
||||
},
|
||||
@@ -190,13 +190,13 @@ impl VecsReaders {
|
||||
|
||||
/// Get reader for specific address type.
|
||||
pub(crate) fn address_reader(&self, address_type: OutputType) -> &Reader {
|
||||
self.addresstypeindex_to_anyaddressindex
|
||||
self.address_type_index_to_any_address_index
|
||||
.get(address_type)
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Reusable buffers for per-block txindex mapping construction.
|
||||
/// Reusable buffers for per-block tx_index mapping construction.
|
||||
pub(crate) struct IndexToTxIndexBuf {
|
||||
counts: Vec<StoredU64>,
|
||||
result: Vec<TxIndex>,
|
||||
@@ -210,15 +210,15 @@ impl IndexToTxIndexBuf {
|
||||
}
|
||||
}
|
||||
|
||||
/// Build index -> txindex mapping for a block, reusing internal buffers.
|
||||
/// Build index -> tx_index mapping for a block, reusing internal buffers.
|
||||
pub(crate) fn build(
|
||||
&mut self,
|
||||
block_first_txindex: TxIndex,
|
||||
block_first_tx_index: TxIndex,
|
||||
block_tx_count: u64,
|
||||
txindex_to_count: &impl ReadableVec<TxIndex, StoredU64>,
|
||||
tx_index_to_count: &impl ReadableVec<TxIndex, StoredU64>,
|
||||
) -> &[TxIndex] {
|
||||
let first = block_first_txindex.to_usize();
|
||||
txindex_to_count.collect_range_into_at(
|
||||
let first = block_first_tx_index.to_usize();
|
||||
tx_index_to_count.collect_range_into_at(
|
||||
first,
|
||||
first + block_tx_count as usize,
|
||||
&mut self.counts,
|
||||
@@ -229,9 +229,9 @@ impl IndexToTxIndexBuf {
|
||||
self.result.reserve(total as usize);
|
||||
|
||||
for (offset, count) in self.counts.iter().enumerate() {
|
||||
let txindex = TxIndex::from(first + offset);
|
||||
let tx_index = TxIndex::from(first + offset);
|
||||
self.result
|
||||
.extend(std::iter::repeat_n(txindex, u64::from(*count) as usize));
|
||||
.extend(std::iter::repeat_n(tx_index, u64::from(*count) as usize));
|
||||
}
|
||||
|
||||
&self.result
|
||||
|
||||
@@ -34,16 +34,16 @@ impl<R: RealizedOps> AddressCohortState<R> {
|
||||
|
||||
pub(crate) fn send(
|
||||
&mut self,
|
||||
addressdata: &mut FundedAddressData,
|
||||
address_data: &mut FundedAddressData,
|
||||
value: Sats,
|
||||
current_price: Cents,
|
||||
prev_price: Cents,
|
||||
ath: Cents,
|
||||
age: Age,
|
||||
) -> Result<()> {
|
||||
let prev = addressdata.cost_basis_snapshot();
|
||||
addressdata.send(value, prev_price)?;
|
||||
let current = addressdata.cost_basis_snapshot();
|
||||
let prev = address_data.cost_basis_snapshot();
|
||||
address_data.send(value, prev_price)?;
|
||||
let current = address_data.cost_basis_snapshot();
|
||||
|
||||
self.inner.send_address(
|
||||
&SupplyState {
|
||||
@@ -83,14 +83,14 @@ impl<R: RealizedOps> AddressCohortState<R> {
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn add(&mut self, addressdata: &FundedAddressData) {
|
||||
pub(crate) fn add(&mut self, address_data: &FundedAddressData) {
|
||||
self.address_count += 1;
|
||||
self.inner
|
||||
.increment_snapshot(&addressdata.cost_basis_snapshot());
|
||||
.increment_snapshot(&address_data.cost_basis_snapshot());
|
||||
}
|
||||
|
||||
pub(crate) fn subtract(&mut self, addressdata: &FundedAddressData) {
|
||||
let snapshot = addressdata.cost_basis_snapshot();
|
||||
pub(crate) fn subtract(&mut self, address_data: &FundedAddressData) {
|
||||
let snapshot = address_data.cost_basis_snapshot();
|
||||
|
||||
// Check for potential underflow before it happens
|
||||
if unlikely(self.inner.supply.utxo_count < snapshot.supply_state.utxo_count) {
|
||||
@@ -103,7 +103,7 @@ impl<R: RealizedOps> AddressCohortState<R> {
|
||||
This means the address is not properly tracked in this cohort.",
|
||||
self.address_count,
|
||||
self.inner.supply,
|
||||
addressdata,
|
||||
address_data,
|
||||
snapshot.supply_state,
|
||||
snapshot.realized_price
|
||||
);
|
||||
@@ -118,7 +118,7 @@ impl<R: RealizedOps> AddressCohortState<R> {
|
||||
This means the address is not properly tracked in this cohort.",
|
||||
self.address_count,
|
||||
self.inner.supply,
|
||||
addressdata,
|
||||
address_data,
|
||||
snapshot.supply_state,
|
||||
snapshot.realized_price
|
||||
);
|
||||
@@ -129,7 +129,7 @@ impl<R: RealizedOps> AddressCohortState<R> {
|
||||
"AddressCohortState::subtract address_count underflow! address_count=0\n\
|
||||
Address being subtracted: {}\n\
|
||||
Realized price: {}",
|
||||
addressdata, snapshot.realized_price
|
||||
address_data, snapshot.realized_price
|
||||
)
|
||||
});
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ use crate::{
|
||||
state::BlockState,
|
||||
},
|
||||
indexes, inputs,
|
||||
internal::{CachedWindowStarts, PerBlockCumulative, db_utils::{finalize_db, open_db}},
|
||||
internal::{CachedWindowStarts, PerBlockCumulativeWithSums, db_utils::{finalize_db, open_db}},
|
||||
outputs, prices, transactions,
|
||||
};
|
||||
|
||||
@@ -70,16 +70,16 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
#[traversable(wrap = "cohorts", rename = "address")]
|
||||
pub address_cohorts: AddressCohorts<M>,
|
||||
#[traversable(wrap = "cointime")]
|
||||
pub coinblocks_destroyed: PerBlockCumulative<StoredF64, M>,
|
||||
pub coinblocks_destroyed: PerBlockCumulativeWithSums<StoredF64, StoredF64, M>,
|
||||
pub addresses: AddressMetricsVecs<M>,
|
||||
|
||||
/// In-memory block state for UTXO processing. Persisted via supply_state.
|
||||
/// Kept across compute() calls to avoid O(n) rebuild on resume.
|
||||
#[traversable(skip)]
|
||||
chain_state: Vec<BlockState>,
|
||||
/// In-memory txindex→height reverse lookup. Kept across compute() calls.
|
||||
/// In-memory tx_index→height reverse lookup. Kept across compute() calls.
|
||||
#[traversable(skip)]
|
||||
txindex_to_height: RangeMap<TxIndex, Height>,
|
||||
tx_index_to_height: RangeMap<TxIndex, Height>,
|
||||
|
||||
/// Cached height→price mapping. Incrementally extended, O(new_blocks) on resume.
|
||||
#[traversable(skip)]
|
||||
@@ -114,12 +114,12 @@ impl Vecs {
|
||||
let address_cohorts = AddressCohorts::forced_import(&db, version, indexes, &states_path, cached_starts)?;
|
||||
|
||||
// Create address data BytesVecs first so we can also use them for identity mappings
|
||||
let fundedaddressindex_to_fundedaddressdata = BytesVec::forced_import_with(
|
||||
vecdb::ImportOptions::new(&db, "fundedaddressdata", version)
|
||||
let funded_address_index_to_funded_address_data = BytesVec::forced_import_with(
|
||||
vecdb::ImportOptions::new(&db, "funded_address_data", version)
|
||||
.with_saved_stamped_changes(SAVED_STAMPED_CHANGES),
|
||||
)?;
|
||||
let emptyaddressindex_to_emptyaddressdata = BytesVec::forced_import_with(
|
||||
vecdb::ImportOptions::new(&db, "emptyaddressdata", version)
|
||||
let empty_address_index_to_empty_address_data = BytesVec::forced_import_with(
|
||||
vecdb::ImportOptions::new(&db, "empty_address_data", version)
|
||||
.with_saved_stamped_changes(SAVED_STAMPED_CHANGES),
|
||||
)?;
|
||||
|
||||
@@ -127,13 +127,13 @@ impl Vecs {
|
||||
let funded_address_index = LazyVecFrom1::init(
|
||||
"funded_address_index",
|
||||
version,
|
||||
fundedaddressindex_to_fundedaddressdata.read_only_boxed_clone(),
|
||||
funded_address_index_to_funded_address_data.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
);
|
||||
let empty_address_index = LazyVecFrom1::init(
|
||||
"empty_address_index",
|
||||
version,
|
||||
emptyaddressindex_to_emptyaddressdata.read_only_boxed_clone(),
|
||||
empty_address_index_to_empty_address_data.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
);
|
||||
|
||||
@@ -173,20 +173,21 @@ impl Vecs {
|
||||
utxo_cohorts,
|
||||
address_cohorts,
|
||||
|
||||
coinblocks_destroyed: PerBlockCumulative::forced_import(
|
||||
coinblocks_destroyed: PerBlockCumulativeWithSums::forced_import(
|
||||
&db,
|
||||
"coinblocks_destroyed",
|
||||
version + Version::TWO,
|
||||
indexes,
|
||||
cached_starts,
|
||||
)?,
|
||||
|
||||
any_address_indexes: AnyAddressIndexesVecs::forced_import(&db, version)?,
|
||||
addresses_data: AddressesDataVecs {
|
||||
funded: fundedaddressindex_to_fundedaddressdata,
|
||||
empty: emptyaddressindex_to_emptyaddressdata,
|
||||
funded: funded_address_index_to_funded_address_data,
|
||||
empty: empty_address_index_to_empty_address_data,
|
||||
},
|
||||
chain_state: Vec::new(),
|
||||
txindex_to_height: RangeMap::default(),
|
||||
tx_index_to_height: RangeMap::default(),
|
||||
|
||||
cached_prices: Vec::new(),
|
||||
cached_timestamps: Vec::new(),
|
||||
@@ -294,9 +295,9 @@ impl Vecs {
|
||||
|
||||
debug!("recovered_height={}", recovered_height);
|
||||
|
||||
// Take chain_state and txindex_to_height out of self to avoid borrow conflicts
|
||||
// Take chain_state and tx_index_to_height out of self to avoid borrow conflicts
|
||||
let mut chain_state = std::mem::take(&mut self.chain_state);
|
||||
let mut txindex_to_height = std::mem::take(&mut self.txindex_to_height);
|
||||
let mut tx_index_to_height = std::mem::take(&mut self.tx_index_to_height);
|
||||
|
||||
// Recover or reuse chain_state
|
||||
let starting_height = if recovered_height.is_zero() {
|
||||
@@ -312,7 +313,7 @@ impl Vecs {
|
||||
)?;
|
||||
|
||||
chain_state.clear();
|
||||
txindex_to_height.truncate(0);
|
||||
tx_index_to_height.truncate(0);
|
||||
|
||||
info!("State recovery: fresh start");
|
||||
Height::ZERO
|
||||
@@ -341,7 +342,7 @@ impl Vecs {
|
||||
debug!("chain_state rebuilt");
|
||||
|
||||
// Truncate RangeMap to match (entries are immutable, safe to keep)
|
||||
txindex_to_height.truncate(end);
|
||||
tx_index_to_height.truncate(end);
|
||||
|
||||
recovered_height
|
||||
};
|
||||
@@ -384,7 +385,7 @@ impl Vecs {
|
||||
starting_height,
|
||||
last_height,
|
||||
&mut chain_state,
|
||||
&mut txindex_to_height,
|
||||
&mut tx_index_to_height,
|
||||
&cached_prices,
|
||||
&cached_timestamps,
|
||||
&cached_price_range_max,
|
||||
@@ -396,9 +397,9 @@ impl Vecs {
|
||||
self.cached_price_range_max = cached_price_range_max;
|
||||
}
|
||||
|
||||
// Put chain_state and txindex_to_height back
|
||||
// Put chain_state and tx_index_to_height back
|
||||
self.chain_state = chain_state;
|
||||
self.txindex_to_height = txindex_to_height;
|
||||
self.tx_index_to_height = tx_index_to_height;
|
||||
|
||||
// 5. Compute aggregates (overlapping cohorts from separate cohorts)
|
||||
info!("Computing overlapping cohorts...");
|
||||
@@ -426,7 +427,7 @@ impl Vecs {
|
||||
r2?;
|
||||
}
|
||||
|
||||
// 6b. Compute address count sum (by addresstype → all)
|
||||
// 6b. Compute address count sum (by address_type → all)
|
||||
self.addresses.funded.compute_rest(starting_indexes, exit)?;
|
||||
self.addresses.empty.compute_rest(starting_indexes, exit)?;
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ impl Vecs {
|
||||
Self {
|
||||
p2pk33: P2PK33Vecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
"p2pk33addressindex",
|
||||
"p2pk33_address_index",
|
||||
version,
|
||||
indexer.vecs.addresses.p2pk33.bytes.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
@@ -100,7 +100,7 @@ impl Vecs {
|
||||
},
|
||||
p2pk65: P2PK65Vecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
"p2pk65addressindex",
|
||||
"p2pk65_address_index",
|
||||
version,
|
||||
indexer.vecs.addresses.p2pk65.bytes.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
@@ -108,7 +108,7 @@ impl Vecs {
|
||||
},
|
||||
p2pkh: P2PKHVecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
"p2pkhaddressindex",
|
||||
"p2pkh_address_index",
|
||||
version,
|
||||
indexer.vecs.addresses.p2pkh.bytes.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
@@ -116,7 +116,7 @@ impl Vecs {
|
||||
},
|
||||
p2sh: P2SHVecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
"p2shaddressindex",
|
||||
"p2sh_address_index",
|
||||
version,
|
||||
indexer.vecs.addresses.p2sh.bytes.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
@@ -124,7 +124,7 @@ impl Vecs {
|
||||
},
|
||||
p2tr: P2TRVecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
"p2traddressindex",
|
||||
"p2tr_address_index",
|
||||
version,
|
||||
indexer.vecs.addresses.p2tr.bytes.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
@@ -132,7 +132,7 @@ impl Vecs {
|
||||
},
|
||||
p2wpkh: P2WPKHVecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
"p2wpkhaddressindex",
|
||||
"p2wpkh_address_index",
|
||||
version,
|
||||
indexer.vecs.addresses.p2wpkh.bytes.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
@@ -140,7 +140,7 @@ impl Vecs {
|
||||
},
|
||||
p2wsh: P2WSHVecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
"p2wshaddressindex",
|
||||
"p2wsh_address_index",
|
||||
version,
|
||||
indexer.vecs.addresses.p2wsh.bytes.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
@@ -148,7 +148,7 @@ impl Vecs {
|
||||
},
|
||||
p2a: P2AVecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
"p2aaddressindex",
|
||||
"p2a_address_index",
|
||||
version,
|
||||
indexer.vecs.addresses.p2a.bytes.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
@@ -156,44 +156,44 @@ impl Vecs {
|
||||
},
|
||||
p2ms: P2MSVecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
"p2msoutputindex",
|
||||
"p2ms_output_index",
|
||||
version,
|
||||
indexer.vecs.scripts.p2ms.to_txindex.read_only_boxed_clone(),
|
||||
indexer.vecs.scripts.p2ms.to_tx_index.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
),
|
||||
},
|
||||
empty: EmptyVecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
"emptyoutputindex",
|
||||
"empty_output_index",
|
||||
version,
|
||||
indexer
|
||||
.vecs
|
||||
.scripts
|
||||
.empty.to_txindex
|
||||
.empty.to_tx_index
|
||||
.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
),
|
||||
},
|
||||
unknown: UnknownVecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
"unknownoutputindex",
|
||||
"unknown_output_index",
|
||||
version,
|
||||
indexer
|
||||
.vecs
|
||||
.scripts
|
||||
.unknown.to_txindex
|
||||
.unknown.to_tx_index
|
||||
.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
),
|
||||
},
|
||||
opreturn: OpReturnVecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
"opreturnindex",
|
||||
"op_return_index",
|
||||
version,
|
||||
indexer
|
||||
.vecs
|
||||
.scripts
|
||||
.opreturn.to_txindex
|
||||
.opreturn.to_tx_index
|
||||
.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
),
|
||||
|
||||
@@ -25,7 +25,7 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub month6: M::Stored<EagerVec<PcoVec<Height, Month6>>>,
|
||||
pub year1: M::Stored<EagerVec<PcoVec<Height, Year1>>>,
|
||||
pub year10: M::Stored<EagerVec<PcoVec<Height, Year10>>>,
|
||||
pub txindex_count: M::Stored<EagerVec<PcoVec<Height, StoredU64>>>,
|
||||
pub tx_index_count: M::Stored<EagerVec<PcoVec<Height, StoredU64>>>,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
@@ -47,7 +47,7 @@ impl Vecs {
|
||||
month6: EagerVec::forced_import(db, "month6", version)?,
|
||||
year1: EagerVec::forced_import(db, "year1", version)?,
|
||||
year10: EagerVec::forced_import(db, "year10", version)?,
|
||||
txindex_count: EagerVec::forced_import(db, "txindex_count", version)?,
|
||||
tx_index_count: EagerVec::forced_import(db, "tx_index_count", version)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@ mod minute30;
|
||||
mod month1;
|
||||
mod month3;
|
||||
mod month6;
|
||||
mod txindex;
|
||||
mod txinindex;
|
||||
mod txoutindex;
|
||||
mod tx_index;
|
||||
mod txin_index;
|
||||
mod txout_index;
|
||||
mod week1;
|
||||
mod year1;
|
||||
mod year10;
|
||||
@@ -51,9 +51,9 @@ pub use minute30::Vecs as Minute30Vecs;
|
||||
pub use month1::Vecs as Month1Vecs;
|
||||
pub use month3::Vecs as Month3Vecs;
|
||||
pub use month6::Vecs as Month6Vecs;
|
||||
pub use txindex::Vecs as TxIndexVecs;
|
||||
pub use txinindex::Vecs as TxInIndexVecs;
|
||||
pub use txoutindex::Vecs as TxOutIndexVecs;
|
||||
pub use tx_index::Vecs as TxIndexVecs;
|
||||
pub use txin_index::Vecs as TxInIndexVecs;
|
||||
pub use txout_index::Vecs as TxOutIndexVecs;
|
||||
pub use week1::Vecs as Week1Vecs;
|
||||
pub use year1::Vecs as Year1Vecs;
|
||||
pub use year10::Vecs as Year10Vecs;
|
||||
@@ -82,9 +82,9 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub month6: Month6Vecs<M>,
|
||||
pub year1: Year1Vecs<M>,
|
||||
pub year10: Year10Vecs<M>,
|
||||
pub txindex: TxIndexVecs<M>,
|
||||
pub txinindex: TxInIndexVecs,
|
||||
pub txoutindex: TxOutIndexVecs,
|
||||
pub tx_index: TxIndexVecs<M>,
|
||||
pub txin_index: TxInIndexVecs,
|
||||
pub txout_index: TxOutIndexVecs,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
@@ -114,9 +114,9 @@ impl Vecs {
|
||||
let month6 = Month6Vecs::forced_import(&db, version)?;
|
||||
let year1 = Year1Vecs::forced_import(&db, version)?;
|
||||
let year10 = Year10Vecs::forced_import(&db, version)?;
|
||||
let txindex = TxIndexVecs::forced_import(&db, version, indexer)?;
|
||||
let txinindex = TxInIndexVecs::forced_import(version, indexer);
|
||||
let txoutindex = TxOutIndexVecs::forced_import(version, indexer);
|
||||
let tx_index = TxIndexVecs::forced_import(&db, version, indexer)?;
|
||||
let txin_index = TxInIndexVecs::forced_import(version, indexer);
|
||||
let txout_index = TxOutIndexVecs::forced_import(version, indexer);
|
||||
|
||||
let cached_mappings = CachedMappings {
|
||||
minute10_first_height: CachedVec::new(&minute10.first_height),
|
||||
@@ -155,9 +155,9 @@ impl Vecs {
|
||||
month6,
|
||||
year1,
|
||||
year10,
|
||||
txindex,
|
||||
txinindex,
|
||||
txoutindex,
|
||||
tx_index,
|
||||
txin_index,
|
||||
txout_index,
|
||||
db,
|
||||
};
|
||||
|
||||
@@ -221,15 +221,15 @@ impl Vecs {
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.txindex.input_count.compute_count_from_indexes(
|
||||
starting_indexes.txindex,
|
||||
&indexer.vecs.transactions.first_txinindex,
|
||||
self.tx_index.input_count.compute_count_from_indexes(
|
||||
starting_indexes.tx_index,
|
||||
&indexer.vecs.transactions.first_txin_index,
|
||||
&indexer.vecs.inputs.outpoint,
|
||||
exit,
|
||||
)?;
|
||||
self.txindex.output_count.compute_count_from_indexes(
|
||||
starting_indexes.txindex,
|
||||
&indexer.vecs.transactions.first_txoutindex,
|
||||
self.tx_index.output_count.compute_count_from_indexes(
|
||||
starting_indexes.tx_index,
|
||||
&indexer.vecs.transactions.first_txout_index,
|
||||
&indexer.vecs.outputs.value,
|
||||
exit,
|
||||
)?;
|
||||
@@ -242,9 +242,9 @@ impl Vecs {
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.height.txindex_count.compute_count_from_indexes(
|
||||
self.height.tx_index_count.compute_count_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.transactions.first_txindex,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
&indexer.vecs.transactions.txid,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -22,7 +22,7 @@ impl Vecs {
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
identity: LazyVecFrom1::init(
|
||||
"txindex",
|
||||
"tx_index",
|
||||
version,
|
||||
indexer.vecs.transactions.txid.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
|
||||
@@ -12,7 +12,7 @@ impl Vecs {
|
||||
pub(crate) fn forced_import(version: Version, indexer: &Indexer) -> Self {
|
||||
Self {
|
||||
identity: LazyVecFrom1::init(
|
||||
"txinindex",
|
||||
"txin_index",
|
||||
version,
|
||||
indexer.vecs.inputs.outpoint.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
|
||||
@@ -12,7 +12,7 @@ impl Vecs {
|
||||
pub(crate) fn forced_import(version: Version, indexer: &Indexer) -> Self {
|
||||
Self {
|
||||
identity: LazyVecFrom1::init(
|
||||
"txoutindex",
|
||||
"txout_index",
|
||||
version,
|
||||
indexer.vecs.outputs.value.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
|
||||
@@ -20,9 +20,9 @@ impl Vecs {
|
||||
.compute(starting_indexes.height, &window_starts, exit, |full| {
|
||||
full.compute_with_skip(
|
||||
starting_indexes.height,
|
||||
&indexes.txindex.input_count,
|
||||
&indexer.vecs.transactions.first_txindex,
|
||||
&indexes.height.txindex_count,
|
||||
&indexes.tx_index.input_count,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
&indexes.height.tx_index_count,
|
||||
exit,
|
||||
0,
|
||||
)
|
||||
|
||||
@@ -18,9 +18,9 @@ impl Vecs {
|
||||
) -> Result<()> {
|
||||
// Validate computed versions against dependencies
|
||||
let dep_version = indexer.vecs.inputs.outpoint.version()
|
||||
+ indexer.vecs.transactions.first_txoutindex.version()
|
||||
+ indexer.vecs.transactions.first_txout_index.version()
|
||||
+ indexer.vecs.outputs.value.version();
|
||||
self.txoutindex
|
||||
self.txout_index
|
||||
.validate_computed_version_or_reset(dep_version)?;
|
||||
self.value.validate_computed_version_or_reset(dep_version)?;
|
||||
|
||||
@@ -29,21 +29,21 @@ impl Vecs {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let len1 = self.txoutindex.len();
|
||||
let len1 = self.txout_index.len();
|
||||
let len2 = self.value.len();
|
||||
let starting = starting_indexes.txinindex.to_usize();
|
||||
let starting = starting_indexes.txin_index.to_usize();
|
||||
let min = len1.min(len2).min(starting);
|
||||
|
||||
if min >= target {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let first_txoutindex_reader = indexer.vecs.transactions.first_txoutindex.reader();
|
||||
let first_txout_index_reader = indexer.vecs.transactions.first_txout_index.reader();
|
||||
let value_reader = indexer.vecs.outputs.value.reader();
|
||||
let actual_total = target - min;
|
||||
let mut entries: Vec<Entry> = Vec::with_capacity(actual_total.min(BATCH_SIZE));
|
||||
// Pre-allocate output buffers for scatter-write pattern
|
||||
let mut out_txoutindex: Vec<TxOutIndex> = Vec::new();
|
||||
let mut out_txout_index: Vec<TxOutIndex> = Vec::new();
|
||||
let mut out_value: Vec<Sats> = Vec::new();
|
||||
|
||||
let mut batch_start = min;
|
||||
@@ -60,49 +60,49 @@ impl Vecs {
|
||||
.for_each_range_at(batch_start, batch_end, |outpoint| {
|
||||
entries.push(Entry {
|
||||
original_idx: j,
|
||||
txindex: outpoint.txindex(),
|
||||
tx_index: outpoint.tx_index(),
|
||||
vout: outpoint.vout(),
|
||||
txoutindex: TxOutIndex::COINBASE,
|
||||
txout_index: TxOutIndex::COINBASE,
|
||||
value: Sats::MAX,
|
||||
});
|
||||
j += 1;
|
||||
});
|
||||
|
||||
// Sort 1: by txindex (group by transaction for sequential first_txoutindex reads)
|
||||
entries.sort_unstable_by_key(|e| e.txindex);
|
||||
// Sort 1: by tx_index (group by transaction for sequential first_txout_index reads)
|
||||
entries.sort_unstable_by_key(|e| e.tx_index);
|
||||
for entry in &mut entries {
|
||||
if entry.txindex.is_coinbase() {
|
||||
if entry.tx_index.is_coinbase() {
|
||||
break;
|
||||
}
|
||||
entry.txoutindex =
|
||||
first_txoutindex_reader.get(entry.txindex.to_usize()) + entry.vout;
|
||||
entry.txout_index =
|
||||
first_txout_index_reader.get(entry.tx_index.to_usize()) + entry.vout;
|
||||
}
|
||||
|
||||
// Sort 2: by txoutindex (sequential value reads)
|
||||
entries.sort_unstable_by_key(|e| e.txoutindex);
|
||||
// Sort 2: by txout_index (sequential value reads)
|
||||
entries.sort_unstable_by_key(|e| e.txout_index);
|
||||
for entry in &mut entries {
|
||||
if entry.txoutindex.is_coinbase() {
|
||||
if entry.txout_index.is_coinbase() {
|
||||
break;
|
||||
}
|
||||
entry.value = value_reader.get(entry.txoutindex.to_usize());
|
||||
entry.value = value_reader.get(entry.txout_index.to_usize());
|
||||
}
|
||||
|
||||
// Scatter-write to output buffers using original_idx (avoids Sort 3)
|
||||
out_txoutindex.clear();
|
||||
out_txoutindex.resize(batch_len, TxOutIndex::COINBASE);
|
||||
out_txout_index.clear();
|
||||
out_txout_index.resize(batch_len, TxOutIndex::COINBASE);
|
||||
out_value.clear();
|
||||
out_value.resize(batch_len, Sats::MAX);
|
||||
|
||||
for entry in &entries {
|
||||
out_txoutindex[entry.original_idx] = entry.txoutindex;
|
||||
out_txout_index[entry.original_idx] = entry.txout_index;
|
||||
out_value[entry.original_idx] = entry.value;
|
||||
}
|
||||
|
||||
for i in 0..batch_len {
|
||||
let txinindex = TxInIndex::from(batch_start + i);
|
||||
self.txoutindex
|
||||
.truncate_push(txinindex, out_txoutindex[i])?;
|
||||
self.value.truncate_push(txinindex, out_value[i])?;
|
||||
let txin_index = TxInIndex::from(batch_start + i);
|
||||
self.txout_index
|
||||
.truncate_push(txin_index, out_txout_index[i])?;
|
||||
self.value.truncate_push(txin_index, out_value[i])?;
|
||||
}
|
||||
|
||||
if batch_end < target {
|
||||
@@ -110,7 +110,7 @@ impl Vecs {
|
||||
}
|
||||
|
||||
let _lock = exit.lock();
|
||||
self.txoutindex.write()?;
|
||||
self.txout_index.write()?;
|
||||
self.value.write()?;
|
||||
db.flush()?;
|
||||
|
||||
@@ -123,8 +123,8 @@ impl Vecs {
|
||||
|
||||
struct Entry {
|
||||
original_idx: usize,
|
||||
txindex: TxIndex,
|
||||
tx_index: TxIndex,
|
||||
vout: Vout,
|
||||
txoutindex: TxOutIndex,
|
||||
txout_index: TxOutIndex,
|
||||
value: Sats,
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use super::Vecs;
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
txoutindex: PcoVec::forced_import(db, "txoutindex", version)?,
|
||||
txout_index: PcoVec::forced_import(db, "txout_index", version)?,
|
||||
value: PcoVec::forced_import(db, "value", version)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,6 +4,6 @@ use vecdb::{PcoVec, Rw, StorageMode};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub txoutindex: M::Stored<PcoVec<TxInIndex, TxOutIndex>>,
|
||||
pub txout_index: M::Stored<PcoVec<TxInIndex, TxOutIndex>>,
|
||||
pub value: M::Stored<PcoVec<TxInIndex, Sats>>,
|
||||
}
|
||||
|
||||
@@ -62,14 +62,14 @@ where
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
txindex_source: &impl ReadableVec<TxIndex, T>,
|
||||
tx_index_source: &impl ReadableVec<TxIndex, T>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: Copy + Ord + From<f64> + Default,
|
||||
f64: From<T>,
|
||||
{
|
||||
self.derive_from_with_skip(indexer, indexes, starting_indexes, txindex_source, exit, 0)
|
||||
self.derive_from_with_skip(indexer, indexes, starting_indexes, tx_index_source, exit, 0)
|
||||
}
|
||||
|
||||
/// Derive from source, skipping first N transactions per block from per-block stats.
|
||||
@@ -82,7 +82,7 @@ where
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
txindex_source: &impl ReadableVec<TxIndex, T>,
|
||||
tx_index_source: &impl ReadableVec<TxIndex, T>,
|
||||
exit: &Exit,
|
||||
skip_count: usize,
|
||||
) -> Result<()>
|
||||
@@ -93,9 +93,9 @@ where
|
||||
// Per-block distribution (supports skip for coinbase exclusion)
|
||||
self.block.compute_with_skip(
|
||||
starting_indexes.height,
|
||||
txindex_source,
|
||||
&indexer.vecs.transactions.first_txindex,
|
||||
&indexes.height.txindex_count,
|
||||
tx_index_source,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
&indexes.height.tx_index_count,
|
||||
exit,
|
||||
skip_count,
|
||||
)?;
|
||||
@@ -103,9 +103,9 @@ where
|
||||
// 6-block rolling: true distribution from all txs in last 6 blocks
|
||||
self.rolling._6b.compute_from_nblocks(
|
||||
starting_indexes.height,
|
||||
txindex_source,
|
||||
&indexer.vecs.transactions.first_txindex,
|
||||
&indexes.height.txindex_count,
|
||||
tx_index_source,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
&indexes.height.tx_index_count,
|
||||
6,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -20,7 +20,7 @@ pub struct PerTxDistribution<T, M: StorageMode = Rw>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
{
|
||||
pub txindex: M::Stored<EagerVec<PcoVec<TxIndex, T>>>,
|
||||
pub tx_index: M::Stored<EagerVec<PcoVec<TxIndex, T>>>,
|
||||
#[traversable(flatten)]
|
||||
pub distribution: TxDerivedDistribution<T, M>,
|
||||
}
|
||||
@@ -30,10 +30,10 @@ where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result<Self> {
|
||||
let txindex = EagerVec::forced_import(db, name, version)?;
|
||||
let tx_index = EagerVec::forced_import(db, name, version)?;
|
||||
let distribution = TxDerivedDistribution::forced_import(db, name, version)?;
|
||||
Ok(Self {
|
||||
txindex,
|
||||
tx_index,
|
||||
distribution,
|
||||
})
|
||||
}
|
||||
@@ -55,7 +55,7 @@ where
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
&self.txindex,
|
||||
&self.tx_index,
|
||||
exit,
|
||||
skip_count,
|
||||
)
|
||||
|
||||
@@ -17,7 +17,7 @@ where
|
||||
S1: ComputedVecValue,
|
||||
S2: ComputedVecValue,
|
||||
{
|
||||
pub txindex: LazyVecFrom2<TxIndex, T, TxIndex, S1, TxIndex, S2>,
|
||||
pub tx_index: LazyVecFrom2<TxIndex, T, TxIndex, S1, TxIndex, S2>,
|
||||
#[traversable(flatten)]
|
||||
pub distribution: TxDerivedDistribution<T, M>,
|
||||
}
|
||||
@@ -32,11 +32,11 @@ where
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
txindex: LazyVecFrom2<TxIndex, T, TxIndex, S1, TxIndex, S2>,
|
||||
tx_index: LazyVecFrom2<TxIndex, T, TxIndex, S1, TxIndex, S2>,
|
||||
) -> Result<Self> {
|
||||
let distribution = TxDerivedDistribution::forced_import(db, name, version)?;
|
||||
Ok(Self {
|
||||
txindex,
|
||||
tx_index,
|
||||
distribution,
|
||||
})
|
||||
}
|
||||
@@ -54,6 +54,6 @@ where
|
||||
LazyVecFrom2<TxIndex, T, TxIndex, S1, TxIndex, S2>: ReadableVec<TxIndex, T>,
|
||||
{
|
||||
self.distribution
|
||||
.derive_from(indexer, indexes, starting_indexes, &self.txindex, exit)
|
||||
.derive_from(indexer, indexes, starting_indexes, &self.tx_index, exit)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ where
|
||||
S2: ComputedVecValue,
|
||||
DSource: ComputedVecValue,
|
||||
{
|
||||
pub txindex: LazyVecFrom2<TxIndex, T, TxIndex, S1, TxIndex, S2>,
|
||||
pub tx_index: LazyVecFrom2<TxIndex, T, TxIndex, S1, TxIndex, S2>,
|
||||
#[traversable(flatten)]
|
||||
pub distribution: LazyTxDerivedDistribution<T, DSource>,
|
||||
}
|
||||
@@ -30,13 +30,13 @@ where
|
||||
pub(crate) fn new<F: UnaryTransform<DSource, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
txindex: LazyVecFrom2<TxIndex, T, TxIndex, S1, TxIndex, S2>,
|
||||
tx_index: LazyVecFrom2<TxIndex, T, TxIndex, S1, TxIndex, S2>,
|
||||
source_distribution: &TxDerivedDistribution<DSource>,
|
||||
) -> Self {
|
||||
let distribution =
|
||||
LazyTxDerivedDistribution::from_tx_derived::<F>(name, version, source_distribution);
|
||||
Self {
|
||||
txindex,
|
||||
tx_index,
|
||||
distribution,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,26 +24,26 @@ impl Vecs {
|
||||
exit,
|
||||
|vec| {
|
||||
// Cursors avoid per-height PcoVec page decompression for the
|
||||
// tx-indexed lookups. Coinbase txindex values are strictly
|
||||
// tx-indexed lookups. Coinbase tx_index values are strictly
|
||||
// increasing, so the cursors only advance forward.
|
||||
let mut txout_cursor = indexer.vecs.transactions.first_txoutindex.cursor();
|
||||
let mut count_cursor = indexes.txindex.output_count.cursor();
|
||||
let mut txout_cursor = indexer.vecs.transactions.first_txout_index.cursor();
|
||||
let mut count_cursor = indexes.tx_index.output_count.cursor();
|
||||
|
||||
vec.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.transactions.first_txindex,
|
||||
|(height, txindex, ..)| {
|
||||
let ti = txindex.to_usize();
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
|(height, tx_index, ..)| {
|
||||
let ti = tx_index.to_usize();
|
||||
|
||||
txout_cursor.advance(ti - txout_cursor.position());
|
||||
let first_txoutindex = txout_cursor.next().unwrap().to_usize();
|
||||
let first_txout_index = txout_cursor.next().unwrap().to_usize();
|
||||
|
||||
count_cursor.advance(ti - count_cursor.position());
|
||||
let output_count: usize = count_cursor.next().unwrap().into();
|
||||
|
||||
let sats = indexer.vecs.outputs.value.fold_range_at(
|
||||
first_txoutindex,
|
||||
first_txoutindex + output_count,
|
||||
first_txout_index,
|
||||
first_txout_index + output_count,
|
||||
Sats::ZERO,
|
||||
|acc, v| acc + v,
|
||||
);
|
||||
@@ -66,9 +66,9 @@ impl Vecs {
|
||||
|vec| {
|
||||
vec.compute_sum_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.transactions.first_txindex,
|
||||
&indexes.height.txindex_count,
|
||||
&transactions_fees.fee.txindex,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
&indexes.height.tx_index_count,
|
||||
&transactions_fees.fee.tx_index,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
|
||||
@@ -23,9 +23,9 @@ impl Vecs {
|
||||
.compute(starting_indexes.height, &window_starts, exit, |full| {
|
||||
full.compute_with_skip(
|
||||
starting_indexes.height,
|
||||
&indexes.txindex.output_count,
|
||||
&indexer.vecs.transactions.first_txindex,
|
||||
&indexes.height.txindex_count,
|
||||
&indexes.tx_index.output_count,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
&indexes.height.tx_index_count,
|
||||
exit,
|
||||
0,
|
||||
)
|
||||
|
||||
@@ -25,29 +25,29 @@ impl Vecs {
|
||||
let target_height = Height::from(target_height - 1);
|
||||
|
||||
// Find min_height from current vec length
|
||||
let current_txoutindex = self.txinindex.len();
|
||||
let min_txoutindex = current_txoutindex.min(starting_indexes.txoutindex.to_usize());
|
||||
let current_txout_index = self.txin_index.len();
|
||||
let min_txout_index = current_txout_index.min(starting_indexes.txout_index.to_usize());
|
||||
|
||||
let starting_stamp = Stamp::from(starting_indexes.height);
|
||||
let _ = self.txinindex.rollback_before(starting_stamp);
|
||||
let _ = self.txin_index.rollback_before(starting_stamp);
|
||||
|
||||
self.txinindex
|
||||
.truncate_if_needed(TxOutIndex::from(min_txoutindex))?;
|
||||
self.txin_index
|
||||
.truncate_if_needed(TxOutIndex::from(min_txout_index))?;
|
||||
|
||||
let txinindex_to_txoutindex = &inputs.spent.txoutindex;
|
||||
let txin_index_to_txout_index = &inputs.spent.txout_index;
|
||||
|
||||
// Find min_height via binary search (first_txoutindex is monotonically non-decreasing)
|
||||
let first_txoutindex_vec = &indexer.vecs.outputs.first_txoutindex;
|
||||
let min_height = if min_txoutindex == 0 {
|
||||
// Find min_height via binary search (first_txout_index is monotonically non-decreasing)
|
||||
let first_txout_index_vec = &indexer.vecs.outputs.first_txout_index;
|
||||
let min_height = if min_txout_index == 0 {
|
||||
Height::ZERO
|
||||
} else if min_txoutindex >= starting_indexes.txoutindex.to_usize() {
|
||||
} else if min_txout_index >= starting_indexes.txout_index.to_usize() {
|
||||
starting_indexes.height
|
||||
} else {
|
||||
let mut lo = 0usize;
|
||||
let mut hi = starting_indexes.height.to_usize() + 1;
|
||||
while lo < hi {
|
||||
let mid = lo + (hi - lo) / 2;
|
||||
if first_txoutindex_vec.collect_one_at(mid).unwrap().to_usize() <= min_txoutindex {
|
||||
if first_txout_index_vec.collect_one_at(mid).unwrap().to_usize() <= min_txout_index {
|
||||
lo = mid + 1;
|
||||
} else {
|
||||
hi = mid;
|
||||
@@ -58,12 +58,12 @@ impl Vecs {
|
||||
|
||||
// Only collect from min_height onward (not from 0)
|
||||
let offset = min_height.to_usize();
|
||||
let first_txoutindex_data =
|
||||
first_txoutindex_vec.collect_range_at(offset, target_height.to_usize() + 1);
|
||||
let first_txinindex_data = indexer
|
||||
let first_txout_index_data =
|
||||
first_txout_index_vec.collect_range_at(offset, target_height.to_usize() + 1);
|
||||
let first_txin_index_data = indexer
|
||||
.vecs
|
||||
.inputs
|
||||
.first_txinindex
|
||||
.first_txin_index
|
||||
.collect_range_at(offset, target_height.to_usize() + 2);
|
||||
|
||||
// Validate: computed height must not exceed starting height
|
||||
@@ -80,47 +80,47 @@ impl Vecs {
|
||||
while batch_start_height <= target_height {
|
||||
let batch_end_height = (batch_start_height + HEIGHT_BATCH).min(target_height);
|
||||
|
||||
// Fill txoutindex up to batch_end_height + 1
|
||||
let batch_txoutindex = if batch_end_height >= target_height {
|
||||
// Fill txout_index up to batch_end_height + 1
|
||||
let batch_txout_index = if batch_end_height >= target_height {
|
||||
indexer.vecs.outputs.value.len()
|
||||
} else {
|
||||
first_txoutindex_data[batch_end_height.to_usize() + 1 - offset].to_usize()
|
||||
first_txout_index_data[batch_end_height.to_usize() + 1 - offset].to_usize()
|
||||
};
|
||||
self.txinindex
|
||||
.fill_to(batch_txoutindex, TxInIndex::UNSPENT)?;
|
||||
self.txin_index
|
||||
.fill_to(batch_txout_index, TxInIndex::UNSPENT)?;
|
||||
|
||||
// Get txin range for this height batch
|
||||
let txin_start =
|
||||
first_txinindex_data[batch_start_height.to_usize() - offset].to_usize();
|
||||
first_txin_index_data[batch_start_height.to_usize() - offset].to_usize();
|
||||
let txin_end = if batch_end_height >= target_height {
|
||||
inputs.spent.txoutindex.len()
|
||||
inputs.spent.txout_index.len()
|
||||
} else {
|
||||
first_txinindex_data[batch_end_height.to_usize() + 1 - offset].to_usize()
|
||||
first_txin_index_data[batch_end_height.to_usize() + 1 - offset].to_usize()
|
||||
};
|
||||
|
||||
// Stream txins directly into pairs — avoids intermediate Vec allocation
|
||||
pairs.clear();
|
||||
let mut j = txin_start;
|
||||
txinindex_to_txoutindex.for_each_range_at(
|
||||
txin_index_to_txout_index.for_each_range_at(
|
||||
txin_start,
|
||||
txin_end,
|
||||
|txoutindex: TxOutIndex| {
|
||||
if !txoutindex.is_coinbase() {
|
||||
pairs.push((txoutindex, TxInIndex::from(j)));
|
||||
|txout_index: TxOutIndex| {
|
||||
if !txout_index.is_coinbase() {
|
||||
pairs.push((txout_index, TxInIndex::from(j)));
|
||||
}
|
||||
j += 1;
|
||||
},
|
||||
);
|
||||
|
||||
pairs.sort_unstable_by_key(|(txoutindex, _)| *txoutindex);
|
||||
pairs.sort_unstable_by_key(|(txout_index, _)| *txout_index);
|
||||
|
||||
for &(txoutindex, txinindex) in &pairs {
|
||||
self.txinindex.update(txoutindex, txinindex)?;
|
||||
for &(txout_index, txin_index) in &pairs {
|
||||
self.txin_index.update(txout_index, txin_index)?;
|
||||
}
|
||||
|
||||
if batch_end_height < target_height {
|
||||
let _lock = exit.lock();
|
||||
self.txinindex.write()?;
|
||||
self.txin_index.write()?;
|
||||
info!(
|
||||
"TxOuts: {:.2}%",
|
||||
batch_end_height.to_usize() as f64 / target_height.to_usize() as f64 * 100.0
|
||||
@@ -132,7 +132,7 @@ impl Vecs {
|
||||
}
|
||||
|
||||
let _lock = exit.lock();
|
||||
self.txinindex
|
||||
self.txin_index
|
||||
.stamped_write_with_changes(Stamp::from(target_height))?;
|
||||
db.flush()?;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ use super::Vecs;
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
txinindex: BytesVec::forced_import(db, "txinindex", version)?,
|
||||
txin_index: BytesVec::forced_import(db, "txin_index", version)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,5 +4,5 @@ use vecdb::{BytesVec, Rw, StorageMode};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub txinindex: M::Stored<BytesVec<TxOutIndex, TxInIndex>>,
|
||||
pub txin_index: M::Stored<BytesVec<TxOutIndex, TxInIndex>>,
|
||||
}
|
||||
|
||||
@@ -118,9 +118,9 @@ impl Vecs {
|
||||
self.height_to_pool
|
||||
.validate_computed_version_or_reset(indexer.stores.height_to_coinbase_tag.version())?;
|
||||
|
||||
let first_txoutindex = indexer.vecs.transactions.first_txoutindex.reader();
|
||||
let outputtype = indexer.vecs.outputs.outputtype.reader();
|
||||
let typeindex = indexer.vecs.outputs.typeindex.reader();
|
||||
let first_txout_index = indexer.vecs.transactions.first_txout_index.reader();
|
||||
let output_type = indexer.vecs.outputs.output_type.reader();
|
||||
let type_index = indexer.vecs.outputs.type_index.reader();
|
||||
let p2pk65 = indexer.vecs.addresses.p2pk65.bytes.reader();
|
||||
let p2pk33 = indexer.vecs.addresses.p2pk33.bytes.reader();
|
||||
let p2pkh = indexer.vecs.addresses.p2pkh.bytes.reader();
|
||||
@@ -138,11 +138,11 @@ impl Vecs {
|
||||
.min(self.height_to_pool.len());
|
||||
|
||||
// Cursors avoid per-height PcoVec page decompression.
|
||||
// Heights are sequential, txindex values derived from them are monotonically
|
||||
// Heights are sequential, tx_index values derived from them are monotonically
|
||||
// increasing, so both cursors only advance forward.
|
||||
let mut first_txindex_cursor = indexer.vecs.transactions.first_txindex.cursor();
|
||||
first_txindex_cursor.advance(min);
|
||||
let mut output_count_cursor = indexes.txindex.output_count.cursor();
|
||||
let mut first_tx_index_cursor = indexer.vecs.transactions.first_tx_index.cursor();
|
||||
first_tx_index_cursor.advance(min);
|
||||
let mut output_count_cursor = indexes.tx_index.output_count.cursor();
|
||||
|
||||
indexer
|
||||
.stores
|
||||
@@ -150,18 +150,18 @@ impl Vecs {
|
||||
.iter()
|
||||
.skip(min)
|
||||
.try_for_each(|(height, coinbase_tag)| -> Result<()> {
|
||||
let txindex = first_txindex_cursor.next().unwrap();
|
||||
let out_start = first_txoutindex.get(txindex.to_usize());
|
||||
let tx_index = first_tx_index_cursor.next().unwrap();
|
||||
let out_start = first_txout_index.get(tx_index.to_usize());
|
||||
|
||||
let ti = txindex.to_usize();
|
||||
let ti = tx_index.to_usize();
|
||||
output_count_cursor.advance(ti - output_count_cursor.position());
|
||||
let outputcount = output_count_cursor.next().unwrap();
|
||||
let output_count_val = output_count_cursor.next().unwrap();
|
||||
|
||||
let pool = (*out_start..(*out_start + *outputcount))
|
||||
let pool = (*out_start..(*out_start + *output_count_val))
|
||||
.map(TxOutIndex::from)
|
||||
.find_map(|txoutindex| {
|
||||
let ot = outputtype.get(txoutindex.to_usize());
|
||||
let ti = usize::from(typeindex.get(txoutindex.to_usize()));
|
||||
.find_map(|txout_index| {
|
||||
let ot = output_type.get(txout_index.to_usize());
|
||||
let ti = usize::from(type_index.get(txout_index.to_usize()));
|
||||
match ot {
|
||||
OutputType::P2PK65 => Some(AddressBytes::from(p2pk65.get(ti))),
|
||||
OutputType::P2PK33 => Some(AddressBytes::from(p2pk33.get(ti))),
|
||||
|
||||
@@ -83,20 +83,20 @@ impl Vecs {
|
||||
self.check_xor_bytes(parser)?;
|
||||
|
||||
// Validate computed versions against dependencies
|
||||
let dep_version = indexer.vecs.transactions.first_txindex.version()
|
||||
let dep_version = indexer.vecs.transactions.first_tx_index.version()
|
||||
+ indexer.vecs.transactions.height.version();
|
||||
self.block
|
||||
.validate_computed_version_or_reset(dep_version)?;
|
||||
self.tx
|
||||
.validate_computed_version_or_reset(dep_version)?;
|
||||
|
||||
let min_txindex = TxIndex::from(self.tx.len()).min(starting_indexes.txindex);
|
||||
let min_tx_index = TxIndex::from(self.tx.len()).min(starting_indexes.tx_index);
|
||||
|
||||
let Some(min_height) = indexer
|
||||
.vecs
|
||||
.transactions
|
||||
.height
|
||||
.collect_one(min_txindex)
|
||||
.collect_one(min_tx_index)
|
||||
.map(|h: Height| h.min(starting_indexes.height))
|
||||
else {
|
||||
return Ok(());
|
||||
@@ -104,13 +104,13 @@ impl Vecs {
|
||||
|
||||
// Cursor avoids per-height PcoVec page decompression.
|
||||
// Heights are sequential, so the cursor only advances forward.
|
||||
let mut first_txindex_cursor = indexer.vecs.transactions.first_txindex.cursor();
|
||||
first_txindex_cursor.advance(min_height.to_usize());
|
||||
let mut first_tx_index_cursor = indexer.vecs.transactions.first_tx_index.cursor();
|
||||
first_tx_index_cursor.advance(min_height.to_usize());
|
||||
|
||||
parser
|
||||
.read(
|
||||
Some(min_height),
|
||||
Some((indexer.vecs.transactions.first_txindex.len() - 1).into()),
|
||||
Some((indexer.vecs.transactions.first_tx_index.len() - 1).into()),
|
||||
)
|
||||
.iter()
|
||||
.try_for_each(|block| -> Result<()> {
|
||||
@@ -119,13 +119,13 @@ impl Vecs {
|
||||
self.block
|
||||
.truncate_push(height, block.metadata().position())?;
|
||||
|
||||
let txindex = first_txindex_cursor.next().unwrap();
|
||||
let tx_index = first_tx_index_cursor.next().unwrap();
|
||||
|
||||
block.tx_metadata().iter().enumerate().try_for_each(
|
||||
|(index, metadata)| -> Result<()> {
|
||||
let txindex = txindex + index;
|
||||
let tx_index = tx_index + index;
|
||||
self.tx
|
||||
.truncate_push(txindex, metadata.position())?;
|
||||
.truncate_push(tx_index, metadata.position())?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
@@ -59,7 +59,7 @@ impl Vecs {
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let source_version =
|
||||
indexer.vecs.outputs.value.version() + indexer.vecs.outputs.outputtype.version();
|
||||
indexer.vecs.outputs.value.version() + indexer.vecs.outputs.output_type.version();
|
||||
self.spot
|
||||
.cents
|
||||
.height
|
||||
@@ -156,34 +156,34 @@ impl Vecs {
|
||||
let total_outputs = indexer.vecs.outputs.value.len();
|
||||
|
||||
// Pre-collect height-indexed data for the range (plus one extra for next-block lookups)
|
||||
let collect_end = (range.end + 1).min(indexer.vecs.transactions.first_txindex.len());
|
||||
let first_txindexes: Vec<TxIndex> = indexer
|
||||
let collect_end = (range.end + 1).min(indexer.vecs.transactions.first_tx_index.len());
|
||||
let first_tx_indexes: Vec<TxIndex> = indexer
|
||||
.vecs
|
||||
.transactions
|
||||
.first_txindex
|
||||
.first_tx_index
|
||||
.collect_range_at(range.start, collect_end);
|
||||
|
||||
let out_firsts: Vec<TxOutIndex> = indexer
|
||||
.vecs
|
||||
.outputs
|
||||
.first_txoutindex
|
||||
.first_txout_index
|
||||
.collect_range_at(range.start, collect_end);
|
||||
|
||||
let mut ref_bins = Vec::with_capacity(range.len());
|
||||
|
||||
// Cursor avoids per-block PcoVec page decompression for
|
||||
// the tx-indexed first_txoutindex lookup. The accessed
|
||||
// txindex values (first_txindex + 1) are strictly increasing
|
||||
// the tx-indexed first_txout_index lookup. The accessed
|
||||
// tx_index values (first_tx_index + 1) are strictly increasing
|
||||
// across blocks, so the cursor only advances forward.
|
||||
let mut txout_cursor = indexer.vecs.transactions.first_txoutindex.cursor();
|
||||
let mut txout_cursor = indexer.vecs.transactions.first_txout_index.cursor();
|
||||
|
||||
// Reusable buffers — avoid per-block allocation
|
||||
let mut values: Vec<Sats> = Vec::new();
|
||||
let mut output_types: Vec<OutputType> = Vec::new();
|
||||
|
||||
for (idx, _h) in range.enumerate() {
|
||||
let first_txindex = first_txindexes[idx];
|
||||
let next_first_txindex = first_txindexes
|
||||
let first_tx_index = first_tx_indexes[idx];
|
||||
let next_first_tx_index = first_tx_indexes
|
||||
.get(idx + 1)
|
||||
.copied()
|
||||
.unwrap_or(TxIndex::from(total_txs));
|
||||
@@ -193,8 +193,8 @@ impl Vecs {
|
||||
.copied()
|
||||
.unwrap_or(TxOutIndex::from(total_outputs))
|
||||
.to_usize();
|
||||
let out_start = if first_txindex.to_usize() + 1 < next_first_txindex.to_usize() {
|
||||
let target = first_txindex.to_usize() + 1;
|
||||
let out_start = if first_tx_index.to_usize() + 1 < next_first_tx_index.to_usize() {
|
||||
let target = first_tx_index.to_usize() + 1;
|
||||
txout_cursor.advance(target - txout_cursor.position());
|
||||
txout_cursor.next().unwrap().to_usize()
|
||||
} else {
|
||||
@@ -207,7 +207,7 @@ impl Vecs {
|
||||
.outputs
|
||||
.value
|
||||
.collect_range_into_at(out_start, out_end, &mut values);
|
||||
indexer.vecs.outputs.outputtype.collect_range_into_at(
|
||||
indexer.vecs.outputs.output_type.collect_range_into_at(
|
||||
out_start,
|
||||
out_end,
|
||||
&mut output_types,
|
||||
|
||||
@@ -27,7 +27,7 @@ impl Vecs {
|
||||
Ok(v.compute_count_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.scripts.p2ms.first_index,
|
||||
&indexer.vecs.scripts.p2ms.to_txindex,
|
||||
&indexer.vecs.scripts.p2ms.to_tx_index,
|
||||
exit,
|
||||
)?)
|
||||
})?;
|
||||
@@ -107,27 +107,27 @@ impl Vecs {
|
||||
Ok(v.compute_count_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.scripts.opreturn.first_index,
|
||||
&indexer.vecs.scripts.opreturn.to_txindex,
|
||||
&indexer.vecs.scripts.opreturn.to_tx_index,
|
||||
exit,
|
||||
)?)
|
||||
})?;
|
||||
|
||||
self.unknownoutput
|
||||
self.unknown_output
|
||||
.compute(starting_indexes.height, exit, |v| {
|
||||
Ok(v.compute_count_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.scripts.unknown.first_index,
|
||||
&indexer.vecs.scripts.unknown.to_txindex,
|
||||
&indexer.vecs.scripts.unknown.to_tx_index,
|
||||
exit,
|
||||
)?)
|
||||
})?;
|
||||
|
||||
self.emptyoutput
|
||||
self.empty_output
|
||||
.compute(starting_indexes.height, exit, |v| {
|
||||
Ok(v.compute_count_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.scripts.empty.first_index,
|
||||
&indexer.vecs.scripts.empty.to_txindex,
|
||||
&indexer.vecs.scripts.empty.to_tx_index,
|
||||
exit,
|
||||
)?)
|
||||
})?;
|
||||
|
||||
@@ -53,16 +53,16 @@ impl Vecs {
|
||||
indexes,
|
||||
cached_starts,
|
||||
)?,
|
||||
emptyoutput: PerBlockCumulativeWithSums::forced_import(
|
||||
empty_output: PerBlockCumulativeWithSums::forced_import(
|
||||
db,
|
||||
"emptyoutput_count",
|
||||
"empty_output_count",
|
||||
version,
|
||||
indexes,
|
||||
cached_starts,
|
||||
)?,
|
||||
unknownoutput: PerBlockCumulativeWithSums::forced_import(
|
||||
unknown_output: PerBlockCumulativeWithSums::forced_import(
|
||||
db,
|
||||
"unknownoutput_count",
|
||||
"unknown_output_count",
|
||||
version,
|
||||
indexes,
|
||||
cached_starts,
|
||||
|
||||
@@ -16,8 +16,8 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub p2wpkh: PerBlockCumulativeWithSums<StoredU64, StoredU64, M>,
|
||||
pub p2wsh: PerBlockCumulativeWithSums<StoredU64, StoredU64, M>,
|
||||
pub opreturn: PerBlockCumulativeWithSums<StoredU64, StoredU64, M>,
|
||||
pub emptyoutput: PerBlockCumulativeWithSums<StoredU64, StoredU64, M>,
|
||||
pub unknownoutput: PerBlockCumulativeWithSums<StoredU64, StoredU64, M>,
|
||||
pub empty_output: PerBlockCumulativeWithSums<StoredU64, StoredU64, M>,
|
||||
pub unknown_output: PerBlockCumulativeWithSums<StoredU64, StoredU64, M>,
|
||||
|
||||
pub segwit: PerBlockCumulativeWithSums<StoredU64, StoredU64, M>,
|
||||
}
|
||||
|
||||
@@ -20,13 +20,13 @@ impl Vecs {
|
||||
exit,
|
||||
|height_vec| {
|
||||
// Validate computed versions against dependencies
|
||||
let dep_version = indexer.vecs.outputs.first_txoutindex.version()
|
||||
+ indexer.vecs.outputs.outputtype.version()
|
||||
let dep_version = indexer.vecs.outputs.first_txout_index.version()
|
||||
+ indexer.vecs.outputs.output_type.version()
|
||||
+ indexer.vecs.outputs.value.version();
|
||||
height_vec.validate_computed_version_or_reset(dep_version)?;
|
||||
|
||||
// Get target height
|
||||
let target_len = indexer.vecs.outputs.first_txoutindex.len();
|
||||
let target_len = indexer.vecs.outputs.first_txout_index.len();
|
||||
if target_len == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
@@ -42,14 +42,14 @@ impl Vecs {
|
||||
}
|
||||
|
||||
// Pre-collect height-indexed data
|
||||
let first_txoutindexes: Vec<TxOutIndex> =
|
||||
indexer.vecs.outputs.first_txoutindex.collect_range_at(
|
||||
let first_txout_indexes: Vec<TxOutIndex> =
|
||||
indexer.vecs.outputs.first_txout_index.collect_range_at(
|
||||
starting_height.to_usize(),
|
||||
target_height.to_usize()
|
||||
+ 2.min(indexer.vecs.outputs.first_txoutindex.len()),
|
||||
+ 2.min(indexer.vecs.outputs.first_txout_index.len()),
|
||||
);
|
||||
|
||||
let mut outputtypes_buf: Vec<OutputType> = Vec::new();
|
||||
let mut output_types_buf: Vec<OutputType> = Vec::new();
|
||||
let mut values_buf: Vec<Sats> = Vec::new();
|
||||
|
||||
// Iterate blocks
|
||||
@@ -58,23 +58,23 @@ impl Vecs {
|
||||
let local_idx = h - starting_height.to_usize();
|
||||
|
||||
// Get output range for this block
|
||||
let first_txoutindex = first_txoutindexes[local_idx];
|
||||
let next_first_txoutindex =
|
||||
if let Some(&next) = first_txoutindexes.get(local_idx + 1) {
|
||||
let first_txout_index = first_txout_indexes[local_idx];
|
||||
let next_first_txout_index =
|
||||
if let Some(&next) = first_txout_indexes.get(local_idx + 1) {
|
||||
next
|
||||
} else {
|
||||
TxOutIndex::from(indexer.vecs.outputs.value.len())
|
||||
};
|
||||
|
||||
let out_start = first_txoutindex.to_usize();
|
||||
let out_end = next_first_txoutindex.to_usize();
|
||||
let out_start = first_txout_index.to_usize();
|
||||
let out_end = next_first_txout_index.to_usize();
|
||||
|
||||
// Pre-collect both vecs into reusable buffers
|
||||
indexer
|
||||
.vecs
|
||||
.outputs
|
||||
.outputtype
|
||||
.collect_range_into_at(out_start, out_end, &mut outputtypes_buf);
|
||||
.output_type
|
||||
.collect_range_into_at(out_start, out_end, &mut output_types_buf);
|
||||
indexer
|
||||
.vecs
|
||||
.outputs
|
||||
@@ -82,7 +82,7 @@ impl Vecs {
|
||||
.collect_range_into_at(out_start, out_end, &mut values_buf);
|
||||
|
||||
let mut opreturn_value = Sats::ZERO;
|
||||
for (ot, val) in outputtypes_buf.iter().zip(values_buf.iter()) {
|
||||
for (ot, val) in output_types_buf.iter().zip(values_buf.iter()) {
|
||||
if *ot == OutputType::OpReturn {
|
||||
opreturn_value += *val;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ impl Vecs {
|
||||
.compute(starting_indexes.height, &window_starts, exit, |height| {
|
||||
Ok(height.compute_count_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.transactions.first_txindex,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
&indexer.vecs.transactions.txid,
|
||||
exit,
|
||||
)?)
|
||||
|
||||
@@ -14,16 +14,16 @@ impl Vecs {
|
||||
indexes: &indexes::Vecs,
|
||||
cached_starts: &CachedWindowStarts,
|
||||
) -> Result<Self> {
|
||||
let txindex_to_is_coinbase = LazyVecFrom2::init(
|
||||
let tx_index_to_is_coinbase = LazyVecFrom2::init(
|
||||
"is_coinbase",
|
||||
version,
|
||||
indexer.vecs.transactions.height.read_only_boxed_clone(),
|
||||
indexer
|
||||
.vecs
|
||||
.transactions
|
||||
.first_txindex
|
||||
.first_tx_index
|
||||
.read_only_boxed_clone(),
|
||||
|index: TxIndex, _height, first_txindex| StoredBool::from(index == first_txindex),
|
||||
|index: TxIndex, _height, first_tx_index| StoredBool::from(index == first_tx_index),
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
@@ -34,7 +34,7 @@ impl Vecs {
|
||||
indexes,
|
||||
cached_starts,
|
||||
)?,
|
||||
is_coinbase: txindex_to_is_coinbase,
|
||||
is_coinbase: tx_index_to_is_coinbase,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,23 +19,23 @@ impl Vecs {
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.input_value.compute_sum_from_indexes(
|
||||
starting_indexes.txindex,
|
||||
&indexer.vecs.transactions.first_txinindex,
|
||||
&indexes.txindex.input_count,
|
||||
starting_indexes.tx_index,
|
||||
&indexer.vecs.transactions.first_txin_index,
|
||||
&indexes.tx_index.input_count,
|
||||
&txins.spent.value,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.output_value.compute_sum_from_indexes(
|
||||
starting_indexes.txindex,
|
||||
&indexer.vecs.transactions.first_txoutindex,
|
||||
&indexes.txindex.output_count,
|
||||
starting_indexes.tx_index,
|
||||
&indexer.vecs.transactions.first_txout_index,
|
||||
&indexes.tx_index.output_count,
|
||||
&indexer.vecs.outputs.value,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.fee.txindex.compute_transform2(
|
||||
starting_indexes.txindex,
|
||||
self.fee.tx_index.compute_transform2(
|
||||
starting_indexes.tx_index,
|
||||
&self.input_value,
|
||||
&self.output_value,
|
||||
|(i, input, output, ..)| {
|
||||
@@ -49,11 +49,11 @@ impl Vecs {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.fee_rate.txindex.compute_transform2(
|
||||
starting_indexes.txindex,
|
||||
&self.fee.txindex,
|
||||
&size_vecs.vsize.txindex,
|
||||
|(txindex, fee, vsize, ..)| (txindex, FeeRate::from((fee, vsize))),
|
||||
self.fee_rate.tx_index.compute_transform2(
|
||||
starting_indexes.tx_index,
|
||||
&self.fee.tx_index,
|
||||
&size_vecs.vsize.tx_index,
|
||||
|(tx_index, fee, vsize, ..)| (tx_index, FeeRate::from((fee, vsize))),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ impl Vecs {
|
||||
version: Version,
|
||||
indexer: &Indexer,
|
||||
) -> Result<Self> {
|
||||
let txindex_to_vsize = LazyVecFrom2::init(
|
||||
let tx_index_to_vsize = LazyVecFrom2::init(
|
||||
"tx_vsize",
|
||||
version,
|
||||
indexer.vecs.transactions.base_size.read_only_boxed_clone(),
|
||||
@@ -23,9 +23,9 @@ impl Vecs {
|
||||
);
|
||||
|
||||
let vsize =
|
||||
LazyPerTxDistribution::forced_import(db, "tx_vsize", version, txindex_to_vsize)?;
|
||||
LazyPerTxDistribution::forced_import(db, "tx_vsize", version, tx_index_to_vsize)?;
|
||||
|
||||
let txindex_to_weight = LazyVecFrom2::init(
|
||||
let tx_index_to_weight = LazyVecFrom2::init(
|
||||
"tx_weight",
|
||||
version,
|
||||
indexer.vecs.transactions.base_size.read_only_boxed_clone(),
|
||||
@@ -36,7 +36,7 @@ impl Vecs {
|
||||
let weight = LazyPerTxDistributionTransformed::new::<VSizeToWeight>(
|
||||
"tx_weight",
|
||||
version,
|
||||
txindex_to_weight,
|
||||
tx_index_to_weight,
|
||||
&vsize.distribution,
|
||||
);
|
||||
|
||||
|
||||
@@ -14,20 +14,20 @@ impl Vecs {
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let tx_vany = |tx_vany: &mut PerBlockCumulativeWithSums<StoredU64, StoredU64>,
|
||||
txversion: TxVersion| {
|
||||
let txversion_vec = &indexer.vecs.transactions.txversion;
|
||||
tx_version: TxVersion| {
|
||||
let tx_version_vec = &indexer.vecs.transactions.tx_version;
|
||||
// Cursor avoids per-transaction PcoVec page decompression.
|
||||
// Txindex values are sequential, so the cursor only advances forward.
|
||||
let mut cursor = txversion_vec.cursor();
|
||||
let mut cursor = tx_version_vec.cursor();
|
||||
tx_vany.compute(starting_indexes.height, exit, |vec| {
|
||||
vec.compute_filtered_count_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.transactions.first_txindex,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
&indexer.vecs.transactions.txid,
|
||||
|txindex| {
|
||||
let ti = txindex.to_usize();
|
||||
|tx_index| {
|
||||
let ti = tx_index.to_usize();
|
||||
cursor.advance(ti - cursor.position());
|
||||
cursor.next().unwrap() == txversion
|
||||
cursor.next().unwrap() == tx_version
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -29,8 +29,8 @@ impl Vecs {
|
||||
|sats_vec| {
|
||||
Ok(sats_vec.compute_filtered_sum_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.transactions.first_txindex,
|
||||
&indexes.height.txindex_count,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
&indexes.height.tx_index_count,
|
||||
&fees_vecs.input_value,
|
||||
|sats| !sats.is_max(),
|
||||
exit,
|
||||
@@ -45,8 +45,8 @@ impl Vecs {
|
||||
|sats_vec| {
|
||||
Ok(sats_vec.compute_sum_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.transactions.first_txindex,
|
||||
&indexes.height.txindex_count,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
&indexes.height.tx_index_count,
|
||||
&fees_vecs.output_value,
|
||||
exit,
|
||||
)?)
|
||||
|
||||
Reference in New Issue
Block a user