Skip to content

fix(rust/signed-doc): Update doc type #365

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Jun 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 90 additions & 19 deletions rust/signed_doc/src/doc_types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,52 +4,123 @@
use std::sync::LazyLock;

use catalyst_types::uuid::Uuid;
use deprecated::{
COMMENT_DOCUMENT_UUID_TYPE, PROPOSAL_ACTION_DOCUMENT_UUID_TYPE, PROPOSAL_DOCUMENT_UUID_TYPE,
};

use crate::DocType;

/// -------------- Document Types --------------
/// Brand document type.
#[allow(clippy::expect_used)]
pub static BRAND_PARAMETERS: LazyLock<DocType> = LazyLock::new(|| {
let ids = &[BRAND_BASE_TYPE];
ids.to_vec()
.try_into()
.expect("Failed to convert brand base types Uuid to DocType")
});

/// Campaign Parameters document type.
#[allow(clippy::expect_used)]
pub static CAMPAIGN_PARAMETERS: LazyLock<DocType> = LazyLock::new(|| {
let ids = &[CAMPAIGN_BASE_TYPE];
ids.to_vec()
.try_into()
.expect("Failed to convert campaign base types Uuid to DocType")
});

/// Category Parameters document type.
#[allow(clippy::expect_used)]
pub static CATEGORY_PARAMETERS: LazyLock<DocType> = LazyLock::new(|| {
let ids = &[CATEGORY_BASE_TYPE];
ids.to_vec()
.try_into()
.expect("Failed to convert category base types Uuid to DocType")
});

