Skip to content

Commit e406d5f

Browse files
committed
Merge branch 'fix/immutable-roll-forward' of github.com:input-output-hk/catalyst-libs into fix/immutable-roll-forward
2 parents 5ac4e67 + 26442a8 commit e406d5f

File tree

8 files changed

+429
-118
lines changed

8 files changed

+429
-118
lines changed

rust/catalyst-types/src/id_uri/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use role_index::RoleIndex;
2828
///
2929
/// Identity of Catalyst Registration.
3030
/// Optionally also identifies a specific Signed Document Key
31-
#[derive(Debug, Clone, PartialEq, Hash)]
31+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3232
#[allow(clippy::module_name_repetitions)]
3333
pub struct IdUri {
3434
/// Username

rust/signed_doc/src/lib.rs

-111
Original file line numberDiff line numberDiff line change
@@ -275,114 +275,3 @@ impl TryFrom<CatalystSignedDocument> for Vec<u8> {
275275
Ok(minicbor::to_vec(value)?)
276276
}
277277
}
278-
279-
#[cfg(test)]
280-
mod tests {
281-
use std::str::FromStr;
282-
283-
use ed25519_dalek::{SigningKey, VerifyingKey};
284-
use metadata::{ContentEncoding, ContentType};
285-
use rand::rngs::OsRng;
286-
287-
use super::*;
288-
use crate::{providers::VerifyingKeyProvider, validator::validate_signatures};
289-
290-
fn test_metadata() -> (UuidV7, UuidV4, serde_json::Value) {
291-
let alg = Algorithm::EdDSA;
292-
let uuid_v7 = UuidV7::new();
293-
let uuid_v4 = UuidV4::new();
294-
let section = "$".to_string();
295-
let collabs = vec!["Alex1".to_string(), "Alex2".to_string()];
296-
let content_type = ContentType::Json;
297-
let content_encoding = ContentEncoding::Brotli;
298-
299-
let metadata_fields = serde_json::json!({
300-
"alg": alg.to_string(),
301-
"content-type": content_type.to_string(),
302-
"content-encoding": content_encoding.to_string(),
303-
"type": uuid_v4.to_string(),
304-
"id": uuid_v7.to_string(),
305-
"ver": uuid_v7.to_string(),
306-
"ref": {"id": uuid_v7.to_string()},
307-
"reply": {"id": uuid_v7.to_string(), "ver": uuid_v7.to_string()},
308-
"template": {"id": uuid_v7.to_string()},
309-
"section": section,
310-
"collabs": collabs,
311-
"campaign_id": {"id": uuid_v7.to_string()},
312-
"election_id": uuid_v4.to_string(),
313-
"brand_id": {"id": uuid_v7.to_string()},
314-
"category_id": {"id": uuid_v7.to_string()},
315-
});
316-
(uuid_v7, uuid_v4, metadata_fields)
317-
}
318-
319-
#[test]
320-
fn catalyst_signed_doc_cbor_roundtrip_test() {
321-
let (uuid_v7, uuid_v4, metadata_fields) = test_metadata();
322-
let content = serde_json::to_vec(&serde_json::Value::Null).unwrap();
323-
324-
let doc = Builder::new()
325-
.with_json_metadata(metadata_fields.clone())
326-
.unwrap()
327-
.with_decoded_content(content.clone())
328-
.build();
329-
330-
assert!(!doc.problem_report().is_problematic());
331-
332-
let bytes: Vec<u8> = doc.try_into().unwrap();
333-
let decoded: CatalystSignedDocument = bytes.as_slice().try_into().unwrap();
334-
335-
assert_eq!(decoded.doc_type().unwrap(), uuid_v4);
336-
assert_eq!(decoded.doc_id().unwrap(), uuid_v7);
337-
assert_eq!(decoded.doc_ver().unwrap(), uuid_v7);
338-
assert_eq!(decoded.doc_content().decoded_bytes().unwrap(), &content);
339-
// TODO: after this test will be moved as a crate integration test, enable this
340-
// assertion assert_eq!(decoded.doc_meta(), metadata_fields.extra());
341-
}
342-
343-
struct Provider(anyhow::Result<Option<VerifyingKey>>);
344-
impl VerifyingKeyProvider for Provider {
345-
async fn try_get_key(
346-
&self, _kid: &IdUri,
347-
) -> anyhow::Result<Option<ed25519_dalek::VerifyingKey>> {
348-
let res = self.0.as_ref().map_err(|e| anyhow::anyhow!("{e}"))?;
349-
Ok(*res)
350-
}
351-
}
352-
353-
#[tokio::test]
354-
async fn signature_verification_test() {
355-
let mut csprng = OsRng;
356-
let sk: SigningKey = SigningKey::generate(&mut csprng);
357-
let content = serde_json::to_vec(&serde_json::Value::Null).unwrap();
358-
let pk = sk.verifying_key();
359-
360-
let kid_str = format!(
361-
"id.catalyst://cardano/{}/0/0",
362-
base64_url::encode(pk.as_bytes())
363-
);
364-
365-
let kid = IdUri::from_str(&kid_str).unwrap();
366-
let (_, _, metadata) = test_metadata();
367-
let signed_doc = Builder::new()
368-
.with_decoded_content(content)
369-
.with_json_metadata(metadata)
370-
.unwrap()
371-
.add_signature(sk.to_bytes(), kid.clone())
372-
.unwrap()
373-
.build();
374-
assert!(!signed_doc.problem_report().is_problematic());
375-
376-
assert!(
377-
validate_signatures(&signed_doc, &Provider(Err(anyhow::anyhow!("some error"))))
378-
.await
379-
.is_err()
380-
);
381-
assert!(validate_signatures(&signed_doc, &Provider(Ok(Some(pk))))
382-
.await
383-
.unwrap());
384-
assert!(!validate_signatures(&signed_doc, &Provider(Ok(None)))
385-
.await
386-
.unwrap());
387-
}
388-
}

