Skip to content

Commit 0d155bf

Browse files
saibatizokustevenjMr-Leshiy
authored
fix(rust/catalyst-types): Convert UUID types to cbor (#148)
* fix(rust/catalyst-types): convert UUID types to cbor::Value * fix(rust/catalyst-types): use minicbor and remove coset * fix(rust/catalyst-types): fix uuid cbor * fix(rust/catalyst-types): fix uuid cbor and add tests * fix(rust/catalyst-types): fix decode error * fix(rust/signed-doc): fix decode error * fix UUID decoding * fix(rust/catalyst-types): encode UUID types with context * feat(rust/catalyst-types): CBOR encode and decode for UUID types with context * fix(rust/catalyst-types): remove redundant tests --------- Co-authored-by: Steven Johnson <[email protected]> Co-authored-by: Mr-Leshiy <[email protected]>
1 parent d0a1b28 commit 0d155bf

File tree

4 files changed

+163
-139
lines changed

4 files changed

+163
-139
lines changed

rust/catalyst-types/Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ name = "catalyst_types"
1717

1818
[dependencies]
1919
blake2b_simd = "1.0.2"
20-
coset = "0.3.8"
2120
displaydoc = "0.2.5"
2221
ed25519-dalek = "2.1.1"
2322
fluent-uri = "0.3.2"
@@ -33,4 +32,4 @@ uuid = { version = "1.11.0", features = ["v4", "v7", "serde"] }
3332

3433
[dev-dependencies]
3534
ed25519-dalek = { version = "2.1.1", features = ["rand_core"] }
36-
rand = "0.8.5"
35+
rand = "0.8.5"
Lines changed: 132 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,148 @@
11
//! `UUID` types.
22
3-
use displaydoc::Display;
4-
use thiserror::Error;
5-
use uuid::Uuid;
6-
73
mod uuid_v4;
84
mod uuid_v7;
95

6+
use minicbor::data::Tag;
107
pub use uuid_v4::UuidV4 as V4;
118
pub use uuid_v7::UuidV7 as V7;
129

1310
/// Invalid Doc Type UUID
1411
pub const INVALID_UUID: uuid::Uuid = uuid::Uuid::from_bytes([0x00; 16]);
1512

16-
/// CBOR tag for UUID content.
17-
pub const UUID_CBOR_TAG: u64 = 37;
18-
19-
/// Errors that can occur when decoding CBOR-encoded UUIDs.
20-
#[derive(Display, Debug, Error)]
21-
pub enum CborUuidError {
22-
/// Invalid CBOR encoded UUID type
23-
InvalidCborType,
24-
/// Invalid CBOR encoded UUID type: invalid bytes size
25-
InvalidByteSize,
26-
/// UUID {uuid} is not `v{expected_version}`
27-
InvalidVersion {
28-
/// The decoded UUID that was checked.
29-
uuid: Uuid,
30-
/// The expected version of the UUID, which did not match the decoded one.
31-
expected_version: usize,
32-
},
13+
/// UUID CBOR tag <https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml/>.
14+
#[allow(dead_code)]
15+
const UUID_CBOR_TAG: u64 = 37;
16+
17+
/// Context for `CBOR` encoding and decoding
18+
pub enum CborContext {
19+
/// Untagged bytes
20+
Untagged,
21+
/// IANA CBOR tag and bytes
22+
Tagged,
23+
/// Optional tag
24+
Optional,
3325
}
3426

35-
/// Decode `CBOR` encoded `UUID`.
36-
pub(crate) fn decode_cbor_uuid(val: &coset::cbor::Value) -> Result<uuid::Uuid, CborUuidError> {
37-
let Some((UUID_CBOR_TAG, coset::cbor::Value::Bytes(bytes))) = val.as_tag() else {
38-
return Err(CborUuidError::InvalidCborType);
27+
/// Validate UUID CBOR Tag.
28+
fn validate_uuid_tag(tag: u64) -> Result<(), minicbor::decode::Error> {
29+
if UUID_CBOR_TAG != tag {
30+
return Err(minicbor::decode::Error::message(format!(
31+
"tag value must be: {UUID_CBOR_TAG}, provided: {tag}"
32+
)));
33+
}
34+
Ok(())
35+
}
36+
37+
/// Decode from `CBOR` into `UUID`
38+
fn decode_cbor_uuid(
39+
d: &mut minicbor::Decoder<'_>, ctx: &mut CborContext,
40+
) -> Result<uuid::Uuid, minicbor::decode::Error> {
41+
let bytes = match ctx {
42+
CborContext::Untagged => d.bytes()?,
43+
CborContext::Tagged => {
44+
let tag = d.tag()?;
45+
validate_uuid_tag(tag.as_u64())?;
46+
d.bytes()?
47+
},
48+
CborContext::Optional => {
49+
let pos = d.position();
50+
if let Ok(tag) = d.tag() {
51+
validate_uuid_tag(tag.as_u64())?;
52+
d.bytes()?
53+
} else {
54+
d.set_position(pos);
55+
d.bytes()?
56+
}
57+
},
3958
};
40-
let uuid = uuid::Uuid::from_bytes(
41-
bytes
42-
.clone()
43-
.try_into()
44-
.map_err(|_| CborUuidError::InvalidByteSize)?,
45-
);
59+
let decoded: [u8; 16] = bytes.try_into().map_err(|_| {
60+
minicbor::decode::Error::message("Invalid CBOR encoded UUID type: invalid bytes size")
61+
})?;
62+
let uuid = uuid::Uuid::from_bytes(decoded);
4663
Ok(uuid)
4764
}
65+
66+
/// Encode `UUID` into `CBOR`
67+
fn encode_cbor_uuid<W: minicbor::encode::Write>(
68+
uuid: uuid::Uuid, e: &mut minicbor::Encoder<W>, ctx: &mut CborContext,
69+
) -> Result<(), minicbor::encode::Error<W::Error>> {
70+
if let CborContext::Tagged = ctx {
71+
e.tag(Tag::new(UUID_CBOR_TAG))?;
72+
}
73+
e.bytes(uuid.as_bytes())?;
74+
Ok(())
75+
}
76+
77+
#[cfg(test)]
78+
mod tests {
79+
80+
use super::{V4, V7};
81+
use crate::uuid::CborContext;
82+
83+
#[test]
84+
fn test_cbor_uuid_v4_roundtrip() {
85+
let uuid: V4 = uuid::Uuid::new_v4().into();
86+
let mut bytes = Vec::new();
87+
minicbor::encode_with(uuid, &mut bytes, &mut CborContext::Untagged).unwrap();
88+
let decoded = minicbor::decode_with(bytes.as_slice(), &mut CborContext::Untagged).unwrap();
89+
assert_eq!(uuid, decoded);
90+
}
91+
92+
#[test]
93+
fn test_cbor_uuid_v7_roundtrip() {
94+
let uuid: V7 = uuid::Uuid::now_v7().into();
95+
let mut bytes = Vec::new();
96+
minicbor::encode_with(uuid, &mut bytes, &mut CborContext::Untagged).unwrap();
97+
let decoded = minicbor::decode_with(bytes.as_slice(), &mut CborContext::Untagged).unwrap();
98+
assert_eq!(uuid, decoded);
99+
}
100+
101+
#[test]
102+
fn test_tagged_cbor_uuid_v4_roundtrip() {
103+
let uuid: V4 = uuid::Uuid::new_v4().into();
104+
let mut bytes = Vec::new();
105+
minicbor::encode_with(uuid, &mut bytes, &mut CborContext::Tagged).unwrap();
106+
let decoded = minicbor::decode_with(bytes.as_slice(), &mut CborContext::Tagged).unwrap();
107+
assert_eq!(uuid, decoded);
108+
}
109+
110+
#[test]
111+
fn test_tagged_cbor_uuid_v7_roundtrip() {
112+
let uuid: V7 = uuid::Uuid::now_v7().into();
113+
let mut bytes = Vec::new();
114+
minicbor::encode_with(uuid, &mut bytes, &mut CborContext::Tagged).unwrap();
115+
let decoded = minicbor::decode_with(bytes.as_slice(), &mut CborContext::Tagged).unwrap();
116+
assert_eq!(uuid, decoded);
117+
}
118+
119+
#[test]
120+
fn test_optional_cbor_uuid_v4_roundtrip() {
121+
let uuid: V4 = uuid::Uuid::new_v4().into();
122+
123+
let mut bytes = Vec::new();
124+
minicbor::encode_with(uuid, &mut bytes, &mut CborContext::Untagged).unwrap();
125+
let decoded = minicbor::decode_with(bytes.as_slice(), &mut CborContext::Optional).unwrap();
126+
assert_eq!(uuid, decoded);
127+
128+
let mut bytes = Vec::new();
129+
minicbor::encode_with(uuid, &mut bytes, &mut CborContext::Tagged).unwrap();
130+
let decoded = minicbor::decode_with(bytes.as_slice(), &mut CborContext::Optional).unwrap();
131+
assert_eq!(uuid, decoded);
132+
}
133+
134+
#[test]
135+
fn test_optional_cbor_uuid_v7_roundtrip() {
136+
let uuid: V7 = uuid::Uuid::now_v7().into();
137+
138+
let mut bytes = Vec::new();
139+
minicbor::encode_with(uuid, &mut bytes, &mut CborContext::Untagged).unwrap();
140+
let decoded = minicbor::decode_with(bytes.as_slice(), &mut CborContext::Optional).unwrap();
141+
assert_eq!(uuid, decoded);
142+
143+
let mut bytes = Vec::new();
144+
minicbor::encode_with(uuid, &mut bytes, &mut CborContext::Tagged).unwrap();
145+
let decoded = minicbor::decode_with(bytes.as_slice(), &mut CborContext::Optional).unwrap();
146+
assert_eq!(uuid, decoded);
147+
}
148+
}

rust/catalyst-types/src/uuid/uuid_v4.rs

Lines changed: 15 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
//! `UUIDv4` Type.
22
use std::fmt::{Display, Formatter};
33

4-
use super::{decode_cbor_uuid, INVALID_UUID};
4+
use minicbor::{Decode, Decoder, Encode};
5+
6+
use super::{decode_cbor_uuid, encode_cbor_uuid, CborContext, INVALID_UUID};
57

68
/// Type representing a `UUIDv4`.
79
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)]
@@ -41,23 +43,18 @@ impl Display for UuidV4 {
4143
}
4244
}
4345

44-
impl TryFrom<&coset::cbor::Value> for UuidV4 {
45-
type Error = super::CborUuidError;
46-
47-
fn try_from(cbor_value: &coset::cbor::Value) -> Result<Self, Self::Error> {
48-
match decode_cbor_uuid(cbor_value) {
49-
Ok(uuid) => {
50-
if uuid.get_version_num() == Self::UUID_VERSION_NUMBER {
51-
Ok(Self { uuid })
52-
} else {
53-
Err(super::CborUuidError::InvalidVersion {
54-
uuid,
55-
expected_version: Self::UUID_VERSION_NUMBER,
56-
})
57-
}
58-
},
59-
Err(e) => Err(e),
60-
}
46+
impl Decode<'_, CborContext> for UuidV4 {
47+
fn decode(d: &mut Decoder<'_>, ctx: &mut CborContext) -> Result<Self, minicbor::decode::Error> {
48+
let uuid = decode_cbor_uuid(d, ctx)?;
49+
Ok(Self { uuid })
50+
}
51+
}
52+
53+
impl Encode<CborContext> for UuidV4 {
54+
fn encode<W: minicbor::encode::Write>(
55+
&self, e: &mut minicbor::Encoder<W>, ctx: &mut CborContext,
56+
) -> Result<(), minicbor::encode::Error<W::Error>> {
57+
encode_cbor_uuid(self.uuid(), e, ctx)
6158
}
6259
}
6360

@@ -81,11 +78,9 @@ impl From<UuidV4> for uuid::Uuid {
8178

8279
#[cfg(test)]
8380
mod tests {
84-
use coset::cbor::Value;
8581
use uuid::Uuid;
8682

8783
use super::*;
88-
use crate::uuid::UUID_CBOR_TAG;
8984

9085
#[test]
9186
fn test_invalid_uuid() {
@@ -112,37 +107,4 @@ mod tests {
112107
"Zero UUID should not be valid"
113108
);
114109
}
115-
116-
#[test]
117-
fn test_try_from_cbor_valid_uuid() {
118-
let uuid = Uuid::new_v4();
119-
let cbor_value = Value::Tag(
120-
UUID_CBOR_TAG,
121-
Box::new(Value::Bytes(uuid.as_bytes().to_vec())),
122-
);
123-
let result = UuidV4::try_from(&cbor_value);
124-
125-
assert!(
126-
result.is_ok(),
127-
"Should successfully parse valid UUID from CBOR"
128-
);
129-
let uuid_v4 = result.unwrap();
130-
assert!(uuid_v4.is_valid(), "Parsed UUIDv4 should be valid");
131-
assert_eq!(
132-
uuid_v4.uuid(),
133-
uuid,
134-
"Parsed UUID should match original UUID"
135-
);
136-
}
137-
138-
#[test]
139-
fn test_try_from_cbor_invalid_uuid() {
140-
let cbor_value = Value::Bytes(vec![0; 16]);
141-
let result = UuidV4::try_from(&cbor_value);
142-
143-
assert!(
144-
result.is_err(),
145-
"Should fail to parse invalid UUID from CBOR"
146-
);
147-
}
148110
}

rust/catalyst-types/src/uuid/uuid_v7.rs

Lines changed: 15 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
//! `UUIDv7` Type.
22
use std::fmt::{Display, Formatter};
33

4-
use super::{decode_cbor_uuid, INVALID_UUID};
4+
use minicbor::{Decode, Decoder, Encode};
5+
6+
use super::{decode_cbor_uuid, encode_cbor_uuid, CborContext, INVALID_UUID};
57

68
/// Type representing a `UUIDv7`.
79
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)]
@@ -41,23 +43,18 @@ impl Display for UuidV7 {
4143
}
4244
}
4345