/// Proposal document type.
#[allow(clippy::expect_used)]
pub static PROPOSAL_DOC_TYPE: LazyLock<DocType> = LazyLock::new(|| {
let ids = &[PROPOSAL_UUID_TYPE];
pub static PROPOSAL: LazyLock<DocType> = LazyLock::new(|| {
let ids = &[PROPOSAL_BASE_TYPE];
ids.to_vec()
.try_into()
.expect("Failed to convert proposal document Uuid to DocType")
});

/// Proposal comment document type.
#[allow(clippy::expect_used)]
pub static PROPOSAL_COMMENT_DOC: LazyLock<DocType> = LazyLock::new(|| {
let ids = &[COMMENT_UUID_TYPE, PROPOSAL_UUID_TYPE];
pub static PROPOSAL_COMMENT: LazyLock<DocType> = LazyLock::new(|| {
let ids = &[COMMENT_BASE_TYPE, PROPOSAL_BASE_TYPE];
ids.to_vec()
.try_into()
.expect("Failed to convert proposal comment document Uuid to DocType")
});

/// Proposal action document type.
#[allow(clippy::expect_used)]
pub static PROPOSAL_ACTION_DOC: LazyLock<DocType> = LazyLock::new(|| {
pub static PROPOSAL_SUBMISSION_ACTION: LazyLock<DocType> = LazyLock::new(|| {
let ids = &[
ACTION_UUID_TYPE,
PROPOSAL_UUID_TYPE,
SUBMISSION_ACTION_UUID_TYPE,
ACTION_BASE_TYPE,
PROPOSAL_BASE_TYPE,
SUBMISSION_ACTION_BASE_TYPE,
];
ids.to_vec()
.try_into()
.expect("Failed to convert proposal action document Uuid to DocType")
});

/// Submission Action UUID type.
pub const SUBMISSION_ACTION_UUID_TYPE: Uuid =
/// Proposal Comment Meta Template document type.
#[allow(clippy::expect_used)]
pub static PROPOSAL_COMMENT_META_TEMPLATE: LazyLock<DocType> = LazyLock::new(|| {
let ids = &[
TEMPLATE_BASE_TYPE,
TEMPLATE_BASE_TYPE,
COMMENT_BASE_TYPE,
PROPOSAL_BASE_TYPE,
];
ids.to_vec()
.try_into()
.expect("Failed to convert proposal comment meta template document Uuid to DocType")
});

/// Proposal Comment Template document type.
#[allow(clippy::expect_used)]
pub static PROPOSAL_COMMENT_TEMPLATE: LazyLock<DocType> = LazyLock::new(|| {
let ids = &[TEMPLATE_BASE_TYPE, COMMENT_BASE_TYPE, PROPOSAL_BASE_TYPE];
ids.to_vec()
.try_into()
.expect("Failed to convert proposal comment template document Uuid to DocType")
});

/// Proposal Template document type.
#[allow(clippy::expect_used)]
pub static PROPOSAL_TEMPLATE: LazyLock<DocType> = LazyLock::new(|| {
let ids = &[TEMPLATE_BASE_TYPE, PROPOSAL_BASE_TYPE];
ids.to_vec()
.try_into()
.expect("Failed to convert proposal template document Uuid to DocType")
});

/// -------------- Base Types --------------
/// Action UUID base type.
pub const ACTION_BASE_TYPE: Uuid = Uuid::from_u128(0x5E60_E623_AD02_4A1B_A1AC_406D_B978_EE48);
/// Brand UUID base type.
pub const BRAND_BASE_TYPE: Uuid = Uuid::from_u128(0xEBCA_BEEB_5BC5_4F95_91E8_CAB8_CA72_4172);
/// Campaign UUID base type.
pub const CAMPAIGN_BASE_TYPE: Uuid = Uuid::from_u128(0x5EF3_2D5D_F240_462C_A7A4_BA4A_F221_FA23);
/// Category UUID base type.
pub const CATEGORY_BASE_TYPE: Uuid = Uuid::from_u128(0x8189_38C3_3139_4DAA_AFE6_974C_7848_8E95);
/// Comment UUID base type.
pub const COMMENT_BASE_TYPE: Uuid = Uuid::from_u128(0xB679_DED3_0E7C_41BA_89F8_DA62_A178_98EA);
/// Decision UUID base type.
pub const DECISION_BASE_TYPE: Uuid = Uuid::from_u128(0x788F_F4C6_D65A_451F_BB33_575F_E056_B411);
/// Moderation Action UUID base type.
pub const MODERATION_ACTION_BASE_TYPE: Uuid =
Uuid::from_u128(0xA5D2_32B8_5E03_4117_9AFD_BE32_B878_FCDD);
/// Proposal UUID base type.
pub const PROPOSAL_BASE_TYPE: Uuid = Uuid::from_u128(0x7808_D2BA_D511_40AF_84E8_C0D1_625F_DFDC);
/// Submission Action UUID base type.
pub const SUBMISSION_ACTION_BASE_TYPE: Uuid =
Uuid::from_u128(0x7892_7329_CFD9_4EA1_9C71_0E01_9B12_6A65);
/// Proposal UUID type.
pub const PROPOSAL_UUID_TYPE: Uuid = PROPOSAL_DOCUMENT_UUID_TYPE;
/// Comment UUID type.
pub const COMMENT_UUID_TYPE: Uuid = COMMENT_DOCUMENT_UUID_TYPE;
/// Action UUID type.
pub const ACTION_UUID_TYPE: Uuid = PROPOSAL_ACTION_DOCUMENT_UUID_TYPE;
/// Template UUID base type.
pub const TEMPLATE_BASE_TYPE: Uuid = Uuid::from_u128(0x0CE8_AB38_9258_4FBC_A62E_7FAA_6E58_318F);

/// Document type which will be deprecated.
pub mod deprecated {
Expand Down
84 changes: 64 additions & 20 deletions rust/signed_doc/src/metadata/doc_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,15 @@ use std::{
hash::{Hash, Hasher},
};

use catalyst_types::uuid::{CborContext, Uuid, UuidV4};
use catalyst_types::uuid::{CborContext, Uuid, UuidV4, UUID_CBOR_TAG};
use coset::cbor::Value;
use minicbor::{Decode, Decoder, Encode};
use serde::{Deserialize, Deserializer};
use tracing::warn;

use crate::{
decode_context::{CompatibilityPolicy, DecodeContext},
doc_types::{
ACTION_UUID_TYPE, COMMENT_UUID_TYPE, PROPOSAL_ACTION_DOC, PROPOSAL_COMMENT_DOC,
PROPOSAL_DOC_TYPE, PROPOSAL_UUID_TYPE,
},
doc_types::{deprecated, PROPOSAL, PROPOSAL_COMMENT, PROPOSAL_SUBMISSION_ACTION},
};

/// List of `UUIDv4` document type.
Expand Down Expand Up @@ -87,6 +85,18 @@ impl TryFrom<Vec<Uuid>> for DocType {
}
}

impl From<DocType> for Vec<Uuid> {
fn from(value: DocType) -> Vec<Uuid> {
value.0.into_iter().map(Uuid::from).collect()
}
}

impl From<DocType> for Vec<String> {
fn from(val: DocType) -> Self {
val.0.into_iter().map(|uuid| uuid.to_string()).collect()
}
}

impl TryFrom<Vec<UuidV4>> for DocType {
type Error = DocTypeError;

Expand Down Expand Up @@ -231,9 +241,11 @@ impl Decode<'_, DecodeContext<'_>> for DocType {
/// <https://github.com/input-output-hk/catalyst-libs/blob/main/docs/src/architecture/08_concepts/signed_doc/types.md#document-types>
fn map_doc_type(uuid: UuidV4) -> DocType {
match uuid {
id if Uuid::from(id) == PROPOSAL_UUID_TYPE => PROPOSAL_DOC_TYPE.clone(),
id if Uuid::from(id) == COMMENT_UUID_TYPE => PROPOSAL_COMMENT_DOC.clone(),
id if Uuid::from(id) == ACTION_UUID_TYPE => PROPOSAL_ACTION_DOC.clone(),
id if Uuid::from(id) == deprecated::PROPOSAL_DOCUMENT_UUID_TYPE => PROPOSAL.clone(),
id if Uuid::from(id) == deprecated::COMMENT_DOCUMENT_UUID_TYPE => PROPOSAL_COMMENT.clone(),
id if Uuid::from(id) == deprecated::PROPOSAL_ACTION_DOCUMENT_UUID_TYPE => {
PROPOSAL_SUBMISSION_ACTION.clone()
},
id => DocType(vec![id]),
}
}
Expand Down Expand Up @@ -284,15 +296,35 @@ impl<'de> Deserialize<'de> for DocType {
}
}

impl From<DocType> for Value {
fn from(value: DocType) -> Self {
Value::Array(
value
.0
.iter()
.map(|uuidv4| {
Value::Tag(
UUID_CBOR_TAG,
Box::new(Value::Bytes(uuidv4.uuid().as_bytes().to_vec())),
)
})
.collect(),
)
}
}

// This is needed to preserve backward compatibility with the old solution.
impl PartialEq for DocType {
fn eq(&self, other: &Self) -> bool {
// List of special-case (single UUID) -> new DocType
// The old one should equal to the new one
let special_cases = [
(PROPOSAL_UUID_TYPE, &*PROPOSAL_DOC_TYPE),
(COMMENT_UUID_TYPE, &*PROPOSAL_COMMENT_DOC),
(ACTION_UUID_TYPE, &*PROPOSAL_ACTION_DOC),
(deprecated::PROPOSAL_DOCUMENT_UUID_TYPE, &*PROPOSAL),
(deprecated::COMMENT_DOCUMENT_UUID_TYPE, &*PROPOSAL_COMMENT),
(
deprecated::PROPOSAL_ACTION_DOCUMENT_UUID_TYPE,
&*PROPOSAL_SUBMISSION_ACTION,
),
];
for (uuid, expected) in special_cases {
match DocType::try_from(uuid) {
Expand Down Expand Up @@ -416,21 +448,33 @@ mod tests {
assert!(matches!(result, Err(DocTypeError::StringConversion(s)) if s == "not-a-uuid"));
}

#[test]
fn test_doc_type_to_value() {
let uuid = uuid::Uuid::new_v4();
let doc_type: Value = DocType(vec![UuidV4::try_from(uuid).unwrap()]).into();

for d in &doc_type.into_array().unwrap() {
let t = d.clone().into_tag().unwrap();
assert_eq!(t.0, UUID_CBOR_TAG);
assert_eq!(t.1.as_bytes().unwrap().len(), 16);
}
}

#[test]
fn test_doctype_equal_special_cases() {
// Direct equal
let uuid: UuidV4 = PROPOSAL_UUID_TYPE.try_into().unwrap();
let uuid = deprecated::PROPOSAL_DOCUMENT_UUID_TYPE;
let dt1 = DocType::try_from(vec![uuid]).unwrap();
let dt2 = DocType::try_from(vec![uuid]).unwrap();
assert_eq!(dt1, dt2);

// single -> special mapped type
let single = DocType::try_from(PROPOSAL_UUID_TYPE).unwrap();
assert_eq!(single, *PROPOSAL_DOC_TYPE);
let single = DocType::try_from(COMMENT_UUID_TYPE).unwrap();
assert_eq!(single, *PROPOSAL_COMMENT_DOC);
let single = DocType::try_from(ACTION_UUID_TYPE).unwrap();
assert_eq!(single, *PROPOSAL_ACTION_DOC);
let single = DocType::try_from(deprecated::PROPOSAL_DOCUMENT_UUID_TYPE).unwrap();
assert_eq!(single, *PROPOSAL);
let single = DocType::try_from(deprecated::COMMENT_DOCUMENT_UUID_TYPE).unwrap();
assert_eq!(single, *PROPOSAL_COMMENT);
let single = DocType::try_from(deprecated::PROPOSAL_ACTION_DOCUMENT_UUID_TYPE).unwrap();
assert_eq!(single, *PROPOSAL_SUBMISSION_ACTION);
}

#[test]
Expand Down Expand Up @@ -459,10 +503,10 @@ mod tests {

#[test]
fn test_deserialize_special_case() {
let uuid = PROPOSAL_UUID_TYPE.to_string();
let uuid = deprecated::PROPOSAL_DOCUMENT_UUID_TYPE.to_string();
let json = json!(uuid);
let dt: DocType = serde_json::from_value(json).unwrap();

assert_eq!(dt, *PROPOSAL_DOC_TYPE);
assert_eq!(dt, *PROPOSAL);
}
}
Loading