mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-28 19:28:11 -07:00
global: big snapshot
This commit is contained in:
@@ -3,7 +3,10 @@ use brk_types::{Cents, Sats, TypeIndex};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::distribution::{
|
||||
addr::{AddrTypeToActivityCounts, AddrTypeToVec},
|
||||
addr::{
|
||||
AddrTypeToActivityCounts, AddrTypeToExposedAddrCount, AddrTypeToExposedAddrSupply,
|
||||
AddrTypeToReusedAddrCount, AddrTypeToReusedAddrUseCount, AddrTypeToVec,
|
||||
},
|
||||
cohorts::AddrCohorts,
|
||||
};
|
||||
|
||||
@@ -25,6 +28,12 @@ pub(crate) fn process_received(
|
||||
addr_count: &mut ByAddrType<u64>,
|
||||
empty_addr_count: &mut ByAddrType<u64>,
|
||||
activity_counts: &mut AddrTypeToActivityCounts,
|
||||
reused_addr_count: &mut AddrTypeToReusedAddrCount,
|
||||
total_reused_addr_count: &mut AddrTypeToReusedAddrCount,
|
||||
reused_addr_use_count: &mut AddrTypeToReusedAddrUseCount,
|
||||
exposed_addr_count: &mut AddrTypeToExposedAddrCount,
|
||||
total_exposed_addr_count: &mut AddrTypeToExposedAddrCount,
|
||||
exposed_addr_supply: &mut AddrTypeToExposedAddrSupply,
|
||||
) {
|
||||
let max_type_len = received_data
|
||||
.iter()
|
||||
@@ -43,6 +52,12 @@ pub(crate) fn process_received(
|
||||
let type_addr_count = addr_count.get_mut(output_type).unwrap();
|
||||
let type_empty_count = empty_addr_count.get_mut(output_type).unwrap();
|
||||
let type_activity = activity_counts.get_mut_unwrap(output_type);
|
||||
let type_reused_count = reused_addr_count.get_mut(output_type).unwrap();
|
||||
let type_total_reused_count = total_reused_addr_count.get_mut(output_type).unwrap();
|
||||
let type_reused_use_count = reused_addr_use_count.get_mut(output_type).unwrap();
|
||||
let type_exposed_count = exposed_addr_count.get_mut(output_type).unwrap();
|
||||
let type_total_exposed_count = total_exposed_addr_count.get_mut(output_type).unwrap();
|
||||
let type_exposed_supply = exposed_addr_supply.get_mut(output_type).unwrap();
|
||||
|
||||
// Aggregate receives by address - each address processed exactly once
|
||||
for (type_index, value) in vec {
|
||||
@@ -57,6 +72,13 @@ pub(crate) fn process_received(
|
||||
// Track receiving activity - each address in receive aggregation
|
||||
type_activity.receiving += 1;
|
||||
|
||||
// Capture state BEFORE the receive mutates funded_txo_count
|
||||
let was_funded = addr_data.is_funded();
|
||||
let was_reused = addr_data.is_reused();
|
||||
let funded_txo_count_before = addr_data.funded_txo_count;
|
||||
let was_pubkey_exposed = addr_data.is_pubkey_exposed(output_type);
|
||||
let exposed_contribution_before = addr_data.exposed_supply_contribution(output_type);
|
||||
|
||||
match status {
|
||||
TrackingStatus::New => {
|
||||
*type_addr_count += 1;
|
||||
@@ -134,6 +156,54 @@ pub(crate) fn process_received(
|
||||
.receive_outputs(addr_data, recv.total_value, price, recv.output_count);
|
||||
}
|
||||
}
|
||||
|
||||
// Update reused counts based on the post-receive state
|
||||
let is_now_reused = addr_data.is_reused();
|
||||
if is_now_reused && !was_reused {
|
||||
// Newly crossed the reuse threshold this block
|
||||
*type_reused_count += 1;
|
||||
*type_total_reused_count += 1;
|
||||
} else if is_now_reused && !was_funded {
|
||||
// Already-reused address reactivating into the funded set
|
||||
*type_reused_count += 1;
|
||||
}
|
||||
|
||||
// Per-block reused-use count: every individual output to this
|
||||
// address counts iff the address was already reused at the
|
||||
// moment of that output. With aggregation, that means we
|
||||
// skip enough outputs at the front to take the lifetime
|
||||
// funding count from `funded_txo_count_before` past 1, then
|
||||
// count the rest. `skipped` is `max(0, 2 - before)`.
|
||||
let skipped = 2u32.saturating_sub(funded_txo_count_before);
|
||||
let counted = recv.output_count.saturating_sub(skipped);
|
||||
*type_reused_use_count += u64::from(counted);
|
||||
|
||||
// Update exposed counts. The address's pubkey-exposure state
|
||||
// is unchanged by a receive (spent_txo_count unchanged), so we
|
||||
// can use the captured `was_pubkey_exposed` for both pre and post.
|
||||
// After the receive the address is always funded, so it's in the
|
||||
// funded exposed set iff its pubkey is exposed.
|
||||
//
|
||||
// Funded exposed enters when the address wasn't funded before but
|
||||
// is now AND its pubkey is exposed.
|
||||
// Total exposed (pk_exposed_at_funding types only) increments on
|
||||
// first-ever receive (status == TrackingStatus::New); for other
|
||||
// types it's incremented in process_sent on the first spend.
|
||||
if !was_funded && was_pubkey_exposed {
|
||||
*type_exposed_count += 1;
|
||||
}
|
||||
if output_type.pubkey_exposed_at_funding()
|
||||
&& matches!(status, TrackingStatus::New)
|
||||
{
|
||||
*type_total_exposed_count += 1;
|
||||
}
|
||||
|
||||
// Update exposed supply via post-receive contribution delta.
|
||||
let exposed_contribution_after =
|
||||
addr_data.exposed_supply_contribution(output_type);
|
||||
// Receives can only add to balance and membership, so the delta
|
||||
// is always non-negative.
|
||||
*type_exposed_supply += exposed_contribution_after - exposed_contribution_before;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,10 @@ use rustc_hash::FxHashSet;
|
||||
use vecdb::VecIndex;
|
||||
|
||||
use crate::distribution::{
|
||||
addr::{AddrTypeToActivityCounts, HeightToAddrTypeToVec},
|
||||
addr::{
|
||||
AddrTypeToActivityCounts, AddrTypeToExposedAddrCount, AddrTypeToExposedAddrSupply,
|
||||
AddrTypeToReusedAddrCount, HeightToAddrTypeToVec,
|
||||
},
|
||||
cohorts::AddrCohorts,
|
||||
compute::PriceRangeMax,
|
||||
};
|
||||
@@ -35,6 +38,10 @@ pub(crate) fn process_sent(
|
||||
addr_count: &mut ByAddrType<u64>,
|
||||
empty_addr_count: &mut ByAddrType<u64>,
|
||||
activity_counts: &mut AddrTypeToActivityCounts,
|
||||
reused_addr_count: &mut AddrTypeToReusedAddrCount,
|
||||
exposed_addr_count: &mut AddrTypeToExposedAddrCount,
|
||||
total_exposed_addr_count: &mut AddrTypeToExposedAddrCount,
|
||||
exposed_addr_supply: &mut AddrTypeToExposedAddrSupply,
|
||||
received_addrs: &ByAddrType<FxHashSet<TypeIndex>>,
|
||||
height_to_price: &[Cents],
|
||||
height_to_timestamp: &[Timestamp],
|
||||
@@ -57,6 +64,10 @@ pub(crate) fn process_sent(
|
||||
let type_addr_count = addr_count.get_mut(output_type).unwrap();
|
||||
let type_empty_count = empty_addr_count.get_mut(output_type).unwrap();
|
||||
let type_activity = activity_counts.get_mut_unwrap(output_type);
|
||||
let type_reused_count = reused_addr_count.get_mut(output_type).unwrap();
|
||||
let type_exposed_count = exposed_addr_count.get_mut(output_type).unwrap();
|
||||
let type_total_exposed_count = total_exposed_addr_count.get_mut(output_type).unwrap();
|
||||
let type_exposed_supply = exposed_addr_supply.get_mut(output_type).unwrap();
|
||||
let type_received = received_addrs.get(output_type);
|
||||
let type_seen = seen_senders.get_mut_unwrap(output_type);
|
||||
|
||||
@@ -78,6 +89,11 @@ pub(crate) fn process_sent(
|
||||
|
||||
let will_be_empty = addr_data.has_1_utxos();
|
||||
|
||||
// Capture exposed state BEFORE the spend mutates spent_txo_count.
|
||||
let was_pubkey_exposed = addr_data.is_pubkey_exposed(output_type);
|
||||
let exposed_contribution_before =
|
||||
addr_data.exposed_supply_contribution(output_type);
|
||||
|
||||
// Compute buckets once
|
||||
let prev_bucket = AmountBucket::from(prev_balance);
|
||||
let new_bucket = AmountBucket::from(new_balance);
|
||||
@@ -91,6 +107,27 @@ pub(crate) fn process_sent(
|
||||
.unwrap();
|
||||
|
||||
cohort_state.send(addr_data, value, current_price, prev_price, peak_price, age)?;
|
||||
// addr_data.spent_txo_count is now incremented by 1.
|
||||
|
||||
// Update exposed supply via post-spend contribution delta.
|
||||
let exposed_contribution_after =
|
||||
addr_data.exposed_supply_contribution(output_type);
|
||||
if exposed_contribution_after >= exposed_contribution_before {
|
||||
*type_exposed_supply += exposed_contribution_after - exposed_contribution_before;
|
||||
} else {
|
||||
*type_exposed_supply -= exposed_contribution_before - exposed_contribution_after;
|
||||
}
|
||||
|
||||
// Update exposed counts on first-ever pubkey exposure.
|
||||
// For non-pk-exposed types this fires on the first spend; for
|
||||
// pk-exposed types it never fires here (was_pubkey_exposed was
|
||||
// already true at first receive in process_received).
|
||||
if !was_pubkey_exposed {
|
||||
*type_total_exposed_count += 1;
|
||||
if !will_be_empty {
|
||||
*type_exposed_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// If crossing a bucket boundary, remove the (now-updated) address from old bucket
|
||||
if will_be_empty || crossing_boundary {
|
||||
@@ -101,6 +138,17 @@ pub(crate) fn process_sent(
|
||||
if will_be_empty {
|
||||
*type_addr_count -= 1;
|
||||
*type_empty_count += 1;
|
||||
// Reused addr leaving the funded reused set
|
||||
if addr_data.is_reused() {
|
||||
*type_reused_count -= 1;
|
||||
}
|
||||
// Exposed addr leaving the funded exposed set: was in set
|
||||
// iff its pubkey was exposed pre-spend (since it was funded
|
||||
// to be in process_sent in the first place), and now leaves
|
||||
// because it's empty.
|
||||
if was_pubkey_exposed {
|
||||
*type_exposed_count -= 1;
|
||||
}
|
||||
lookup.move_to_empty(output_type, type_index);
|
||||
} else if crossing_boundary {
|
||||
cohorts
|
||||
|
||||
Reference in New Issue
Block a user