rust/signed_doc/src/providers.rs

+31-6
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,28 @@ pub trait CatalystSignedDocumentProvider: Send + Sync {
2323
) -> impl Future<Output = anyhow::Result<Option<CatalystSignedDocument>>> + Send;
2424
}
2525

26-
#[cfg(test)]
27-
pub(crate) mod tests {
26+
pub mod tests {
27+
//! Simple providers implementation just for the testing purposes
28+
2829
use std::collections::HashMap;
2930

3031
use catalyst_types::uuid::Uuid;
3132

32-
use super::*;
33+
use super::{
34+
CatalystSignedDocument, CatalystSignedDocumentProvider, DocumentRef, IdUri, VerifyingKey,
35+
VerifyingKeyProvider,
36+
};
3337

34-
/// Index documents only by `id` field
38+
/// Simple testing implementation of `CatalystSignedDocumentProvider`
3539
#[derive(Default)]
36-
pub(crate) struct TestCatalystSignedDocumentProvider(HashMap<Uuid, CatalystSignedDocument>);
40+
pub struct TestCatalystSignedDocumentProvider(HashMap<Uuid, CatalystSignedDocument>);
3741

3842
impl TestCatalystSignedDocumentProvider {
39-
pub(crate) fn add_document(&mut self, doc: CatalystSignedDocument) -> anyhow::Result<()> {
43+
/// Inserts document into the `TestCatalystSignedDocumentProvider`
44+
///
45+
/// # Errors
46+
/// - Missing document id
47+
pub fn add_document(&mut self, doc: CatalystSignedDocument) -> anyhow::Result<()> {
4048
self.0.insert(doc.doc_id()?.uuid(), doc);
4149
Ok(())
4250
}
@@ -49,4 +57,21 @@ pub(crate) mod tests {
4957
Ok(self.0.get(&doc_ref.id.uuid()).cloned())
5058
}
5159
}
60+
61+
/// Simple testing implementation of `VerifyingKeyProvider`
62+
#[derive(Default)]
63+
pub struct TestVerifyingKeyProvider(HashMap<IdUri, VerifyingKey>);
64+
65+
impl TestVerifyingKeyProvider {
66+
/// Inserts public key into the `TestVerifyingKeyProvider`
67+
pub fn add_pk(&mut self, kid: IdUri, pk: VerifyingKey) {
68+
self.0.insert(kid, pk);
69+
}
70+
}
71+
72+
impl VerifyingKeyProvider for TestVerifyingKeyProvider {
73+
async fn try_get_key(&self, kid: &IdUri) -> anyhow::Result<Option<VerifyingKey>> {
74+
Ok(self.0.get(kid).copied())
75+
}
76+
}
5277
}

rust/signed_doc/tests/comment.rs

+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
//! Integration test for comment document validation part.
2+
3+
use catalyst_signed_doc::{providers::tests::TestCatalystSignedDocumentProvider, *};
4+
5+
mod common;
6+
7+
#[tokio::test]
8+
async fn test_valid_comment_doc() {
9+
let (proposal_doc, proposal_doc_id) =
10+
common::create_dummy_doc(doc_types::PROPOSAL_DOCUMENT_UUID_TYPE).unwrap();
11+
let (template_doc, template_doc_id) =
12+
common::create_dummy_doc(doc_types::COMMENT_TEMPLATE_UUID_TYPE).unwrap();
13+
14+
let uuid_v7 = UuidV7::new();
15+
let (doc, ..) = common::create_dummy_signed_doc(Some(serde_json::json!({
16+
"alg": Algorithm::EdDSA.to_string(),
17+
"content-type": ContentType::Json.to_string(),
18+
"content-encoding": ContentEncoding::Brotli.to_string(),
19+
"type": doc_types::COMMENT_DOCUMENT_UUID_TYPE,
20+
"id": uuid_v7.to_string(),
21+
"ver": uuid_v7.to_string(),
22+
"template": {
23+
"id": template_doc_id
24+
},
25+
"ref": {
26+
"id": proposal_doc_id
27+
}
28+
})))
29+
.unwrap();
30+
31+
let mut provider = TestCatalystSignedDocumentProvider::default();
32+
provider.add_document(template_doc).unwrap();
33+
provider.add_document(proposal_doc).unwrap();
34+
35+
let is_valid = validator::validate(&doc, &provider).await.unwrap();
36+
37+
assert!(is_valid);
38+
}
39+
40+
#[tokio::test]
41+
async fn test_valid_comment_doc_with_reply() {
42+
let empty_json = serde_json::to_vec(&serde_json::json!({})).unwrap();
43+
44+
let (proposal_doc, proposal_doc_id) =
45+
common::create_dummy_doc(doc_types::PROPOSAL_DOCUMENT_UUID_TYPE).unwrap();
46+
let (template_doc, template_doc_id) =
47+
common::create_dummy_doc(doc_types::COMMENT_TEMPLATE_UUID_TYPE).unwrap();
48+
49+
let comment_doc_id = UuidV7::new();
50+
let comment_doc = Builder::new()
51+
.with_json_metadata(serde_json::json!({
52+
"id": comment_doc_id,
53+
"type": doc_types::COMMENT_DOCUMENT_UUID_TYPE,
54+
"content-type": ContentType::Json.to_string(),
55+
"template": { "id": comment_doc_id.to_string() },
56+
"ref": {
57+
"id": proposal_doc_id
58+
},
59+
}))
60+
.unwrap()
61+
.with_decoded_content(empty_json.clone())
62+
.build();
63+
64+
let uuid_v7 = UuidV7::new();
65+
let (doc, ..) = common::create_dummy_signed_doc(Some(serde_json::json!({
66+
"alg": Algorithm::EdDSA.to_string(),
67+
"content-type": ContentType::Json.to_string(),
68+
"content-encoding": ContentEncoding::Brotli.to_string(),
69+
"type": doc_types::COMMENT_DOCUMENT_UUID_TYPE,
70+
"id": uuid_v7.to_string(),
71+
"ver": uuid_v7.to_string(),
72+
"template": {
73+
"id": template_doc_id
74+
},
75+
"ref": {
76+
"id": proposal_doc_id
77+
},
78+
"reply": {
79+
"id": comment_doc_id,
80+
"ver": uuid_v7
81+
}
82+
})))
83+
.unwrap();
84+
85+
let mut provider = TestCatalystSignedDocumentProvider::default();
86+
provider.add_document(template_doc).unwrap();
87+
provider.add_document(proposal_doc).unwrap();
88+
provider.add_document(comment_doc).unwrap();
89+
90+
let is_valid = validator::validate(&doc, &provider).await.unwrap();
91+
92+
assert!(is_valid);
93+
}
94+
95+
#[tokio::test]
96+
async fn test_invalid_comment_doc() {
97+
let (proposal_doc, _) =
98+
common::create_dummy_doc(doc_types::PROPOSAL_DOCUMENT_UUID_TYPE).unwrap();
99+
let (template_doc, template_doc_id) =
100+
common::create_dummy_doc(doc_types::COMMENT_TEMPLATE_UUID_TYPE).unwrap();
101+
102+
let uuid_v7 = UuidV7::new();
103+
let (doc, ..) = common::create_dummy_signed_doc(Some(serde_json::json!({
104+
"alg": Algorithm::EdDSA.to_string(),
105+
"content-type": ContentType::Json.to_string(),
106+
"content-encoding": ContentEncoding::Brotli.to_string(),
107+
"type": doc_types::COMMENT_DOCUMENT_UUID_TYPE,
108+
"id": uuid_v7.to_string(),
109+
"ver": uuid_v7.to_string(),
110+
"template": {
111+
"id": template_doc_id
112+
},
113+
// without ref
114+
"ref": serde_json::Value::Null
115+
})))
116+
.unwrap();
117+
118+
let mut provider = TestCatalystSignedDocumentProvider::default();
119+
provider.add_document(template_doc).unwrap();
120+
provider.add_document(proposal_doc).unwrap();
121+
122+
let is_valid = validator::validate(&doc, &provider).await.unwrap();
123+
124+
assert!(!is_valid);
125+
}

rust/signed_doc/tests/common/mod.rs

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#![allow(dead_code)]
2+
3+
use std::str::FromStr;
4+
5+
use catalyst_signed_doc::*;
6+
7+
pub fn test_metadata() -> (UuidV7, UuidV4, serde_json::Value) {
8+
let uuid_v7 = UuidV7::new();
9+
let uuid_v4 = UuidV4::new();
10+
11+
let metadata_fields = serde_json::json!({
12+
"alg": Algorithm::EdDSA.to_string(),
13+
"content-type": ContentType::Json.to_string(),
14+
"content-encoding": ContentEncoding::Brotli.to_string(),
15+
"type": uuid_v4.to_string(),
16+
"id": uuid_v7.to_string(),
17+
"ver": uuid_v7.to_string(),
18+
"ref": {"id": uuid_v7.to_string()},
19+
"reply": {"id": uuid_v7.to_string(), "ver": uuid_v7.to_string()},
20+
"template": {"id": uuid_v7.to_string()},
21+
"section": "$".to_string(),
22+
"collabs": vec!["Alex1".to_string(), "Alex2".to_string()],
23+
"campaign_id": {"id": uuid_v7.to_string()},
24+
"election_id": uuid_v4.to_string(),
25+
"brand_id": {"id": uuid_v7.to_string()},
26+
"category_id": {"id": uuid_v7.to_string()},
27+
});
28+
29+
(uuid_v7, uuid_v4, metadata_fields)
30+
}
31+
32+
pub fn create_dummy_key_pair() -> anyhow::Result<(
33+
ed25519_dalek::SigningKey,
34+
ed25519_dalek::VerifyingKey,
35+
IdUri,
36+
)> {
37+
let sk = create_signing_key();
38+
let pk = sk.verifying_key();
39+
let kid = IdUri::from_str(&format!(
40+
"id.catalyst://cardano/{}/0/0",
41+
base64_url::encode(pk.as_bytes())
42+
))?;
43+
44+
Ok((sk, pk, kid))
45+
}
46+
47+
pub fn create_dummy_doc(doc_type_id: Uuid) -> anyhow::Result<(CatalystSignedDocument, UuidV7)> {
48+
let empty_json = serde_json::to_vec(&serde_json::json!({}))?;
49+
50+
let doc_id = UuidV7::new();
51+
52+
let doc = Builder::new()
53+
.with_json_metadata(serde_json::json!({
54+
"id": doc_id,
55+
"type": doc_type_id,
56+
"content-type": ContentType::Json.to_string(),
57+
"template": { "id": doc_id.to_string() }
58+
}))?
59+
.with_decoded_content(empty_json.clone())
60+
.build();
61+
62+
Ok((doc, doc_id))
63+
}
64+
65+
pub fn create_signing_key() -> ed25519_dalek::SigningKey {
66+
let mut csprng = rand::rngs::OsRng;
67+
ed25519_dalek::SigningKey::generate(&mut csprng)
68+
}
69+
70+
pub fn create_dummy_signed_doc(
71+
with_metadata: Option<serde_json::Value>,
72+
) -> anyhow::Result<(CatalystSignedDocument, ed25519_dalek::VerifyingKey, IdUri)> {
73+
let (sk, pk, kid) = create_dummy_key_pair()?;
74+
75+
let content = serde_json::to_vec(&serde_json::Value::Null)?;
76+
let (_, _, metadata) = test_metadata();
77+
78+
let signed_doc = Builder::new()
79+
.with_decoded_content(content)
80+
.with_json_metadata(with_metadata.unwrap_or(metadata))?
81+
.add_signature(sk.to_bytes(), kid.clone())?
82+
.build();
83+
84+
Ok((signed_doc, pk, kid))
85+
}

0 commit comments

Comments
 (0)