Skip to content

Commit 05efc44

Browse files
bkioshnstevenj
andauthored
feat(rust/cbork): Add cbork-utils (#134)
* feat(cbork): setup cbork-utils Signed-off-by: bkioshn <[email protected]> * feat(cbork): add cbor decode helper Signed-off-by: bkioshn <[email protected]> * fix(cbork): spacing Signed-off-by: bkioshn <[email protected]> --------- Signed-off-by: bkioshn <[email protected]> Co-authored-by: Steven Johnson <[email protected]>
1 parent 8df0b1c commit 05efc44

File tree

5 files changed

+258
-2
lines changed

5 files changed

+258
-2
lines changed

rust/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ members = [
88
"cbork",
99
"cbork-abnf-parser",
1010
"cbork-cddl-parser",
11+
"cbork-utils",
1112
"catalyst-voting",
1213
"catalyst-voting",
1314
"immutable-ledger",

rust/Earthfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ COPY_SRC:
1212
cardano-blockchain-types \
1313
cardano-chain-follower \
1414
catalyst-voting vote-tx-v1 vote-tx-v2 \
15-
cbork cbork-abnf-parser cbork-cddl-parser \
15+
cbork cbork-abnf-parser cbork-cddl-parser cbork-utils \
1616
hermes-ipfs \
1717
signed_doc \
1818
rbac-registration \
@@ -55,7 +55,7 @@ build:
5555
DO rust-ci+EXECUTE \
5656
--cmd="/scripts/std_build.py" \
5757
--args1="--libs=c509-certificate --libs=cardano-blockchain-types --libs=cardano-chain-follower --libs=hermes-ipfs" \
58-
--args2="--libs=cbork-cddl-parser --libs=cbork-abnf-parser" \
58+
--args2="--libs=cbork-cddl-parser --libs=cbork-abnf-parser --libs=cbork-utils" \
5959
--args3="--libs=catalyst-voting --libs=immutable-ledger --libs=vote-tx-v1 --libs=vote-tx-v2" \
6060
--args4="--bins=cbork/cbork --libs=rbac-registration --libs=signed_doc" \
6161
--args5="--cov_report=$HOME/build/coverage-report.info" \

rust/cbork-utils/Cargo.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "cbork-utils"
3+
version = "0.0.1"
4+
edition.workspace = true
5+
license.workspace = true
6+
authors.workspace = true
7+
homepage.workspace = true
8+
repository.workspace = true
9+
10+
[lints]
11+
workspace = true
12+
13+
[dependencies]
14+
minicbor = { version = "0.25.1", features = ["std"] }
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
//! CBOR decoding helper functions.
2+
3+
use minicbor::{data::Tag, decode, Decoder};
4+
5+
/// Generic helper function for decoding different types.
6+
///
7+
/// # Errors
8+
///
9+
/// Error if the decoding fails.
10+
pub fn decode_helper<'a, T, C>(
11+
d: &mut Decoder<'a>, from: &str, context: &mut C,
12+
) -> Result<T, decode::Error>
13+
where T: minicbor::Decode<'a, C> {
14+
T::decode(d, context).map_err(|e| {
15+
decode::Error::message(format!(
16+
"Failed to decode {:?} in {from}: {e}",
17+
std::any::type_name::<T>()
18+
))
19+
})
20+
}
21+
22+
/// Helper function for decoding bytes.
23+
///
24+
/// # Errors
25+
///
26+
/// Error if the decoding fails.
27+
pub fn decode_bytes(d: &mut Decoder, from: &str) -> Result<Vec<u8>, decode::Error> {
28+
d.bytes().map(<[u8]>::to_vec).map_err(|e| {
29+
decode::Error::message(format!(
30+
"Failed to decode bytes in {from}:
31+
{e}"
32+
))
33+
})
34+
}
35+
36+
/// Helper function for decoding array.
37+
///
38+
/// # Errors
39+
///
40+
/// Error if the decoding fails.
41+
pub fn decode_array_len(d: &mut Decoder, from: &str) -> Result<u64, decode::Error> {
42+
d.array()
43+
.map_err(|e| {
44+
decode::Error::message(format!(
45+
"Failed to decode array in {from}:
46+
{e}"
47+
))
48+
})?
49+
.ok_or(decode::Error::message(format!(
50+
"Failed to decode array in {from}, unexpected indefinite length",
51+
)))
52+
}
53+
54+
/// Helper function for decoding map.
55+
///
56+
/// # Errors
57+
///
58+
/// Error if the decoding fails.
59+
pub fn decode_map_len(d: &mut Decoder, from: &str) -> Result<u64, decode::Error> {
60+
d.map()
61+
.map_err(|e| decode::Error::message(format!("Failed to decode map in {from}: {e}")))?
62+
.ok_or(decode::Error::message(format!(
63+
"Failed to decode map in {from}, unexpected indefinite length",
64+
)))
65+
}
66+
67+
/// Helper function for decoding tag.
68+
///
69+
/// # Errors
70+
///
71+
/// Error if the decoding fails.
72+
pub fn decode_tag(d: &mut Decoder, from: &str) -> Result<Tag, decode::Error> {
73+
d.tag()
74+
.map_err(|e| decode::Error::message(format!("Failed to decode tag in {from}: {e}")))
75+
}
76+
77+
/// Decode any in CDDL, only support basic datatype
78+
///
79+
/// # Errors
80+
///
81+
/// Error if the decoding fails.
82+
pub fn decode_any(d: &mut Decoder, from: &str) -> Result<Vec<u8>, decode::Error> {
83+
match d.datatype()? {
84+
minicbor::data::Type::String => {
85+
match decode_helper::<String, _>(d, &format!("{from} Any"), &mut ()) {
86+
Ok(i) => Ok(i.as_bytes().to_vec()),
87+
Err(e) => Err(e),
88+
}
89+
},
90+
minicbor::data::Type::U8 => {
91+
match decode_helper::<u8, _>(d, &format!("{from} Any"), &mut ()) {
92+
Ok(i) => Ok(i.to_be_bytes().to_vec()),
93+
Err(e) => Err(e),
94+
}
95+
},
96+
minicbor::data::Type::U16 => {
97+
match decode_helper::<u16, _>(d, &format!("{from} Any"), &mut ()) {
98+
Ok(i) => Ok(i.to_be_bytes().to_vec()),
99+
Err(e) => Err(e),
100+
}
101+
},
102+
minicbor::data::Type::U32 => {
103+
match decode_helper::<u32, _>(d, &format!("{from} Any"), &mut ()) {
104+
Ok(i) => Ok(i.to_be_bytes().to_vec()),
105+
Err(e) => Err(e),
106+
}
107+
},
108+
minicbor::data::Type::U64 => {
109+
match decode_helper::<u64, _>(d, &format!("{from} Any"), &mut ()) {
110+
Ok(i) => Ok(i.to_be_bytes().to_vec()),
111+
Err(e) => Err(e),
112+
}
113+
},
114+
minicbor::data::Type::I8 => {
115+
match decode_helper::<i8, _>(d, &format!("{from} Any"), &mut ()) {
116+
Ok(i) => Ok(i.to_be_bytes().to_vec()),
117+
Err(e) => Err(e),
118+
}
119+
},
120+
minicbor::data::Type::I16 => {
121+
match decode_helper::<i16, _>(d, &format!("{from} Any"), &mut ()) {
122+
Ok(i) => Ok(i.to_be_bytes().to_vec()),
123+
Err(e) => Err(e),
124+
}
125+
},
126+
minicbor::data::Type::I32 => {
127+
match decode_helper::<i32, _>(d, &format!("{from} Any"), &mut ()) {
128+
Ok(i) => Ok(i.to_be_bytes().to_vec()),
129+
Err(e) => Err(e),
130+
}
131+
},
132+
minicbor::data::Type::I64 => {
133+
match decode_helper::<i64, _>(d, &format!("{from} Any"), &mut ()) {
134+
Ok(i) => Ok(i.to_be_bytes().to_vec()),
135+
Err(e) => Err(e),
136+
}
137+
},
138+
minicbor::data::Type::Bytes => Ok(decode_bytes(d, &format!("{from} Any"))?),
139+
minicbor::data::Type::Array => {
140+
Ok(decode_array_len(d, &format!("{from} Any"))?
141+
.to_be_bytes()
142+
.to_vec())
143+
},
144+
_ => {
145+
Err(decode::Error::message(format!(
146+
"{from} Any, Data type not supported"
147+
)))
148+
},
149+
}
150+
}
151+
152+
#[cfg(test)]
153+
mod tests {
154+
155+
use minicbor::Encoder;
156+
157+
use super::*;
158+
159+
#[test]
160+
fn test_decode_any_bytes() {
161+
let mut buf = Vec::new();
162+
let mut e = Encoder::new(&mut buf);
163+
e.bytes(&[1, 2, 3, 4]).expect("Error encoding bytes");
164+
165+
let mut d = Decoder::new(&buf);
166+
let result = decode_any(&mut d, "test").expect("Error decoding bytes");
167+
assert_eq!(result, vec![1, 2, 3, 4]);
168+
}
169+
170+
#[test]
171+
fn test_decode_any_string() {
172+
let mut buf = Vec::new();
173+
let mut e = Encoder::new(&mut buf);
174+
e.str("hello").expect("Error encoding string");
175+
176+
let mut d = Decoder::new(&buf);
177+
let result = decode_any(&mut d, "test").expect("Error decoding string");
178+
assert_eq!(result, b"hello".to_vec());
179+
}
180+
181+
#[test]
182+
fn test_decode_any_array() {
183+
// The array should contain a supported type
184+
let mut buf = Vec::new();
185+
let mut e = Encoder::new(&mut buf);
186+
e.array(2).expect("Error encoding array");
187+
e.u8(1).expect("Error encoding u8");
188+
e.u8(2).expect("Error encoding u8");
189+
let mut d = Decoder::new(&buf);
190+
let result = decode_any(&mut d, "test").expect("Error decoding array");
191+
// The decode of array is just a length of the array
192+
assert_eq!(
193+
u64::from_be_bytes(result.try_into().expect("Error converting bytes to u64")),
194+
2
195+
);
196+
}
197+
198+
#[test]
199+
fn test_decode_any_u32() {
200+
let mut buf = Vec::new();
201+
let mut e = Encoder::new(&mut buf);
202+
let num: u32 = 123_456_789;
203+
e.u32(num).expect("Error encoding u32");
204+
205+
let mut d = Decoder::new(&buf);
206+
let result = decode_any(&mut d, "test").expect("Error decoding u32");
207+
assert_eq!(
208+
u32::from_be_bytes(result.try_into().expect("Error converting bytes to u32")),
209+
num
210+
);
211+
}
212+
213+
#[test]
214+
fn test_decode_any_i32() {
215+
let mut buf = Vec::new();
216+
let mut e = Encoder::new(&mut buf);
217+
let num: i32 = -123_456_789;
218+
e.i32(num).expect("Error encoding i32");
219+
let mut d = Decoder::new(&buf);
220+
let result = decode_any(&mut d, "test").expect("Error decoding i32");
221+
assert_eq!(
222+
i32::from_be_bytes(result.try_into().expect("Error converting bytes to i32")),
223+
num
224+
);
225+
}
226+
227+
#[test]
228+
fn test_decode_any_unsupported_type() {
229+
let mut buf = Vec::new();
230+
let mut e = Encoder::new(&mut buf);
231+
e.null().expect("Error encoding null"); // Encode a null type which is unsupported
232+
233+
let mut d = Decoder::new(&buf);
234+
let result = decode_any(&mut d, "test");
235+
// Should print out the error message with the location of the error
236+
assert!(result.is_err());
237+
}
238+
}

rust/cbork-utils/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
//! CBOR utility modules.
2+
3+
pub mod decode_helper;

0 commit comments

Comments
 (0)