Skip to content

Commit fc8d5ba

Browse files
committed
make all validation async
1 parent c287e3a commit fc8d5ba

File tree

7 files changed

+110
-73
lines changed

7 files changed

+110
-73
lines changed

rust/signed_doc/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ strum = { version = "0.26.3", features = ["derive"] }
2424
clap = { version = "4.5.23", features = ["derive", "env"] }
2525
jsonschema = "0.28.3"
2626
jsonpath-rust = "0.7.5"
27-
27+
futures = "0.3.31"
2828

2929
[dev-dependencies]
3030
base64-url = "3.0.0"

rust/signed_doc/src/doc_types/comment_document.rs

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22
//! <https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/catalyst_docs/comment/#comment-document>
33
44
use catalyst_types::{problem_report::ProblemReport, uuid::Uuid};
5+
use futures::{future::BoxFuture, FutureExt};
56

67
use crate::{
78
doc_types::{COMMENT_TEMPLATE_UUID_TYPE, PROPOSAL_DOCUMENT_UUID_TYPE},
89
error::CatalystSignedDocError,
910
metadata::{ContentEncoding, ContentType},
11+
providers::CatalystSignedDocumentProvider,
1012
validator::{utils::validate_provided_doc, StatefullValidation, StatelessValidation},
1113
CatalystSignedDocument, DocumentRef,
1214
};
@@ -40,11 +42,18 @@ impl StatelessValidation for CommentDocument {
4042
];
4143
}
4244

43-
impl<DocProvider> StatefullValidation<DocProvider> for CommentDocument
44-
where DocProvider: 'static + Fn(&DocumentRef) -> Option<CatalystSignedDocument>
45+
impl<Provider> StatefullValidation<Provider> for CommentDocument
46+
where Provider: 'static + CatalystSignedDocumentProvider
4547
{
46-
const STATEFULL_RULES: &[crate::validator::StatefullRule<Self, DocProvider>] =
47-
&[template_full_check, ref_full_check, reply_full_check];
48+
fn rules<'a>(
49+
&'a self, provider: &'a Provider, report: &'a ProblemReport,
50+
) -> Vec<BoxFuture<'a, anyhow::Result<bool>>> {
51+
vec![
52+
template_statefull_check(self, provider, report).boxed(),
53+
ref_statefull_check(self, provider, report).boxed(),
54+
reply_statefull_check(self, provider, report).boxed(),
55+
]
56+
}
4857
}
4958

5059
/// `type` field validation
@@ -116,10 +125,10 @@ fn reply_stateless_check(doc: &CatalystSignedDocument, report: &ProblemReport) -
116125
}
117126

