1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
use crate::{Sats, Term, Timestamp};
/// Represents the age of a UTXO or address balance.
/// Encapsulates all age-related calculations in one type-safe struct.
#[derive(Debug, Clone, Copy)]
pub struct Age {
/// Age in hours (primary internal unit for cohort boundaries)
hours: usize,
/// Age in days as float (for satdays_destroyed - established terminology)
days: f64,
}
impl Age {
/// Create from timestamps and block count
#[inline]
pub fn new(current_timestamp: Timestamp, prev_timestamp: Timestamp) -> Self {
Self {
hours: current_timestamp.difference_in_hours_between(prev_timestamp),
days: current_timestamp.difference_in_days_between_float(prev_timestamp),
}
}
/// Hours old (for cohort bucket lookup via HOURS_* boundaries)
#[inline]
pub fn hours(&self) -> usize {
self.hours
}
/// Days old as float (for satdays_destroyed - established terminology)
#[inline]
pub fn days(&self) -> f64 {
self.days
}
/// STH or LTH based on age (5 months = 3600 hours threshold)
#[inline]
pub fn term(&self) -> Term {
if self.hours >= Term::THRESHOLD_HOURS {
Term::Lth
} else {
Term::Sth
}
}
/// Calculate satdays destroyed for given supply
#[inline]
pub fn satdays_destroyed(&self, supply: Sats) -> Sats {
Sats::from((u64::from(supply) as f64 * self.days).floor() as u64)
}
}