Skip to content

Commit 61ea015

Browse files
committed
wip
1 parent 28adeb9 commit 61ea015

File tree

2 files changed

+201
-16
lines changed

2 files changed

+201
-16
lines changed

rust/signed_doc/src/providers.rs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use std::{future::Future, time::Duration};
44

5-
use catalyst_types::catalyst_id::CatalystId;
5+
use catalyst_types::{catalyst_id::CatalystId, uuid::UuidV7};
66
use ed25519_dalek::VerifyingKey;
77

88
use crate::{CatalystSignedDocument, DocumentRef};
@@ -18,12 +18,19 @@ pub trait VerifyingKeyProvider {
1818

1919
/// `CatalystSignedDocument` Provider trait
2020
pub trait CatalystSignedDocumentProvider: Send + Sync {
21-
/// Try to get `CatalystSignedDocument`from document reference
21+
/// Try to get `CatalystSignedDocument` from document reference
2222
fn try_get_doc(
2323
&self,
2424
doc_ref: &DocumentRef,
2525
) -> impl Future<Output = anyhow::Result<Option<CatalystSignedDocument>>> + Send;
2626

27+
/// Try to get the last known version of the `CatalystSignedDocument`, same
28+
/// `id` and the highest known `ver`.
29+
fn try_get_last_doc(
30+
&self,
31+
id: UuidV7,
32+
) -> impl Future<Output = anyhow::Result<Option<CatalystSignedDocument>>> + Send;
33+
2734
/// Returns a future threshold value, which is used in the validation of the `ver`
2835
/// field that it is not too far in the future.
2936
/// If `None` is returned, skips "too far in the future" validation.
@@ -48,7 +55,6 @@ pub mod tests {
4855

4956
/// Simple testing implementation of `CatalystSignedDocumentProvider`
5057
#[derive(Default, Debug)]
51-
5258
pub struct TestCatalystSignedDocumentProvider(HashMap<DocumentRef, CatalystSignedDocument>);
5359

5460
impl TestCatalystSignedDocumentProvider {
@@ -82,6 +88,18 @@ pub mod tests {
8288
Ok(self.0.get(doc_ref).cloned())
8389
}
8490

91+
async fn try_get_last_doc(
92+
&self,
93+
id: catalyst_types::uuid::UuidV7,
94+
) -> anyhow::Result<Option<CatalystSignedDocument>> {
95+
Ok(self
96+
.0
97+
.iter()
98+
.filter(|(doc_ref, _)| doc_ref.id() == &id)
99+
.max_by_key(|(doc_ref, _)| doc_ref.ver().uuid())
100+
.map(|(_, doc)| doc.clone()))
101+
}
102+
85103
fn future_threshold(&self) -> Option<std::time::Duration> {
86104
Some(Duration::from_secs(5))
87105
}

rust/signed_doc/src/validator/rules/ver.rs

Lines changed: 180 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
//! Validator for Signed Document Version
22
3-
use crate::{
4-
providers::CatalystSignedDocumentProvider, CatalystSignedDocument, DocLocator, DocumentRef,
5-
};
3+
use crate::{providers::CatalystSignedDocumentProvider, CatalystSignedDocument};
64

75
/// Signed Document `ver` field validation rule
86
pub(crate) struct VerRule;
@@ -43,17 +41,54 @@ impl VerRule {
4341
&format!("Document Version {ver} cannot be smaller than Document ID {id}"),
4442
);
4543
is_valid = false;
46-
}
44+
} else if let Some(last_doc) = provider.try_get_last_doc(id).await? {
45+
let Ok(last_doc_ver) = last_doc.doc_ver() else {
46+
doc.report().missing_field(
47+
"ver",
48+
&format!(
49+
"Missing `ver` field in the latest known document, for the the id {id}"
50+
),
51+
);
52+
return Ok(false);
53+
};
4754

48-
if ver != id {
49-
let first_submitted_doc = DocumentRef::new(id, id, DocLocator::default());
50-
if provider.try_get_doc(&first_submitted_doc).await?.is_none() {
55+
if last_doc_ver >= ver {
5156
doc.report().functional_validation(
52-
&format!("`ver` and `id` are not equal, ver: {ver}, id: {id}. Document with `id` and `ver` being equal MUST exist"),
53-
"Cannot get a first version document from the provider, document for which `id` and `ver` are equal.",
57+
&format!("New document ver should be greater that the submitted latest known. New document ver: {ver}, latest known ver: {last_doc_ver}"),
58+
&format!("Document's `ver` field should continuously incrising, for the the id {id}"),
5459
);
5560
is_valid = false;
5661
}
62+
63+
let Ok(last_doc_type) = last_doc.doc_type() else {
64+
doc.report().missing_field(
65+
"type",
66+
&format!(
67+
"Missing `type` field in the latest known document. Last known document id: {id}, ver: {last_doc_ver}."
68+
),
69+
);
70+
return Ok(false);
71+
};
72+
73+
let Ok(doc_type) = doc.doc_type() else {
74+
doc.report()
75+
.missing_field("type", &format!("Missing `type` field."));
76+
return Ok(false);
77+
};
78+
79+
if last_doc_type != doc_type {
80+
doc.report().functional_validation(
81+
&format!("New document type should be the same that the submitted latest known. New document type: {doc_type}, latest known ver: {last_doc_type}"),
82+
&format!("Document's type should be the same for all documents with the same id {id}"),
83+
);
84+
is_valid = false;
85+
}
86+
} else if ver != id {
87+
doc.report().functional_validation(
88+
&format!("`ver` and `id` are not equal, ver: {ver}, id: {id}. Document with `id` and `ver` being equal MUST exist"),
89+
"Cannot get a first version document from the provider, document for which `id` and `ver` are equal.",
90+
);
91+
is_valid = false;
5792
}
5893

5994
Ok(is_valid)
@@ -70,7 +105,7 @@ mod tests {
70105
use super::*;
71106
use crate::{
72107
builder::tests::Builder, metadata::SupportedField,
73-
providers::tests::TestCatalystSignedDocumentProvider, UuidV7,
108+
providers::tests::TestCatalystSignedDocumentProvider, UuidV4, UuidV7,
74109
};
75110

76111
#[test_case(
@@ -86,6 +121,7 @@ mod tests {
86121
)]
87122
#[test_case(
88123
|provider| {
124+
let doc_type = UuidV4::new();
89125
let now = SystemTime::now()
90126
.duration_since(SystemTime::UNIX_EPOCH)
91127
.unwrap()
@@ -96,6 +132,7 @@ mod tests {
96132
let first_doc = Builder::new()
97133
.with_metadata_field(SupportedField::Id(id))
98134
.with_metadata_field(SupportedField::Ver(id))
135+
.with_metadata_field(SupportedField::Type(doc_type.into()))
99136
.build();
100137
provider.add_document(None, &first_doc).unwrap();
101138
@@ -105,23 +142,26 @@ mod tests {
105142
Builder::new()
106143
.with_metadata_field(SupportedField::Id(id))
107144
.with_metadata_field(SupportedField::Ver(ver))
145+
.with_metadata_field(SupportedField::Type(doc_type.into()))
108146
.build()
109147
}
110148
=> true;
111149
"`ver` greater than `id`"
112150
)]
113151
#[test_case(
114152
|provider| {
153+
let doc_type = UuidV4::new();
115154
let now = SystemTime::now()
116-
.duration_since(SystemTime::UNIX_EPOCH)
117-
.unwrap()
118-
.as_secs();
155+
.duration_since(SystemTime::UNIX_EPOCH)
156+
.unwrap()
157+
.as_secs();
119158
let id = Uuid::new_v7(Timestamp::from_unix_time(now + 1, 0, 0, 0))
120159
.try_into()
121160
.unwrap();
122161
let first_doc = Builder::new()
123162
.with_metadata_field(SupportedField::Id(id))
124163
.with_metadata_field(SupportedField::Ver(id))
164+
.with_metadata_field(SupportedField::Type(doc_type.into()))
125165
.build();
126166
provider.add_document(None, &first_doc).unwrap();
127167
@@ -131,13 +171,55 @@ mod tests {
131171
Builder::new()
132172
.with_metadata_field(SupportedField::Id(id))
133173
.with_metadata_field(SupportedField::Ver(ver))
174+
.with_metadata_field(SupportedField::Type(doc_type.into()))
134175
.build()
135176
}
136177
=> false;
137178
"`ver` less than `id`"
138179
)]
180+
#[test_case(
181+
|provider| {
182+
let doc_type = UuidV4::new();
183+
let now = SystemTime::now()
184+
.duration_since(SystemTime::UNIX_EPOCH)
185+
.unwrap()
186+
.as_secs();
187+
let id = Uuid::new_v7(Timestamp::from_unix_time(now + 1, 0, 0, 0))
188+
.try_into()
189+
.unwrap();
190+
let doc = Builder::new()
191+
.with_metadata_field(SupportedField::Id(id))
192+
.with_metadata_field(SupportedField::Ver(id))
193+
.with_metadata_field(SupportedField::Type(doc_type.into()))
194+
.build();
195+
provider.add_document(None, &doc).unwrap();
196+
197+
198+
let ver = Uuid::new_v7(Timestamp::from_unix_time(now + 3, 0, 0, 0))
199+
.try_into()
200+
.unwrap();
201+
let doc = Builder::new()
202+
.with_metadata_field(SupportedField::Id(id))
203+
.with_metadata_field(SupportedField::Ver(ver))
204+
.with_metadata_field(SupportedField::Type(doc_type.into()))
205+
.build();
206+
provider.add_document(None, &doc).unwrap();
207+
208+
let ver = Uuid::new_v7(Timestamp::from_unix_time(now + 2, 0, 0, 0))
209+
.try_into()
210+
.unwrap();
211+
Builder::new()
212+
.with_metadata_field(SupportedField::Id(id))
213+
.with_metadata_field(SupportedField::Ver(ver))
214+
.with_metadata_field(SupportedField::Type(doc_type.into()))
215+
.build()
216+
}
217+
=> false;
218+
"`ver` less than `ver` field for of the latest known document"
219+
)]
139220
#[test_case(
140221
|_| {
222+
let doc_type = UuidV4::new();
141223
let now = SystemTime::now()
142224
.duration_since(SystemTime::UNIX_EPOCH)
143225
.unwrap()
@@ -151,11 +233,96 @@ mod tests {
151233
Builder::new()
152234
.with_metadata_field(SupportedField::Id(id))
153235
.with_metadata_field(SupportedField::Ver(ver))
236+
.with_metadata_field(SupportedField::Type(doc_type.into()))
154237
.build()
155238
}
156239
=> false;
157240
"missing first version document"
158241
)]
242+
#[test_case(
243+
|provider| {
244+
let doc_type = UuidV4::new();
245+
let now = SystemTime::now()
246+
.duration_since(SystemTime::UNIX_EPOCH)
247+
.unwrap()
248+
.as_secs();
249+
let id = Uuid::new_v7(Timestamp::from_unix_time(now - 1, 0, 0, 0))
250+
.try_into()
251+
.unwrap();
252+
let first_doc = Builder::new()
253+
.with_metadata_field(SupportedField::Id(id))
254+
.with_metadata_field(SupportedField::Ver(id))
255+
.with_metadata_field(SupportedField::Type(doc_type.into()))
256+
.build();
257+
provider.add_document(None, &first_doc).unwrap();
258+
259+
let ver = Uuid::new_v7(Timestamp::from_unix_time(now + 1, 0, 0, 0))
260+
.try_into()
261+
.unwrap();
262+
Builder::new()
263+
.with_metadata_field(SupportedField::Id(id))
264+
.with_metadata_field(SupportedField::Ver(ver))
265+
.build()
266+
}
267+
=> false;
268+
"missing `type` field"
269+
)]
270+
#[test_case(
271+
|provider| {
272+
let doc_type = UuidV4::new();
273+
let now = SystemTime::now()
274+
.duration_since(SystemTime::UNIX_EPOCH)
275+
.unwrap()
276+
.as_secs();
277+
let id = Uuid::new_v7(Timestamp::from_unix_time(now - 1, 0, 0, 0))
278+
.try_into()
279+
.unwrap();
280+
let first_doc = Builder::new()
281+
.with_metadata_field(SupportedField::Id(id))
282+
.with_metadata_field(SupportedField::Ver(id))
283+
.build();
284+
provider.add_document(None, &first_doc).unwrap();
285+
286+
let ver = Uuid::new_v7(Timestamp::from_unix_time(now + 1, 0, 0, 0))
287+
.try_into()
288+
.unwrap();
289+
Builder::new()
290+
.with_metadata_field(SupportedField::Id(id))
291+
.with_metadata_field(SupportedField::Ver(ver))
292+
.with_metadata_field(SupportedField::Type(doc_type.into()))
293+
.build()
294+
}
295+
=> false;
296+
"missing `type` field for the latest known document"
297+
)]
298+
#[test_case(
299+
|provider| {
300+
let now = SystemTime::now()
301+
.duration_since(SystemTime::UNIX_EPOCH)
302+
.unwrap()
303+
.as_secs();
304+
let id = Uuid::new_v7(Timestamp::from_unix_time(now - 1, 0, 0, 0))
305+
.try_into()
306+
.unwrap();
307+
let first_doc = Builder::new()
308+
.with_metadata_field(SupportedField::Id(id))
309+
.with_metadata_field(SupportedField::Ver(id))
310+
.with_metadata_field(SupportedField::Type(UuidV4::new().into()))
311+
.build();
312+
provider.add_document(None, &first_doc).unwrap();
313+
314+
let ver = Uuid::new_v7(Timestamp::from_unix_time(now + 1, 0, 0, 0))
315+
.try_into()
316+
.unwrap();
317+
Builder::new()
318+
.with_metadata_field(SupportedField::Id(id))
319+
.with_metadata_field(SupportedField::Ver(ver))
320+
.with_metadata_field(SupportedField::Type(UuidV4::new().into()))
321+
.build()
322+
}
323+
=> false;
324+
"diverge `type` field with the latest known document"
325+
)]
159326
#[test_case(
160327
|_| {
161328
Builder::new()

0 commit comments

Comments
 (0)