44-
impl TryFrom<&coset::cbor::Value> for UuidV7 {
45-
type Error = super::CborUuidError;
46-
47-
fn try_from(cbor_value: &coset::cbor::Value) -> Result<Self, Self::Error> {
48-
match decode_cbor_uuid(cbor_value) {
49-
Ok(uuid) => {
50-
if uuid.get_version_num() == Self::UUID_VERSION_NUMBER {
51-
Ok(Self { uuid })
52-
} else {
53-
Err(super::CborUuidError::InvalidVersion {
54-
uuid,
55-
expected_version: Self::UUID_VERSION_NUMBER,
56-
})
57-
}
58-
},
59-
Err(e) => Err(e),
60-
}
46+
impl Decode<'_, CborContext> for UuidV7 {
47+
fn decode(d: &mut Decoder<'_>, ctx: &mut CborContext) -> Result<Self, minicbor::decode::Error> {
48+
let uuid = decode_cbor_uuid(d, ctx)?;
49+
Ok(Self { uuid })
50+
}
51+
}
52+
53+
impl Encode<CborContext> for UuidV7 {
54+
fn encode<W: minicbor::encode::Write>(
55+
&self, e: &mut minicbor::Encoder<W>, ctx: &mut CborContext,
56+
) -> Result<(), minicbor::encode::Error<W::Error>> {
57+
encode_cbor_uuid(self.uuid(), e, ctx)
6158
}
6259
}
6360

@@ -81,11 +78,9 @@ impl From<UuidV7> for uuid::Uuid {
8178

8279
#[cfg(test)]
8380
mod tests {
84-
use coset::cbor::Value;
8581
use uuid::Uuid;
8682

8783
use super::*;
88-
use crate::uuid::UUID_CBOR_TAG;
8984

9085
#[test]
9186
fn test_invalid_uuid() {
@@ -113,37 +108,4 @@ mod tests {
113108
"Zero UUID should not be valid"
114109
);
115110
}
116-
117-
#[test]
118-
fn test_try_from_cbor_valid_uuid() {
119-
let uuid = Uuid::try_parse("017f22e3-79b0-7cc7-98cf-e0bbf8a1c5f1").unwrap();
120-
let cbor_value = Value::Tag(
121-
UUID_CBOR_TAG,
122-
Box::new(Value::Bytes(uuid.as_bytes().to_vec())),
123-
);
124-
let result = UuidV7::try_from(&cbor_value);
125-
126-
assert!(
127-
result.is_ok(),
128-
"Should successfully parse valid UUID from CBOR"
129-
);
130-
let uuid_v7 = result.unwrap();
131-
assert!(uuid_v7.is_valid(), "Parsed UUIDv7 should be valid");
132-
assert_eq!(
133-
uuid_v7.uuid(),
134-
uuid,
135-
"Parsed UUID should match original UUID"
136-
);
137-
}
138-
139-
#[test]
140-
fn test_try_from_cbor_invalid_uuid() {
141-
let cbor_value = Value::Bytes(vec![0; 16]);
142-
let result = UuidV7::try_from(&cbor_value);
143-
144-
assert!(
145-
result.is_err(),
146-
"Should fail to parse invalid UUID from CBOR"
147-
);
148-
}
149111
}

0 commit comments

Comments
 (0)