Skip to content

Commit cf0207e

Browse files
cosmir17oxidyn0
authored andcommitted
fix: correct timestamp unit mismatch in currentCapacity calculation (#929)
* fix: correct timestamp unit mismatch in currentCapacity calculation * fix: use u64 for timestamp newtypes per review
1 parent 75e88c6 commit cf0207e

2 files changed

Lines changed: 35 additions & 10 deletions

File tree

indexer-api/src/infra/storage/dust.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::{
1717
};
1818
use fastrace::trace;
1919
use indexer_common::{
20-
domain::{ByteVec, CardanoRewardAddress, LedgerVersion, ledger},
20+
domain::{ByteVec, CardanoRewardAddress, LedgerVersion, TimestampMs, TimestampSecs, ledger},
2121
infra::sqlx::U128BeBytes,
2222
};
2323
use indoc::indoc;
@@ -78,31 +78,32 @@ impl DustStorage for Storage {
7878
.fetch_optional(&*self.pool)
7979
.await?;
8080

81-
if let Some((value, ctime)) = result {
81+
if let Some((value, ctime_raw)) = result {
8282
let value = u128::from(value);
8383
night_balance = value;
8484

8585
// DUST generation rate = STAR * generation_decay_rate SPECK/second.
8686
generation_rate = value.saturating_mul(generation_decay_rate);
8787

88-
// Calculate current capacity based on elapsed time since creation.
88+
// dust_generation_info.ctime is in seconds (ledger convention).
89+
let ctime = TimestampSecs(ctime_raw as u64);
90+
8991
// Get current timestamp from latest block.
92+
// blocks.timestamp is in milliseconds (Substrate Timestamp pallet).
9093
let current_time_query = indoc! {"
9194
SELECT timestamp
9295
FROM blocks
9396
ORDER BY height DESC
9497
LIMIT 1
9598
"};
9699

97-
let current_timestamp = sqlx::query_as::<_, (i64,)>(current_time_query)
100+
let now = sqlx::query_as::<_, (i64,)>(current_time_query)
98101
.fetch_optional(&*self.pool)
99102
.await?
100-
.map(|(t,)| t)
101-
.unwrap_or(ctime);
103+
.map(|(t,)| TimestampMs(t as u64))
104+
.unwrap_or(ctime.to_ms());
102105

103-
// Calculate elapsed seconds since creation.
104-
// Convert from milliseconds to seconds.
105-
let elapsed_seconds = ((current_timestamp - ctime).max(0) as u128) / 1000;
106+
let elapsed_seconds = now.elapsed_seconds_since(ctime.to_ms());
106107

107108
// Maximum capacity (static cap) = STAR * night_dust_ratio.
108109
max_capacity = value.saturating_mul(night_dust_ratio);
@@ -111,7 +112,7 @@ impl DustStorage for Storage {
111112
// elapsed_seconds. Capped at max_capacity.
112113
let generated_capacity = value
113114
.saturating_mul(generation_decay_rate)
114-
.saturating_mul(elapsed_seconds);
115+
.saturating_mul(elapsed_seconds as u128);
115116
current_capacity = generated_capacity.min(max_capacity);
116117
}
117118
}

indexer-common/src/domain.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,30 @@ mod network_id_tests {
124124
}
125125
}
126126

127+
/// A timestamp in milliseconds since the Unix epoch (Substrate Timestamp pallet convention).
128+
/// Use when working with values from the `blocks.timestamp` column.
129+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
130+
pub struct TimestampMs(pub u64);
131+
132+
/// A timestamp in seconds since the Unix epoch (ledger convention).
133+
/// Use when working with values from `dust_generation_info.ctime`.
134+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
135+
pub struct TimestampSecs(pub u64);
136+
137+
impl TimestampSecs {
138+
/// Convert to milliseconds.
139+
pub fn to_ms(self) -> TimestampMs {
140+
TimestampMs(self.0 * 1000)
141+
}
142+
}
143+
144+
impl TimestampMs {
145+
/// Calculate elapsed seconds since an earlier timestamp.
146+
pub fn elapsed_seconds_since(self, earlier: TimestampMs) -> u64 {
147+
self.0.saturating_sub(earlier.0) / 1000
148+
}
149+
}
150+
127151
/// The outcome of applying a regular transaction to the ledger state along with extracted data.
128152
#[derive(Debug, Default, Clone, PartialEq, Eq)]
129153
pub struct ApplyRegularTransactionOutcome {

0 commit comments

Comments
 (0)