Skip to content

Commit 2f3fbe7

Browse files
authored
Merge branch 'main' into feat/serialize-signed-docs
2 parents 260a7d7 + b9d020f commit 2f3fbe7

File tree

5 files changed

+160
-56
lines changed

5 files changed

+160
-56
lines changed

rust/catalyst-types/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ minicbor = { version = "0.25.1", features = ["std"] }
2525
num-traits = "0.2.19"
2626
orx-concurrent-vec = "3.1.0"
2727
pallas-crypto = { version = "0.30.1", git = "https://github.com/input-output-hk/catalyst-pallas.git", rev = "9b5183c8b90b90fe2cc319d986e933e9518957b3" }
28-
serde = { version = "1.0.217", features = ["derive"] }
28+
serde = { version = "1.0.217", features = ["derive", "rc"] }
2929
thiserror = "2.0.9"
3030
base64-url = "3.0.0"
3131
uuid = { version = "1.11.0", features = ["v4", "v7", "serde"] }
@@ -36,3 +36,4 @@ tracing = "0.1.41"
3636
[dev-dependencies]
3737
ed25519-dalek = { version = "2.1.1", features = ["rand_core"] }
3838
rand = "0.8.5"
39+
serde_json = "1"

rust/catalyst-types/src/problem_report.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,4 +469,14 @@ mod tests {
469469
// The original report must have the same (problematic) state.
470470
assert!(original.is_problematic());
471471
}
472+
473+
#[test]
474+
fn serialize() {
475+
let report = ProblemReport::new("top level context");
476+
report.invalid_value("field name", "found", "constraint", "context");
477+
478+
let serialized = serde_json::to_string(&report).unwrap();
479+
let expected = r#"{"context":"top level context","report":[{"kind":{"type":"InvalidValue","field":"field name","value":"found","constraint":"constraint"},"context":"context"}]}"#;
480+
assert_eq!(serialized, expected);
481+
}
472482
}

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

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,19 @@ pub const INVALID_UUID: uuid::Uuid = uuid::Uuid::from_bytes([0x00; 16]);
1414
#[allow(dead_code)]
1515
const UUID_CBOR_TAG: u64 = 37;
1616