118127
/// `template` statefull validation
119-
fn template_full_check<DocProvider>(
120-
doc: &CommentDocument, provider: &DocProvider, report: &ProblemReport,
121-
) -> bool
122-
where DocProvider: Fn(&DocumentRef) -> Option<CatalystSignedDocument> {
128+
async fn template_statefull_check<Provider>(
129+
doc: &CommentDocument, provider: &Provider, report: &ProblemReport,
130+
) -> anyhow::Result<bool>
131+
where Provider: 'static + CatalystSignedDocumentProvider {
123132
let template_validator = |template_doc: CatalystSignedDocument| {
124133
if template_doc.doc_type().uuid() != COMMENT_TEMPLATE_UUID_TYPE {
125134
report.invalid_value(
@@ -167,13 +176,14 @@ where DocProvider: Fn(&DocumentRef) -> Option<CatalystSignedDocument> {
167176
report,
168177
template_validator,
169178
)
179+
.await
170180
}
171181

172182
/// `ref` statefull validation
173-
fn ref_full_check<DocProvider>(
174-
doc: &CommentDocument, provider: &DocProvider, report: &ProblemReport,
175-
) -> bool
176-
where DocProvider: Fn(&DocumentRef) -> Option<CatalystSignedDocument> {
183+
async fn ref_statefull_check<Provider>(
184+
doc: &CommentDocument, provider: &Provider, report: &ProblemReport,
185+
) -> anyhow::Result<bool>
186+
where Provider: 'static + CatalystSignedDocumentProvider {
177187
let ref_validator = |proposal_doc: CatalystSignedDocument| -> bool {
178188
if proposal_doc.doc_type().uuid() != PROPOSAL_DOCUMENT_UUID_TYPE {
179189
report.invalid_value(
@@ -186,14 +196,14 @@ where DocProvider: Fn(&DocumentRef) -> Option<CatalystSignedDocument> {
186196
}
187197
true
188198
};
189-
validate_provided_doc(&doc.doc_ref, "Proposal", provider, report, ref_validator)
199+
validate_provided_doc(&doc.doc_ref, "Proposal", provider, report, ref_validator).await
190200
}
191201

192202
/// `reply` statefull validation
193-
fn reply_full_check<DocProvider>(
194-
doc: &CommentDocument, provider: &DocProvider, report: &ProblemReport,
195-
) -> bool
196-
where DocProvider: Fn(&DocumentRef) -> Option<CatalystSignedDocument> {
203+
async fn reply_statefull_check<Provider>(
204+
doc: &CommentDocument, provider: &Provider, report: &ProblemReport,
205+
) -> anyhow::Result<bool>
206+
where Provider: 'static + CatalystSignedDocumentProvider {
197207
if let Some(reply) = &doc.reply {
198208
let reply_validator = |comment_doc: CatalystSignedDocument| -> bool {
199209
if comment_doc.doc_type().uuid() != COMMENT_DOCUMENT_UUID_TYPE {
@@ -222,9 +232,9 @@ where DocProvider: Fn(&DocumentRef) -> Option<CatalystSignedDocument> {
222232

223233
true
224234
};
225-
return validate_provided_doc(reply, "Comment", provider, report, reply_validator);
235+
return validate_provided_doc(reply, "Comment", provider, report, reply_validator).await;
226236
}
227-
true
237+
Ok(true)
228238
}
229239

230240
impl CommentDocument {

rust/signed_doc/src/doc_types/proposal_document.rs

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
//! Proposal Document object implementation
22
//! <https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/catalyst_docs/proposal/#proposal-document>
33
4+
#![allow(dead_code)]
5+
46
use catalyst_types::{problem_report::ProblemReport, uuid::Uuid};
57

68
use super::{CATEGORY_DOCUMENT_UUID_TYPE, PROPOSAL_TEMPLATE_UUID_TYPE};
79
use crate::{
810
error::CatalystSignedDocError,
911
metadata::{ContentEncoding, ContentType},
12+
providers::CatalystSignedDocumentProvider,
1013
validator::{
11-
utils::validate_provided_doc, StatefullRule, StatefullValidation, StatelessRule,
12-
StatelessValidation,
14+
utils::validate_provided_doc, StatefullValidation, StatelessRule, StatelessValidation,
1315
},
1416
CatalystSignedDocument, DocumentRef,
1517
};
@@ -37,11 +39,15 @@ impl StatelessValidation for ProposalDocument {
3739
];
3840
}
3941

40-
impl<DocProvider> StatefullValidation<DocProvider> for ProposalDocument
41-
where DocProvider: 'static + Fn(&DocumentRef) -> Option<CatalystSignedDocument>
42+
impl<Provider> StatefullValidation<Provider> for ProposalDocument
43+
where Provider: 'static + CatalystSignedDocumentProvider
4244
{
43-
const STATEFULL_RULES: &[StatefullRule<Self, DocProvider>] =
44-
&[template_full_check, category_full_check];
45+
fn rules(
46+
&self, _provider: &Provider, _report: &ProblemReport,
47+
) -> Vec<std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<bool>> + Send>>>
48+
{
49+
vec![]
50+
}
4551
}
4652

4753
/// `type` field validation
@@ -104,10 +110,10 @@ fn template_stateless_check(doc: &CatalystSignedDocument, report: &ProblemReport
104110
}
105111

106112
/// `template` statefull validation
107-
fn template_full_check<DocProvider>(
108-
doc: &ProposalDocument, provider: &DocProvider, report: &ProblemReport,
109-
) -> bool
110-
where DocProvider: Fn(&DocumentRef) -> Option<CatalystSignedDocument> {
113+
async fn template_statefull_check<Provider>(
114+
doc: &ProposalDocument, provider: &Provider, report: &ProblemReport,
115+
) -> anyhow::Result<bool>
116+
where Provider: 'static + CatalystSignedDocumentProvider {
111117
let template_validator = |template_doc: CatalystSignedDocument| {
112118
if template_doc.doc_type().uuid() != PROPOSAL_TEMPLATE_UUID_TYPE {
113119
report.invalid_value(
@@ -154,13 +160,14 @@ where DocProvider: Fn(&DocumentRef) -> Option<CatalystSignedDocument> {
154160
report,
155161
template_validator,
156162
)
163+
.await
157164
}
158165

159166
/// `category_id` statefull validation
160-
fn category_full_check<DocProvider>(
161-
doc: &ProposalDocument, provider: &DocProvider, report: &ProblemReport,
162-
) -> bool
163-
where DocProvider: Fn(&DocumentRef) -> Option<CatalystSignedDocument> {
167+
async fn category_statefull_check<Provider>(
168+
doc: &ProposalDocument, provider: &Provider, report: &ProblemReport,
169+
) -> anyhow::Result<bool>
170+
where Provider: 'static + CatalystSignedDocumentProvider {
164171
if let Some(category) = &doc.category {
165172
let category_validator = |category_doc: CatalystSignedDocument| -> bool {
166173
if category_doc.doc_type().uuid() != CATEGORY_DOCUMENT_UUID_TYPE {
@@ -174,9 +181,10 @@ where DocProvider: Fn(&DocumentRef) -> Option<CatalystSignedDocument> {
174181
}
175182
true
176183
};
177-
return validate_provided_doc(category, "Category", provider, report, category_validator);
184+
return validate_provided_doc(category, "Category", provider, report, category_validator)
185+
.await;
178186
}
179-
true
187+
Ok(true)
180188
}
181189

182190
impl ProposalDocument {

rust/signed_doc/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ impl CatalystSignedDocument {
161161
};
162162

163163
for (signature, kid) in self.signatures().cose_signatures_with_kids() {
164-
match provider.try_get_vk(kid).await {
164+
match provider.try_get_key(kid).await {
165165
Ok(Some(pk)) => {
166166
let tbs_data = cose_sign.tbs_data(&[], signature);
167167
match signature.signature.as_slice().try_into() {
@@ -413,7 +413,7 @@ mod tests {
413413

414414
struct Provider(anyhow::Result<Option<VerifyingKey>>);
415415
impl VerifyingKeyProvider for Provider {
416-
async fn try_get_vk(
416+
async fn try_get_key(
417417
&self, _kid: &IdUri,
418418
) -> anyhow::Result<Option<ed25519_dalek::VerifyingKey>> {
419419
let res = self.0.as_ref().map_err(|e| anyhow::anyhow!("{e}"))?;

rust/signed_doc/src/providers.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,20 @@ use std::future::Future;
55
use catalyst_types::id_uri::IdUri;
66
use ed25519_dalek::VerifyingKey;
77

8+
use crate::{CatalystSignedDocument, DocumentRef};
9+
810
/// `VerifyingKey` Provider trait
911
pub trait VerifyingKeyProvider {
1012
/// Try to get `VerifyingKey`
11-
fn try_get_vk(&self, kid: &IdUri)
12-
-> impl Future<Output = anyhow::Result<Option<VerifyingKey>>>;
13+
fn try_get_key(
14+
&self, kid: &IdUri,
15+
) -> impl Future<Output = anyhow::Result<Option<VerifyingKey>>>;
16+
}
17+
18+
/// `CatalystSignedDocument` Provider trait
19+
pub trait CatalystSignedDocumentProvider: Send + Sync {
20+
/// Try to get `CatalystSignedDocument`
21+
fn try_get_doc(
22+
&self, doc_ref: &DocumentRef,
23+
) -> impl Future<Output = anyhow::Result<Option<CatalystSignedDocument>>> + Send;
1324
}

rust/signed_doc/src/validator/mod.rs

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,20 @@
22
33
pub(crate) mod utils;
44

5+
use std::future::Future;
6+
57
use catalyst_types::problem_report::ProblemReport;
8+
use futures::future::BoxFuture;
69

710
use crate::{
811
doc_types::{CommentDocument, DocumentType, ProposalDocument},
912
error::CatalystSignedDocError,
10-
CatalystSignedDocument, DocumentRef,
13+
providers::CatalystSignedDocumentProvider,
14+
CatalystSignedDocument,
1115
};
1216

1317
/// Stateless validation function rule type
14-
pub(crate) type StatelessRule = fn(&CatalystSignedDocument, &ProblemReport) -> bool;
15-
/// Statefull validation function rule type
16-
pub(crate) type StatefullRule<DocType, DocProvider> =
17-
fn(&DocType, &DocProvider, &ProblemReport) -> bool;
18+
pub type StatelessRule = fn(&CatalystSignedDocument, &ProblemReport) -> bool;
1819

1920
/// Trait for defining a stateless validation rules.
2021
pub trait StatelessValidation
@@ -33,24 +34,31 @@ where Self: 'static
3334
}
3435

3536
/// Trait for defining a statefull validation rules.
36-
pub trait StatefullValidation<DocProvider>
37+
pub trait StatefullValidation<Provider>
3738
where
3839
Self: 'static,
39-
DocProvider: 'static + Fn(&DocumentRef) -> Option<CatalystSignedDocument>,
40+
Provider: 'static + CatalystSignedDocumentProvider,
4041
{
41-
/// Statefull validation rules
42-
const STATEFULL_RULES: &[StatefullRule<Self, DocProvider>];
42+
/// Statefull validation rules list
43+
fn rules<'a>(
44+
&'a self, provider: &'a Provider, report: &'a ProblemReport,
45+
) -> Vec<BoxFuture<'a, anyhow::Result<bool>>>;
4346

4447
/// Perform a statefull validation, collecting a problem report
4548
///
4649
/// # Errors
47-
/// Returns an error if `provider` will return an error, fails fast in this case.
48-
fn validate(&self, provider: &DocProvider, report: &ProblemReport) -> anyhow::Result<bool> {
49-
let res = Self::STATEFULL_RULES
50-
.iter()
51-
.map(|rule| rule(self, provider, report))
52-
.all(|res| res);
53-
Ok(res)
50+
/// Returns an error if `provider` return an error.
51+
fn validate(
52+
&self, provider: &Provider, report: &ProblemReport,
53+
) -> impl Future<Output = anyhow::Result<bool>> {
54+
async {
55+
for res in futures::future::join_all(self.rules(provider, report)).await {
56+
if !(res?) {
57+
return Ok(false);
58+
}
59+
}
60+
Ok(true)
61+
}
5462
}
5563
}
5664

@@ -62,10 +70,10 @@ where
6270
/// Returns a report of validation failures and the source error.
6371
/// If `provider` returns error, fails fast and placed this error into
6472
/// `CatalystSignedDocError::error`.
65-
pub fn validate<DocProvider>(
66-
doc: &CatalystSignedDocument, provider: &DocProvider,
73+
pub async fn validate<Provider>(
74+
doc: &CatalystSignedDocument, provider: &Provider,
6775
) -> Result<(), CatalystSignedDocError>
68-
where DocProvider: 'static + Fn(&DocumentRef) -> Option<CatalystSignedDocument> {
76+
where Provider: 'static + CatalystSignedDocumentProvider {
6977
let report = ProblemReport::new("Catalyst Signed Document Validation");
7078

7179
let doc_type: DocumentType = match doc.doc_type().try_into() {
@@ -84,7 +92,7 @@ where DocProvider: 'static + Fn(&DocumentRef) -> Option<CatalystSignedDocument>
8492
},
8593
};
8694

87-
match validate_inner(doc_type, doc, provider, &report) {
95+
match validate_inner(doc_type, doc, provider, &report).await {
8896
Ok(()) if report.is_problematic() => {
8997
Err(CatalystSignedDocError::new(
9098
report,
@@ -103,23 +111,23 @@ where DocProvider: 'static + Fn(&DocumentRef) -> Option<CatalystSignedDocument>
103111
///
104112
/// If `provider` returns error, fails fast and placed this error into
105113
/// `CatalystSignedDocError::error`.
106-
fn validate_inner<DocProvider>(
107-
doc_type: DocumentType, doc: &CatalystSignedDocument, provider: &DocProvider,
114+
async fn validate_inner<Provider>(
115+
doc_type: DocumentType, doc: &CatalystSignedDocument, provider: &Provider,
108116
report: &ProblemReport,
109117
) -> anyhow::Result<()>
110118
where
111-
DocProvider: 'static + Fn(&DocumentRef) -> Option<CatalystSignedDocument>,
119+
Provider: 'static + CatalystSignedDocumentProvider,
112120
{
113121
#[allow(clippy::match_same_arms)]
114122
match doc_type {
115123
DocumentType::ProposalDocument => {
116124
let doc = ProposalDocument::from_signed_doc(doc, report)?;
117-
doc.validate(provider, report)?;
125+
doc.validate(provider, report).await?;
118126
},
119127
DocumentType::ProposalTemplate => {},
120128
DocumentType::CommentDocument => {
121129
let doc = CommentDocument::from_signed_doc(doc, report)?;
122-
doc.validate(provider, report)?;
130+
doc.validate(provider, report).await?;
123131
},
124132
DocumentType::CommentTemplate => {},
125133
DocumentType::ReviewDocument => {},

rust/signed_doc/src/validator/utils.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,25 @@
22
33
use catalyst_types::problem_report::ProblemReport;
44

5-
use crate::{CatalystSignedDocument, DocumentRef};
5+
use crate::{providers::CatalystSignedDocumentProvider, CatalystSignedDocument, DocumentRef};
66

77
/// A helper validation document function, which validates a document from the
88
/// `ValidationDataProvider`.
9-
pub(crate) fn validate_provided_doc<DocProvider, Validator>(
10-
doc_ref: &DocumentRef, doc_name: &str, provider: &DocProvider, report: &ProblemReport,
9+
pub(crate) async fn validate_provided_doc<Provider, Validator>(
10+
doc_ref: &DocumentRef, doc_name: &str, provider: &Provider, report: &ProblemReport,
1111
validator: Validator,
12-
) -> bool
12+
) -> anyhow::Result<bool>
1313
where
14-
DocProvider: Fn(&DocumentRef) -> Option<CatalystSignedDocument>,
14+
Provider: 'static + CatalystSignedDocumentProvider,
1515
Validator: Fn(CatalystSignedDocument) -> bool,
1616
{
17-
if let Some(doc) = provider(doc_ref) {
18-
validator(doc)
17+
if let Some(doc) = provider.try_get_doc(doc_ref).await? {
18+
Ok(validator(doc))
1919
} else {
2020
report.functional_validation(
2121
format!("Cannot retrieve a {doc_name} document {doc_ref}").as_str(),
2222
"Validation data provider could not return a corresponding {doc_name}.",
2323
);
24-
false
24+
Ok(false)
2525
}
2626
}

0 commit comments

Comments
 (0)