From 42a5755372c2910f6625e800bcd2d820b5c4889a Mon Sep 17 00:00:00 2001 From: bkioshn Date: Thu, 2 Jan 2025 19:27:30 +0700 Subject: [PATCH 1/3] feat(cbork): setup cbork-utils Signed-off-by: bkioshn --- rust/Cargo.toml | 1 + rust/Earthfile | 4 ++-- rust/cbork-utils/Cargo.toml | 14 ++++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 rust/cbork-utils/Cargo.toml diff --git a/rust/Cargo.toml b/rust/Cargo.toml index e01edc7e30..2b82dd865f 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -7,6 +7,7 @@ members = [ "cbork", "cbork-abnf-parser", "cbork-cddl-parser", + "cbork-utils", "catalyst-voting", "catalyst-voting", "immutable-ledger", diff --git a/rust/Earthfile b/rust/Earthfile index ea762219c5..412171a10d 100644 --- a/rust/Earthfile +++ b/rust/Earthfile @@ -11,7 +11,7 @@ COPY_SRC: c509-certificate \ cardano-chain-follower \ catalyst-voting vote-tx-v1 vote-tx-v2 \ - cbork cbork-abnf-parser cbork-cddl-parser \ + cbork cbork-abnf-parser cbork-cddl-parser cbork-utils\ hermes-ipfs \ signed_doc \ rbac-registration \ @@ -54,7 +54,7 @@ build: DO rust-ci+EXECUTE \ --cmd="/scripts/std_build.py" \ --args1="--libs=c509-certificate --libs=cardano-chain-follower --libs=hermes-ipfs" \ - --args2="--libs=cbork-cddl-parser --libs=cbork-abnf-parser" \ + --args2="--libs=cbork-cddl-parser --libs=cbork-abnf-parser --libs=cbork-utils"\ --args3="--libs=catalyst-voting --libs=immutable-ledger --libs=vote-tx-v1 --libs=vote-tx-v2" \ --args4="--bins=cbork/cbork --libs=rbac-registration --libs=signed_doc" \ --args5="--cov_report=$HOME/build/coverage-report.info" \ diff --git a/rust/cbork-utils/Cargo.toml b/rust/cbork-utils/Cargo.toml new file mode 100644 index 0000000000..a32ad179b8 --- /dev/null +++ b/rust/cbork-utils/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "cbork-utils" +version = "0.0.1" +edition.workspace = true +license.workspace = true +authors.workspace = true +homepage.workspace = true +repository.workspace = true + +[lints] +workspace = true + +[dependencies] +minicbor = { version = "0.25.1", features = ["std"] } From c52eea0082bf22cdc08c0e2c828c182d3a9adad8 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Thu, 2 Jan 2025 19:28:11 +0700 Subject: [PATCH 2/3] feat(cbork): add cbor decode helper Signed-off-by: bkioshn --- rust/cbork-utils/src/decode_helper.rs | 238 ++++++++++++++++++++++++++ rust/cbork-utils/src/lib.rs | 3 + 2 files changed, 241 insertions(+) create mode 100644 rust/cbork-utils/src/decode_helper.rs create mode 100644 rust/cbork-utils/src/lib.rs diff --git a/rust/cbork-utils/src/decode_helper.rs b/rust/cbork-utils/src/decode_helper.rs new file mode 100644 index 0000000000..7ebf2d7afb --- /dev/null +++ b/rust/cbork-utils/src/decode_helper.rs @@ -0,0 +1,238 @@ +//! CBOR decoding helper functions. + +use minicbor::{data::Tag, decode, Decoder}; + +/// Generic helper function for decoding different types. +/// +/// # Errors +/// +/// Error if the decoding fails. +pub fn decode_helper<'a, T, C>( + d: &mut Decoder<'a>, from: &str, context: &mut C, +) -> Result +where T: minicbor::Decode<'a, C> { + T::decode(d, context).map_err(|e| { + decode::Error::message(format!( + "Failed to decode {:?} in {from}: {e}", + std::any::type_name::() + )) + }) +} + +/// Helper function for decoding bytes. +/// +/// # Errors +/// +/// Error if the decoding fails. +pub fn decode_bytes(d: &mut Decoder, from: &str) -> Result, decode::Error> { + d.bytes().map(<[u8]>::to_vec).map_err(|e| { + decode::Error::message(format!( + "Failed to decode bytes in {from}: + {e}" + )) + }) +} + +/// Helper function for decoding array. +/// +/// # Errors +/// +/// Error if the decoding fails. +pub fn decode_array_len(d: &mut Decoder, from: &str) -> Result { + d.array() + .map_err(|e| { + decode::Error::message(format!( + "Failed to decode array in {from}: + {e}" + )) + })? + .ok_or(decode::Error::message(format!( + "Failed to decode array in {from}, unexpected indefinite length", + ))) +} + +/// Helper function for decoding map. +/// +/// # Errors +/// +/// Error if the decoding fails. +pub fn decode_map_len(d: &mut Decoder, from: &str) -> Result { + d.map() + .map_err(|e| decode::Error::message(format!("Failed to decode map in {from}: {e}")))? + .ok_or(decode::Error::message(format!( + "Failed to decode map in {from}, unexpected indefinite length", + ))) +} + +/// Helper function for decoding tag. +/// +/// # Errors +/// +/// Error if the decoding fails. +pub fn decode_tag(d: &mut Decoder, from: &str) -> Result { + d.tag() + .map_err(|e| decode::Error::message(format!("Failed to decode tag in {from}: {e}"))) +} + +/// Decode any in CDDL, only support basic datatype +/// +/// # Errors +/// +/// Error if the decoding fails. +pub fn decode_any(d: &mut Decoder, from: &str) -> Result, decode::Error> { + match d.datatype()? { + minicbor::data::Type::String => { + match decode_helper::(d, &format!("{from} Any"), &mut ()) { + Ok(i) => Ok(i.as_bytes().to_vec()), + Err(e) => Err(e), + } + }, + minicbor::data::Type::U8 => { + match decode_helper::(d, &format!("{from} Any"), &mut ()) { + Ok(i) => Ok(i.to_be_bytes().to_vec()), + Err(e) => Err(e), + } + }, + minicbor::data::Type::U16 => { + match decode_helper::(d, &format!("{from} Any"), &mut ()) { + Ok(i) => Ok(i.to_be_bytes().to_vec()), + Err(e) => Err(e), + } + }, + minicbor::data::Type::U32 => { + match decode_helper::(d, &format!("{from} Any"), &mut ()) { + Ok(i) => Ok(i.to_be_bytes().to_vec()), + Err(e) => Err(e), + } + }, + minicbor::data::Type::U64 => { + match decode_helper::(d, &format!("{from} Any"), &mut ()) { + Ok(i) => Ok(i.to_be_bytes().to_vec()), + Err(e) => Err(e), + } + }, + minicbor::data::Type::I8 => { + match decode_helper::(d, &format!("{from} Any"), &mut ()) { + Ok(i) => Ok(i.to_be_bytes().to_vec()), + Err(e) => Err(e), + } + }, + minicbor::data::Type::I16 => { + match decode_helper::(d, &format!("{from} Any"), &mut ()) { + Ok(i) => Ok(i.to_be_bytes().to_vec()), + Err(e) => Err(e), + } + }, + minicbor::data::Type::I32 => { + match decode_helper::(d, &format!("{from} Any"), &mut ()) { + Ok(i) => Ok(i.to_be_bytes().to_vec()), + Err(e) => Err(e), + } + }, + minicbor::data::Type::I64 => { + match decode_helper::(d, &format!("{from} Any"), &mut ()) { + Ok(i) => Ok(i.to_be_bytes().to_vec()), + Err(e) => Err(e), + } + }, + minicbor::data::Type::Bytes => Ok(decode_bytes(d, &format!("{from} Any"))?), + minicbor::data::Type::Array => { + Ok(decode_array_len(d, &format!("{from} Any"))? + .to_be_bytes() + .to_vec()) + }, + _ => { + Err(decode::Error::message(format!( + "{from} Any, Data type not supported" + ))) + }, + } +} + +#[cfg(test)] +mod tests { + + use minicbor::Encoder; + + use super::*; + + #[test] + fn test_decode_any_bytes() { + let mut buf = Vec::new(); + let mut e = Encoder::new(&mut buf); + e.bytes(&[1, 2, 3, 4]).expect("Error encoding bytes"); + + let mut d = Decoder::new(&buf); + let result = decode_any(&mut d, "test").expect("Error decoding bytes"); + assert_eq!(result, vec![1, 2, 3, 4]); + } + + #[test] + fn test_decode_any_string() { + let mut buf = Vec::new(); + let mut e = Encoder::new(&mut buf); + e.str("hello").expect("Error encoding string"); + + let mut d = Decoder::new(&buf); + let result = decode_any(&mut d, "test").expect("Error decoding string"); + assert_eq!(result, b"hello".to_vec()); + } + + #[test] + fn test_decode_any_array() { + // The array should contain a supported type + let mut buf = Vec::new(); + let mut e = Encoder::new(&mut buf); + e.array(2).expect("Error encoding array"); + e.u8(1).expect("Error encoding u8"); + e.u8(2).expect("Error encoding u8"); + let mut d = Decoder::new(&buf); + let result = decode_any(&mut d, "test").expect("Error decoding array"); + // The decode of array is just a length of the array + assert_eq!( + u64::from_be_bytes(result.try_into().expect("Error converting bytes to u64")), + 2 + ); + } + + #[test] + fn test_decode_any_u32() { + let mut buf = Vec::new(); + let mut e = Encoder::new(&mut buf); + let num: u32 = 123_456_789; + e.u32(num).expect("Error encoding u32"); + + let mut d = Decoder::new(&buf); + let result = decode_any(&mut d, "test").expect("Error decoding u32"); + assert_eq!( + u32::from_be_bytes(result.try_into().expect("Error converting bytes to u32")), + num + ); + } + + #[test] + fn test_decode_any_i32() { + let mut buf = Vec::new(); + let mut e = Encoder::new(&mut buf); + let num: i32 = -123_456_789; + e.i32(num).expect("Error encoding i32"); + let mut d = Decoder::new(&buf); + let result = decode_any(&mut d, "test").expect("Error decoding i32"); + assert_eq!( + i32::from_be_bytes(result.try_into().expect("Error converting bytes to i32")), + num + ); + } + + #[test] + fn test_decode_any_unsupported_type() { + let mut buf = Vec::new(); + let mut e = Encoder::new(&mut buf); + e.null().expect("Error encoding null"); // Encode a null type which is unsupported + + let mut d = Decoder::new(&buf); + let result = decode_any(&mut d, "test"); + // Should print out the error message with the location of the error + assert!(result.is_err()); + } +} diff --git a/rust/cbork-utils/src/lib.rs b/rust/cbork-utils/src/lib.rs new file mode 100644 index 0000000000..1b29a48aaf --- /dev/null +++ b/rust/cbork-utils/src/lib.rs @@ -0,0 +1,3 @@ +//! CBOR utility modules. + +pub mod decode_helper; From a51a87bd0e020dd3a3b7696024335403013b9f47 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Thu, 2 Jan 2025 19:35:13 +0700 Subject: [PATCH 3/3] fix(cbork): spacing Signed-off-by: bkioshn --- rust/Earthfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/Earthfile b/rust/Earthfile index 412171a10d..c8ea2cfb74 100644 --- a/rust/Earthfile +++ b/rust/Earthfile @@ -11,7 +11,7 @@ COPY_SRC: c509-certificate \ cardano-chain-follower \ catalyst-voting vote-tx-v1 vote-tx-v2 \ - cbork cbork-abnf-parser cbork-cddl-parser cbork-utils\ + cbork cbork-abnf-parser cbork-cddl-parser cbork-utils \ hermes-ipfs \ signed_doc \ rbac-registration \ @@ -54,7 +54,7 @@ build: DO rust-ci+EXECUTE \ --cmd="/scripts/std_build.py" \ --args1="--libs=c509-certificate --libs=cardano-chain-follower --libs=hermes-ipfs" \ - --args2="--libs=cbork-cddl-parser --libs=cbork-abnf-parser --libs=cbork-utils"\ + --args2="--libs=cbork-cddl-parser --libs=cbork-abnf-parser --libs=cbork-utils" \ --args3="--libs=catalyst-voting --libs=immutable-ledger --libs=vote-tx-v1 --libs=vote-tx-v2" \ --args4="--bins=cbork/cbork --libs=rbac-registration --libs=signed_doc" \ --args5="--cov_report=$HOME/build/coverage-report.info" \