17+
/// Uuid validation errors, which could occur during decoding or converting to
18+
/// `UuidV4` or `UuidV7` types.
19+
#[derive(Debug, Clone, thiserror::Error)]
20+
#[allow(clippy::module_name_repetitions)]
21+
pub enum UuidError {
22+
/// `UUIDv4` invalid error
23+
#[error("'{0}' is not a valid UUIDv4")]
24+
InvalidUuidV4(uuid::Uuid),
25+
/// `UUIDv7` invalid error
26+
#[error("'{0}' is not a valid UUIDv7")]
27+
InvalidUuidV7(uuid::Uuid),
28+
}
29+
1730
/// Context for `CBOR` encoding and decoding
1831
pub enum CborContext {
1932
/// Untagged bytes
@@ -82,25 +95,45 @@ mod tests {
8295

8396
#[test]
8497
fn test_cbor_uuid_v4_roundtrip() {
85-
let uuid: V4 = uuid::Uuid::new_v4().into();
98+
let uuid = V4::new();
8699
let mut bytes = Vec::new();
87100
minicbor::encode_with(uuid, &mut bytes, &mut CborContext::Untagged).unwrap();
88101
let decoded = minicbor::decode_with(bytes.as_slice(), &mut CborContext::Untagged).unwrap();
89102
assert_eq!(uuid, decoded);
90103
}
91104

105+
#[test]
106+
fn test_cbor_uuid_v4_invalid_decoding() {
107+
let uuid_v7 = V7::new();
108+
let mut bytes = Vec::new();
109+
minicbor::encode_with(uuid_v7, &mut bytes, &mut CborContext::Untagged).unwrap();
110+
assert!(
111+
minicbor::decode_with::<_, V4>(bytes.as_slice(), &mut CborContext::Untagged).is_err()
112+
);
113+
}
114+
92115
#[test]
93116
fn test_cbor_uuid_v7_roundtrip() {
94-
let uuid: V7 = uuid::Uuid::now_v7().into();
117+
let uuid = V7::new();
95118
let mut bytes = Vec::new();
96119
minicbor::encode_with(uuid, &mut bytes, &mut CborContext::Untagged).unwrap();
97120
let decoded = minicbor::decode_with(bytes.as_slice(), &mut CborContext::Untagged).unwrap();
98121
assert_eq!(uuid, decoded);
99122
}
100123

124+
#[test]
125+
fn test_cbor_uuid_v7_invalid_decoding() {
126+
let uuid_v4 = V4::new();
127+
let mut bytes = Vec::new();
128+
minicbor::encode_with(uuid_v4, &mut bytes, &mut CborContext::Untagged).unwrap();
129+
assert!(
130+
minicbor::decode_with::<_, V7>(bytes.as_slice(), &mut CborContext::Untagged).is_err()
131+
);
132+
}
133+
101134
#[test]
102135
fn test_tagged_cbor_uuid_v4_roundtrip() {
103-
let uuid: V4 = uuid::Uuid::new_v4().into();
136+
let uuid = V4::new();
104137
let mut bytes = Vec::new();
105138
minicbor::encode_with(uuid, &mut bytes, &mut CborContext::Tagged).unwrap();
106139
let decoded = minicbor::decode_with(bytes.as_slice(), &mut CborContext::Tagged).unwrap();
@@ -109,7 +142,7 @@ mod tests {
109142

110143
#[test]
111144
fn test_tagged_cbor_uuid_v7_roundtrip() {
112-
let uuid: V7 = uuid::Uuid::now_v7().into();
145+
let uuid = V7::new();
113146
let mut bytes = Vec::new();
114147
minicbor::encode_with(uuid, &mut bytes, &mut CborContext::Tagged).unwrap();
115148
let decoded = minicbor::decode_with(bytes.as_slice(), &mut CborContext::Tagged).unwrap();
@@ -118,7 +151,7 @@ mod tests {
118151

119152
#[test]
120153
fn test_optional_cbor_uuid_v4_roundtrip() {
121-
let uuid: V4 = uuid::Uuid::new_v4().into();
154+
let uuid = V4::new();
122155

123156
let mut bytes = Vec::new();
124157
minicbor::encode_with(uuid, &mut bytes, &mut CborContext::Untagged).unwrap();
@@ -133,7 +166,7 @@ mod tests {
133166

134167
#[test]
135168
fn test_optional_cbor_uuid_v7_roundtrip() {
136-
let uuid: V7 = uuid::Uuid::now_v7().into();
169+
let uuid = V7::new();
137170

138171
let mut bytes = Vec::new();
139172
minicbor::encode_with(uuid, &mut bytes, &mut CborContext::Untagged).unwrap();

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

Lines changed: 56 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,51 +2,65 @@
22
use std::fmt::{Display, Formatter};
33

44
use minicbor::{Decode, Decoder, Encode};
5+
use uuid::Uuid;
56

6-
use super::{decode_cbor_uuid, encode_cbor_uuid, CborContext, INVALID_UUID};
7+
use super::{decode_cbor_uuid, encode_cbor_uuid, CborContext, UuidError, INVALID_UUID};
78

89
/// Type representing a `UUIDv4`.
9-
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)]
10-
#[serde(from = "uuid::Uuid")]
11-
#[serde(into = "uuid::Uuid")]
12-
pub struct UuidV4 {
13-
/// UUID
14-
uuid: uuid::Uuid,
15-
}
10+
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, serde::Serialize)]
11+
pub struct UuidV4(Uuid);
1612

1713
impl UuidV4 {
1814
/// Version for `UUIDv4`.
1915
const UUID_VERSION_NUMBER: usize = 4;
2016

17+
/// Generates a random `UUIDv4`.
18+
#[must_use]
19+
#[allow(clippy::new_without_default)]
20+
pub fn new() -> Self {
21+
Self(Uuid::new_v4())
22+
}
23+
2124
/// Generates a zeroed out `UUIDv4` that can never be valid.
2225
#[must_use]
2326
pub fn invalid() -> Self {
24-
Self { uuid: INVALID_UUID }
27+
Self(INVALID_UUID)
2528
}
2629

2730
/// Check if this is a valid `UUIDv4`.
2831
#[must_use]
2932
pub fn is_valid(&self) -> bool {
30-
self.uuid != INVALID_UUID && self.uuid.get_version_num() == Self::UUID_VERSION_NUMBER
33+
is_valid(&self.uuid())
3134
}
3235

3336
/// Returns the `uuid::Uuid` type.
3437
#[must_use]
35-
pub fn uuid(&self) -> uuid::Uuid {
36-
self.uuid
38+
pub fn uuid(&self) -> Uuid {
39+
self.0
3740
}
3841
}
3942

43+
/// Check if this is a valid `UUIDv4`.
44+
fn is_valid(uuid: &Uuid) -> bool {
45+
uuid != &INVALID_UUID && uuid.get_version_num() == UuidV4::UUID_VERSION_NUMBER
46+
}
47+
4048
impl Display for UuidV4 {
4149
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
42-
write!(f, "{}", self.uuid)
50+
write!(f, "{}", self.0)
4351
}
4452
}
4553

4654
impl Decode<'_, CborContext> for UuidV4 {
4755
fn decode(d: &mut Decoder<'_>, ctx: &mut CborContext) -> Result<Self, minicbor::decode::Error> {
4856
let uuid = decode_cbor_uuid(d, ctx)?;
49-
Ok(Self { uuid })
57+
if is_valid(&uuid) {
58+
Ok(Self(uuid))
59+
} else {
60+
Err(minicbor::decode::Error::message(UuidError::InvalidUuidV4(
61+
uuid,
62+
)))
63+
}
5064
}
5165
}
5266

@@ -59,27 +73,41 @@ impl Encode<CborContext> for UuidV4 {
5973
}
6074

6175
/// Returns a `UUIDv4` from `uuid::Uuid`.
62-
///
63-
/// NOTE: This does not guarantee that the `UUID` is valid.
64-
impl From<uuid::Uuid> for UuidV4 {
65-
fn from(uuid: uuid::Uuid) -> Self {
66-
Self { uuid }
76+
impl TryFrom<Uuid> for UuidV4 {
77+
type Error = UuidError;
78+
79+
fn try_from(uuid: Uuid) -> Result<Self, Self::Error> {
80+
if is_valid(&uuid) {
81+
Ok(Self(uuid))
82+
} else {
83+
Err(UuidError::InvalidUuidV4(uuid))
84+
}
6785
}
6886
}
6987

7088
/// Returns a `uuid::Uuid` from `UUIDv4`.
7189
///
7290
/// NOTE: This does not guarantee that the `UUID` is valid.
73-
impl From<UuidV4> for uuid::Uuid {
91+
impl From<UuidV4> for Uuid {
7492
fn from(value: UuidV4) -> Self {
75-
value.uuid
93+
value.0
94+
}
95+
}
96+
97+
impl<'de> serde::Deserialize<'de> for UuidV4 {
98+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
99+
where D: serde::Deserializer<'de> {
100+
let uuid = Uuid::deserialize(deserializer)?;
101+
if is_valid(&uuid) {
102+
Ok(Self(uuid))
103+
} else {
104+
Err(serde::de::Error::custom(UuidError::InvalidUuidV4(uuid)))
105+
}
76106
}
77107
}
78108

79109
#[cfg(test)]
80110
mod tests {
81-
use uuid::Uuid;
82-
83111
use super::*;
84112

85113
#[test]
@@ -95,15 +123,17 @@ mod tests {
95123

96124
#[test]
97125
fn test_valid_uuid() {
98-
let valid_uuid = UuidV4::from(Uuid::new_v4());
126+
let valid_uuid = UuidV4::try_from(Uuid::new_v4()).unwrap();
127+
assert!(valid_uuid.is_valid(), "Valid UUID should be valid");
128+
129+
let valid_uuid = UuidV4::new();
99130
assert!(valid_uuid.is_valid(), "Valid UUID should be valid");
100131
}
101132

102133
#[test]
103134
fn test_invalid_version_uuid() {
104-
let invalid_version_uuid = UuidV4::from(Uuid::from_u128(0));
105135
assert!(
106-
!invalid_version_uuid.is_valid(),
136+
UuidV4::try_from(INVALID_UUID).is_err(),
107137
"Zero UUID should not be valid"
108138
);
109139
}

0 commit comments

Comments
 (0)