Skip to content

Commit 985339e

Browse files
bfoss765claude
andcommitted
fix(dash-spv): use monotonic Instant for pending islock TTL
Wall-clock SystemTime can jump backward/forward, corrupting the elapsed-time check that expires unverifiable pending InstantLocks. Switch PendingInstantLock::first_seen from SystemTime to std::time::Instant, which is monotonic and correct for measuring an elapsed TTL. Instant::elapsed returns a Duration directly, so the expiry check simplifies to a plain comparison (matching the existing UnconfirmedTx pattern in types.rs). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent c0f0da9 commit 985339e

1 file changed

Lines changed: 9 additions & 10 deletions

File tree

dash-spv/src/sync/instantsend/manager.rs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
66
use std::collections::HashMap;
77
use std::sync::Arc;
8-
use std::time::{Duration, SystemTime};
8+
use std::time::{Duration, Instant, SystemTime};
99

1010
use dashcore::ephemerealdata::instant_lock::InstantLock;
1111
use dashcore::hashes::Hash;
@@ -49,8 +49,9 @@ pub(super) struct PendingInstantLock {
4949
/// The InstantLock data.
5050
instant_lock: InstantLock,
5151
/// When the lock was first received, used to expire locks that never
52-
/// become verifiable.
53-
first_seen: SystemTime,
52+
/// become verifiable. Uses a monotonic `Instant` so the elapsed-time TTL
53+
/// check is unaffected by wall-clock adjustments.
54+
first_seen: Instant,
5455
}
5556

5657
/// InstantSend manager.
@@ -122,7 +123,7 @@ impl InstantSendManager {
122123
} else {
123124
self.queue_pending(PendingInstantLock {
124125
instant_lock: instantlock.clone(),
125-
first_seen: SystemTime::now(),
126+
first_seen: Instant::now(),
126127
});
127128
self.progress.update_pending(self.pending_instantlocks.len());
128129
}
@@ -239,8 +240,7 @@ impl InstantSendManager {
239240
let txid = pending_lock.instant_lock.txid;
240241

241242
// Drop locks that have been awaiting quorum data for too long.
242-
let expired =
243-
pending_lock.first_seen.elapsed().map(|age| age > PENDING_TTL).unwrap_or(false);
243+
let expired = pending_lock.first_seen.elapsed() > PENDING_TTL;
244244
if expired {
245245
tracing::warn!(
246246
"Dropping InstantLock for txid {} after awaiting quorum data for over {}s",
@@ -308,8 +308,7 @@ impl InstantSendManager {
308308
pub(super) fn expire_pending(&mut self) -> usize {
309309
let before = self.pending_instantlocks.len();
310310
self.pending_instantlocks.retain(|pending| {
311-
let expired =
312-
pending.first_seen.elapsed().map(|age| age > PENDING_TTL).unwrap_or(false);
311+
let expired = pending.first_seen.elapsed() > PENDING_TTL;
313312
if expired {
314313
tracing::warn!(
315314
"Dropping InstantLock for txid {} after awaiting quorum data for over {}s",
@@ -404,14 +403,14 @@ mod tests {
404403
fn expired_pending(txid: Txid) -> PendingInstantLock {
405404
PendingInstantLock {
406405
instant_lock: create_test_instantlock(txid),
407-
first_seen: SystemTime::now() - Duration::from_secs(PENDING_TTL.as_secs() + 60),
406+
first_seen: Instant::now() - Duration::from_secs(PENDING_TTL.as_secs() + 60),
408407
}
409408
}
410409

411410
fn fresh_pending(txid: Txid) -> PendingInstantLock {
412411
PendingInstantLock {
413412
instant_lock: create_test_instantlock(txid),
414-
first_seen: SystemTime::now(),
413+
first_seen: Instant::now(),
415414
}
416415
}
417416

0 commit comments

Comments
 (0)