Skip to content

Commit be096fe

Browse files
committed
initial
1 parent 2ca4e5d commit be096fe

File tree

5 files changed

+109
-117
lines changed

5 files changed

+109
-117
lines changed

rust/signed_doc/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ futures = "0.3.31"
3333
ed25519-bip32 = "0.4.1" # used by the `mk_signed_doc` cli tool
3434
tracing = "0.1.40"
3535
thiserror = "2.0.11"
36+
chrono = "0.4.42"
3637

3738
[dev-dependencies]
3839
base64-url = "3.0.0"

rust/signed_doc/src/validator/rules/chain/tests.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,17 @@ use crate::{
88
};
99

1010
mod helper {
11-
use std::time::{Duration, SystemTime, UNIX_EPOCH};
12-
1311
use catalyst_types::uuid::UuidV7;
12+
use chrono::{Duration, Utc};
1413
use uuid::{Timestamp, Uuid};
1514

16-
pub(super) fn get_now_plus_uuidv7(secs: u64) -> UuidV7 {
17-
let future_time = SystemTime::now()
18-
.checked_add(Duration::from_secs(secs))
19-
.unwrap();
20-
let duration_since_epoch = future_time.duration_since(UNIX_EPOCH).unwrap();
15+
pub(super) fn get_now_plus_uuidv7(secs: i64) -> UuidV7 {
16+
let future_time = Utc::now()
17+
.checked_add_signed(Duration::seconds(secs))
18+
.expect("time overflow in future_time calculation");
2119

22-
let unix_secs = duration_since_epoch.as_secs();
23-
let nanos = duration_since_epoch.subsec_nanos();
20+
let unix_secs = u64::try_from(future_time.timestamp()).unwrap_or(0);
21+
let nanos = future_time.timestamp_subsec_nanos();
2422

2523
let ts = Timestamp::from_unix(uuid::NoContext, unix_secs, nanos);
2624
let uuid = Uuid::new_v7(ts);

rust/signed_doc/src/validator/rules/id/mod.rs

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33
#[cfg(test)]
44
mod tests;
55

6-
use std::time::{Duration, SystemTime};
7-
8-
use anyhow::Context;
6+
use chrono::{Duration, TimeZone, Utc};
97

108
use crate::{providers::CatalystSignedDocumentProvider, CatalystSignedDocument};
119

@@ -16,11 +14,10 @@ pub(crate) struct IdRule;
1614
impl IdRule {
1715
/// Validates document `id` field on the timestamps:
1816
/// 1. If `provider.future_threshold()` not `None`, document `id` cannot be too far in
19-
/// the future (`future_threshold` arg) from `SystemTime::now()` based on the
20-
/// provide threshold
21-
/// 2. If `provider.future_threshold()` not `None`, document `id` cannot be too far
22-
/// behind (`past_threshold` arg) from `SystemTime::now()` based on the provide
17+
/// the future (`future_threshold` arg) from `Utc::now()` based on the provided
2318
/// threshold
19+
/// 2. If `provider.past_threshold()` not `None`, document `id` cannot be too far
20+
/// behind (`past_threshold` arg) from `Utc::now()` based on the provided threshold
2421
#[allow(clippy::unused_async)]
2522
pub(crate) async fn check<Provider>(
2623
&self,
@@ -46,46 +43,46 @@ impl IdRule {
4643
.ok_or(anyhow::anyhow!("Document `id` field must be a UUIDv7"))?
4744
.to_unix();
4845

49-
let Some(id_time) =
50-
SystemTime::UNIX_EPOCH.checked_add(Duration::new(id_time_secs, id_time_nanos))
51-
else {
52-
doc.report().invalid_value(
53-
"id",
54-
&id.to_string(),
55-
"Must a valid duration since `UNIX_EPOCH`",
56-
"Cannot instantiate a valid `SystemTime` value from the provided `id` field timestamp.",
57-
);
58-
return Ok(false);
59-
};
46+
let id_time = Utc
47+
.timestamp_opt(i64::try_from(id_time_secs).unwrap_or(0), id_time_nanos)
48+
.single()
49+
.ok_or_else(|| anyhow::anyhow!("Invalid timestamp in document `id` field"))?;
50+
51+
let now = Utc::now();
6052

61-
let now = SystemTime::now();
53+
let diff = id_time.signed_duration_since(now);
6254

63-
if let Ok(id_age) = id_time.duration_since(now) {
64-
// `now` is earlier than `id_time`
55+
if diff.num_nanoseconds().unwrap_or(0) > 0 {
56+
// id_time is in the future
6557
if let Some(future_threshold) = provider.future_threshold() {
66-
if id_age > future_threshold {
58+
let threshold = Duration::from_std(future_threshold)?;
59+
if diff > threshold {
6760
doc.report().invalid_value(
6861
"id",
6962
&id.to_string(),
7063
"id < now + future_threshold",
71-
&format!("Document Version timestamp {id} cannot be too far in future (threshold: {future_threshold:?}) from now: {now:?}"),
64+
&format!(
65+
"Document Version timestamp {id} cannot be too far in future (threshold: {threshold:?}) from now: {now:?}"
66+
),
7267
);
7368
is_valid = false;
7469
}
7570
}
7671
} else {
77-
// `id_time` is earlier than `now`
78-
let id_age = now
79-
.duration_since(id_time)
80-
.context("BUG! `id_time` must be earlier than `now` at this place")?;
72+
// id_time is in the past
73+
// make positive duration
74+
let id_age = diff.abs();
8175

8276
if let Some(past_threshold) = provider.past_threshold() {
83-
if id_age > past_threshold {
77+
let threshold = Duration::from_std(past_threshold)?;
78+
if id_age > threshold {
8479
doc.report().invalid_value(
8580
"id",
8681
&id.to_string(),
8782
"id > now - past_threshold",
88-
&format!("Document Version timestamp {id} cannot be too far behind (threshold: {past_threshold:?}) from now: {now:?}",),
83+
&format!(
84+
"Document Version timestamp {id} cannot be too far behind (threshold: {threshold:?}) from now: {now:?}"
85+
),
8986
);
9087
is_valid = false;
9188
}

rust/signed_doc/src/validator/rules/id/tests.rs

Lines changed: 29 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
use std::time::SystemTime;
2-
1+
use chrono::Utc;
32
use test_case::test_case;
43
use uuid::{Timestamp, Uuid};
54

@@ -22,46 +21,46 @@ use crate::{
2221
#[test_case(
2322
#[allow(clippy::arithmetic_side_effects)]
2423
|provider| {
25-
let now = SystemTime::now()
26-
.duration_since(SystemTime::UNIX_EPOCH)
27-
.unwrap()
28-
.as_secs();
29-
let to_far_in_past = Uuid::new_v7(Timestamp::from_unix_time(
30-
now - provider.past_threshold().unwrap().as_secs() - 1,
31-
0,
32-
0,
33-
0,
34-
))
35-
.try_into()
36-
.unwrap();
24+
let now = Utc::now().timestamp();
25+
let past_threshold_secs = i64::try_from(provider.past_threshold().unwrap().as_secs()).unwrap_or(0);
26+
27+
let too_far_in_past = Uuid::new_v7(Timestamp::from_unix_time(
28+
u64::try_from(now - past_threshold_secs - 1).unwrap_or(0),
29+
0,
30+
0,
31+
0,
32+
))
33+
.try_into()
34+
.unwrap();
35+
3736
Builder::new()
38-
.with_metadata_field(SupportedField::Id(to_far_in_past))
37+
.with_metadata_field(SupportedField::Id(too_far_in_past))
3938
.build()
4039
}
4140
=> false;
42-
"`id` to far in past"
41+
"`id` too far in past"
4342
)]
4443
#[test_case(
4544
#[allow(clippy::arithmetic_side_effects)]
4645
|provider| {
47-
let now = SystemTime::now()
48-
.duration_since(SystemTime::UNIX_EPOCH)
49-
.unwrap()
50-
.as_secs();
51-
let to_far_in_future = Uuid::new_v7(Timestamp::from_unix_time(
52-
now + provider.future_threshold().unwrap().as_secs() + 1,
53-
0,
54-
0,
55-
0,
56-
))
57-
.try_into()
58-
.unwrap();
46+
let now = Utc::now().timestamp();
47+
let future_threshold_secs = i64::try_from(provider.future_threshold().unwrap().as_secs()).unwrap_or(0);
48+
49+
let too_far_in_future = Uuid::new_v7(Timestamp::from_unix_time(
50+
u64::try_from(now + future_threshold_secs + 1).unwrap_or(0),
51+
0,
52+
0,
53+
0,
54+
))
55+
.try_into()
56+
.unwrap();
57+
5958
Builder::new()
60-
.with_metadata_field(SupportedField::Id(to_far_in_future))
59+
.with_metadata_field(SupportedField::Id(too_far_in_future))
6160
.build()
6261
}
6362
=> false;
64-
"`id` to far in future"
63+
"`id` too far in future"
6564
)]
6665
#[test_case(
6766
|_| {

0 commit comments

Comments
 (0)