From a1b0db146ae9a9756f620de816d5de2c2ab7a3db Mon Sep 17 00:00:00 2001 From: bkioshn Date: Sun, 10 Nov 2024 07:14:47 +0700 Subject: [PATCH 01/45] feat: move cip509 from cardano chain follower Signed-off-by: bkioshn --- rust/Cargo.toml | 3 +- rust/rbac-registration/Cargo.toml | 45 ++ .../src/cardano/cip509/mod.rs | 737 ++++++++++++++++++ .../src/cardano/cip509/rbac/certs.rs | 99 +++ .../src/cardano/cip509/rbac/mod.rs | 167 ++++ .../src/cardano/cip509/rbac/pub_key.rs | 69 ++ .../src/cardano/cip509/rbac/role_data.rs | 141 ++++ .../src/cardano/cip509/x509_chunks.rs | 122 +++ rust/rbac-registration/src/cardano/mod.rs | 4 + .../src/cardano/transaction/mod.rs | 4 + .../src/cardano/transaction/raw_aux_data.rs | 273 +++++++ .../src/cardano/transaction/witness.rs | 123 +++ rust/rbac-registration/src/lib.rs | 4 + .../src/test_data/cardano/conway_1.block | 1 + .../src/test_data/cardano/conway_2.block | 1 + .../src/test_data/cardano/conway_3.block | 1 + rust/rbac-registration/src/utils/cip19.rs | 136 ++++ .../src/utils/decode_helper.rs | 215 +++++ rust/rbac-registration/src/utils/general.rs | 27 + rust/rbac-registration/src/utils/hashing.rs | 29 + rust/rbac-registration/src/utils/mod.rs | 6 + 21 files changed, 2206 insertions(+), 1 deletion(-) create mode 100644 rust/rbac-registration/Cargo.toml create mode 100644 rust/rbac-registration/src/cardano/cip509/mod.rs create mode 100644 rust/rbac-registration/src/cardano/cip509/rbac/certs.rs create mode 100644 rust/rbac-registration/src/cardano/cip509/rbac/mod.rs create mode 100644 rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs create mode 100644 rust/rbac-registration/src/cardano/cip509/rbac/role_data.rs create mode 100644 rust/rbac-registration/src/cardano/cip509/x509_chunks.rs create mode 100644 rust/rbac-registration/src/cardano/mod.rs create mode 100644 rust/rbac-registration/src/cardano/transaction/mod.rs create mode 100644 rust/rbac-registration/src/cardano/transaction/raw_aux_data.rs create mode 100644 rust/rbac-registration/src/cardano/transaction/witness.rs create mode 100644 rust/rbac-registration/src/lib.rs create mode 100644 rust/rbac-registration/src/test_data/cardano/conway_1.block create mode 100644 rust/rbac-registration/src/test_data/cardano/conway_2.block create mode 100644 rust/rbac-registration/src/test_data/cardano/conway_3.block create mode 100644 rust/rbac-registration/src/utils/cip19.rs create mode 100644 rust/rbac-registration/src/utils/decode_helper.rs create mode 100644 rust/rbac-registration/src/utils/general.rs create mode 100644 rust/rbac-registration/src/utils/hashing.rs create mode 100644 rust/rbac-registration/src/utils/mod.rs diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 94ed9ac812e..0a2fe3fbd51 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -7,7 +7,8 @@ members = [ "cbork", "cbork-abnf-parser", "cbork-cddl-parser", - "catalyst-voting", "jormungandr-vote-tx", + "catalyst-voting", "jormungandr-vote-tx", "rbac-registration", + "rbac-registration", ] [workspace.package] diff --git a/rust/rbac-registration/Cargo.toml b/rust/rbac-registration/Cargo.toml new file mode 100644 index 00000000000..be690b80242 --- /dev/null +++ b/rust/rbac-registration/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "rbac-registration" +description = "Role Based Access Control Registration" +keywords = ["cardano", "catalyst", "rbac registration"] +version = "0.0.1" +authors = [ + "Arissara Chotivichit " +] +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[lib] +crate-type = ["cdylib", "rlib"] + +[lints] +workspace = true + +[dependencies] +hex = "0.4.3" +anyhow = "1.0.89" +strum = "0.26.3" +strum_macros = "0.26.4" +regex = "1.11.0" +ed25519-dalek = { version = "2.1.1", features = ["pem"] } +minicbor = { version = "0.25.1", features = ["alloc", "derive", "half"] } +brotli = "7.0.0" +zstd = "0.13.2" +x509-cert = "0.2.5" +der-parser = "9.0.0" +bech32 = "0.11.0" +dashmap = "6.1.0" +blake2b_simd = "1.0.2" +tracing = "0.1.40" +serde = "1.0.210" +serde_json = "1.0.128" + +c509-certificate = { version = "0.0.3", git = "https://github.com/input-output-hk/catalyst-libs.git" , tag = "v0.0.3" } +pallas = { version = "0.30.1", git = "https://github.com/input-output-hk/catalyst-pallas.git", rev = "9b5183c8b90b90fe2cc319d986e933e9518957b3" } +pallas-hardano = { version = "0.30.1", git = "https://github.com/input-output-hk/catalyst-pallas.git", rev = "9b5183c8b90b90fe2cc319d986e933e9518957b3" } +pallas-crypto = { version = "0.30.1", git = "https://github.com/input-output-hk/catalyst-pallas.git", rev = "9b5183c8b90b90fe2cc319d986e933e9518957b3" } + + +[dev-dependencies] diff --git a/rust/rbac-registration/src/cardano/cip509/mod.rs b/rust/rbac-registration/src/cardano/cip509/mod.rs new file mode 100644 index 00000000000..c55c1f93ef7 --- /dev/null +++ b/rust/rbac-registration/src/cardano/cip509/mod.rs @@ -0,0 +1,737 @@ +//! Cardano Improvement Proposal 509 (CIP-509) metadata module. +//! Doc Reference: +//! CDDL Reference: + +// cspell: words pkix + +pub mod rbac; +pub mod x509_chunks; + +use c509_certificate::{general_names::general_name::GeneralNameValue, C509ExtensionType}; +use der_parser::der::parse_der_sequence; +use minicbor::{ + decode::{self}, + Decode, Decoder, +}; +use pallas::{ + codec::{ + minicbor::{Encode, Encoder}, + utils::Bytes, + }, + ledger::traverse::MultiEraTx, +}; +use rbac::{certs::C509Cert, role_data::RoleData}; +use strum_macros::FromRepr; +use x509_cert::{der::Decode as _, ext::pkix::ID_CE_SUBJECT_ALT_NAME}; +use x509_chunks::X509Chunks; + +use super::transaction::witness::TxWitness; +use crate::utils::{ + cip19::{compare_key_hash, extract_cip19_hash, extract_key_hash}, + decode_helper::{decode_bytes, decode_helper, decode_map_len}, + general::{decode_utf8, decremented_index}, + hashing::{blake2b_128, blake2b_256}, +}; + +/// CIP509 label. +pub const LABEL: u64 = 509; + +/// Context-specific primitive type with tag number 6 (`raw_tag` 134) for +/// uniform resource identifier (URI) in the subject alternative name extension. +pub(crate) const URI: u8 = 134; + +/// CIP509. +#[derive(Debug, PartialEq, Clone, Default)] +pub struct Cip509 { + /// `UUIDv4` Purpose . + pub purpose: [u8; 16], // (bytes .size 16) + /// Transaction inputs hash. + pub txn_inputs_hash: [u8; 16], // bytes .size 16 + /// Optional previous transaction ID. + pub prv_tx_id: Option<[u8; 32]>, // bytes .size 32 + /// x509 chunks. + pub x509_chunks: X509Chunks, // chunk_type => [ + x509_chunk ] + /// Validation signature. + pub validation_signature: Vec, // bytes size (1..64) +} + +/// Enum of CIP509 metadatum with its associated unsigned integer value. +#[allow(clippy::module_name_repetitions)] +#[derive(FromRepr, Debug, PartialEq)] +#[repr(u8)] +pub(crate) enum Cip509IntIdentifier { + /// Purpose. + Purpose = 0, + /// Transaction inputs hash. + TxInputsHash = 1, + /// Previous transaction ID. + PreviousTxId = 2, + /// Validation signature. + ValidationSignature = 99, +} + +impl Decode<'_, ()> for Cip509 { + fn decode(d: &mut Decoder, ctx: &mut ()) -> Result { + let map_len = decode_map_len(d, "CIP509")?; + let mut cip509_metadatum = Cip509::default(); + for _ in 0..map_len { + // Use probe to peak + let key = d.probe().u8()?; + if let Some(key) = Cip509IntIdentifier::from_repr(key) { + // Consuming the int + let _: u8 = decode_helper(d, "CIP509", ctx)?; + match key { + Cip509IntIdentifier::Purpose => { + cip509_metadatum.purpose = decode_bytes(d, "CIP509 purpose")? + .try_into() + .map_err(|_| decode::Error::message("Invalid data size of Purpose"))?; + }, + Cip509IntIdentifier::TxInputsHash => { + cip509_metadatum.txn_inputs_hash = + decode_bytes(d, "CIP509 txn inputs hash")? + .try_into() + .map_err(|_| { + decode::Error::message("Invalid data size of TxInputsHash") + })?; + }, + Cip509IntIdentifier::PreviousTxId => { + cip509_metadatum.prv_tx_id = Some( + decode_bytes(d, "CIP509 previous tx ID")? + .try_into() + .map_err(|_| { + decode::Error::message("Invalid data size of PreviousTxId") + })?, + ); + }, + Cip509IntIdentifier::ValidationSignature => { + let validation_signature = decode_bytes(d, "CIP509 validation signature")?; + if validation_signature.is_empty() || validation_signature.len() > 64 { + return Err(decode::Error::message( + "Invalid data size of ValidationSignature", + )); + } + cip509_metadatum.validation_signature = validation_signature; + }, + } + } else { + // Handle the x509 chunks 10 11 12 + let x509_chunks = X509Chunks::decode(d, ctx)?; + cip509_metadatum.x509_chunks = x509_chunks; + } + } + Ok(cip509_metadatum) + } +} + +impl Cip509 { + /// Basic validation for CIP509 + /// The validation include the following: + /// * Hashing the transaction inputs within the transaction should match the + /// txn-inputs-hash + /// * Auxiliary data hash within the transaction should match the hash of the + /// auxiliary data itself. + /// * Public key validation for role 0 where public key extracted from x509 and c509 + /// subject alternative name should match one of the witness in witness set within + /// the transaction. + /// * Payment key reference validation for role 0 where the reference should be either + /// 1. Negative index reference - reference to transaction output in transaction: + /// should match some of the key within witness set. + /// 2. Positive index reference - reference to the transaction input in + /// transaction: only check whether the index exist within the transaction + /// inputs. + /// + /// See: + /// * + /// * + /// + /// Note: This CIP509 is still under development and is subject to change. + /// + /// # Parameters + /// * `txn` - Transaction data was attached to and to be validated/decoded against. + /// * `txn_idx` - Transaction Index + /// * `validation_report` - Validation report to store the validation result. + pub fn validate(&self, txn: &MultiEraTx, txn_idx: usize, validation_report: &mut Vec) { + self.validate_txn_inputs_hash(txn, validation_report); + self.validate_aux(txn, validation_report); + // Validate the role 0 + if let Some(role_set) = &self.x509_chunks.0.role_set { + // Validate only role 0 + for role in role_set { + if role.role_number == 0 { + self.validate_stake_public_key(txn, txn_idx, validation_report); + self.validate_payment_key(txn, txn_idx, role, validation_report); + } + } + } + } + + /// Transaction inputs hash validation. + /// Must exist and match the hash of the transaction inputs. + fn validate_txn_inputs_hash( + &self, txn: &MultiEraTx, validation_report: &mut Vec, + ) -> Option { + let mut buffer = Vec::new(); + let mut e = Encoder::new(&mut buffer); + // CIP-0509 is expected to be in conway era + if let MultiEraTx::Conway(tx) = txn { + let inputs = tx.transaction_body.inputs.clone(); + if let Err(e) = e.array(inputs.len() as u64) { + validation_report.push(format!( + "Failed to encode array of transaction input in validate_txn_inputs_hash: {e}" + )); + return None; + } + for input in &inputs { + match input.encode(&mut e, &mut ()) { + Ok(()) => {}, + Err(e) => { + validation_report.push(format!( + "Failed to encode transaction input in validate_txn_inputs_hash {e}" + )); + return None; + }, + } + } + let inputs_hash = match blake2b_128(&buffer) { + Ok(hash) => hash, + Err(e) => { + validation_report.push(format!( + "Failed to hash transaction inputs in validate_txn_inputs_hash {e}" + )); + return None; + }, + }; + Some(inputs_hash == self.txn_inputs_hash) + } else { + validation_report.push( + "Unsupported transaction era for transaction inputs hash validation".to_string(), + ); + None + } + } + + /// Validate the auxiliary data with the auxiliary data hash in the transaction. + fn validate_aux(&self, txn: &MultiEraTx, validation_report: &mut Vec) -> Option { + // CIP-0509 is expected to be in conway era + if let MultiEraTx::Conway(tx) = txn { + if let pallas::codec::utils::Nullable::Some(a) = &tx.auxiliary_data { + let original_aux = a.raw_cbor(); + let aux_data_hash = + tx.transaction_body + .auxiliary_data_hash + .as_ref() + .or_else(|| { + validation_report + .push("Auxiliary data hash not found in transaction".to_string()); + None + })?; + validate_aux_helper(original_aux, aux_data_hash, validation_report) + } else { + validation_report.push("Auxiliary data not found in transaction".to_string()); + None + } + } else { + validation_report + .push("Unsupported transaction era for auxiliary data validation".to_string()); + None + } + } + + /// Validate the stake public key in the certificate with witness set in transaction. + #[allow(clippy::too_many_lines)] + fn validate_stake_public_key( + &self, txn: &MultiEraTx, txn_idx: usize, validation_report: &mut Vec, + ) -> Option { + let mut pk_addrs = Vec::new(); + + // CIP-0509 is expected to be in conway era + if let MultiEraTx::Conway(_) = txn { + // X509 certificate + if let Some(x509_certs) = &self.x509_chunks.0.x509_certs { + for cert in x509_certs { + // Attempt to decode the DER certificate + let der_cert = match x509_cert::Certificate::from_der(&cert.0) { + Ok(cert) => cert, + Err(e) => { + validation_report + .push(format!("Failed to decode x509 certificate DER: {e}")); + return None; + }, + }; + + // Find the Subject Alternative Name extension + let san_ext = der_cert + .tbs_certificate + .extensions + .as_ref() + .and_then(|exts| { + exts.iter() + .find(|ext| ext.extn_id == ID_CE_SUBJECT_ALT_NAME) + }); + + // Subject Alternative Name extension if it exists + if let Some(san_ext) = san_ext { + match parse_der_sequence(san_ext.extn_value.as_bytes()) { + Ok((_, parsed_seq)) => { + for data in parsed_seq.ref_iter() { + // Check for context-specific primitive type with tag number + // 6 (raw_tag 134) + if data.header.raw_tag() == Some(&[URI]) { + match data.content.as_slice() { + Ok(content) => { + // Decode the UTF-8 string + let addr: String = match decode_utf8(content) { + Ok(addr) => addr, + Err(e) => { + validation_report.push(format!( + "Failed to decode UTF-8 string for context-specific primitive type with raw tag 134: {e}", + ), + ); + return None; + }, + }; + + // Extract the CIP19 hash and push into + // array + if let Some(h) = + extract_cip19_hash(&addr, Some("stake")) + { + pk_addrs.push(h); + } + }, + Err(e) => { + validation_report.push( + format!("Failed to process content for context-specific primitive type with raw tag 134: {e}")); + return None; + }, + } + } + } + }, + Err(e) => { + validation_report.push( + format!( + "Failed to parse DER sequence for Subject Alternative Name extension: {e}" + ) + ); + return None; + }, + } + } + } + } + // C509 Certificate + if let Some(c509_certs) = &self.x509_chunks.0.c509_certs { + for cert in c509_certs { + match cert { + C509Cert::C509CertInMetadatumReference(_) => { + validation_report.push( + "C509 metadatum reference is currently not supported".to_string(), + ); + }, + C509Cert::C509Certificate(c509) => { + for exts in c509.tbs_cert().extensions().extensions() { + if *exts.registered_oid().c509_oid().oid() + == C509ExtensionType::SubjectAlternativeName.oid() + { + match exts.value() { + c509_certificate::extensions::extension::ExtensionValue::AlternativeName(alt_name) => { + match alt_name.general_name() { + c509_certificate::extensions::alt_name::GeneralNamesOrText::GeneralNames(gn) => { + for name in gn.general_names() { + if name.gn_type() == &c509_certificate::general_names::general_name::GeneralNameTypeRegistry::UniformResourceIdentifier { + match name.gn_value() { + GeneralNameValue::Text(s) => { + if let Some(h) = extract_cip19_hash(s, Some("stake")) { + pk_addrs.push(h); + } + }, + _ => { + validation_report.push( + "Failed to get the value of subject alternative name".to_string(), + ); + } + } + } + } + }, + c509_certificate::extensions::alt_name::GeneralNamesOrText::Text(_) => { + validation_report.push( + "Failed to find C509 general names in subject alternative name".to_string(), + ); + } + } + }, + _ => { + validation_report.push( + "Failed to get C509 subject alternative name".to_string() + ); + } + } + } + } + }, + } + } + } + } else { + validation_report + .push("Unsupported transaction era for stake public key validation".to_string()); + return None; + } + + // Create TxWitness + let witnesses = match TxWitness::new(&[txn.clone()]) { + Ok(witnesses) => witnesses, + Err(e) => { + validation_report.push(format!("Failed to create TxWitness: {e}")); + return None; + }, + }; + + let index = match u16::try_from(txn_idx) { + Ok(value) => value, + Err(e) => { + validation_report + .push(format!("Failed to convert transaction index to usize: {e}")); + return None; + }, + }; + Some( + compare_key_hash(&pk_addrs, &witnesses, index) + .map_err(|e| { + validation_report + .push(format!("Failed to compare public keys with witnesses {e}")); + }) + .is_ok(), + ) + } + + /// Validate the payment key + fn validate_payment_key( + &self, txn: &MultiEraTx, txn_idx: usize, role_data: &RoleData, + validation_report: &mut Vec, + ) -> Option { + if let Some(payment_key) = role_data.payment_key { + if payment_key == 0 { + validation_report + .push("Invalid payment reference key, 0 is not allowed".to_string()); + return None; + } + // CIP-0509 is expected to be in conway era + if let MultiEraTx::Conway(tx) = txn { + // Negative indicates reference to tx output + if payment_key < 0 { + let index = match decremented_index(payment_key.abs()) { + Ok(value) => value, + Err(e) => { + validation_report + .push(format!("Failed to get index of payment key: {e}")); + return None; + }, + }; + let outputs = tx.transaction_body.outputs.clone(); + let witness = match TxWitness::new(&[txn.clone()]) { + Ok(witnesses) => witnesses, + Err(e) => { + validation_report.push(format!("Failed to create TxWitness: {e}")); + return None; + }, + }; + + if let Some(output) = outputs.get(index) { + match output { + pallas::ledger::primitives::conway::PseudoTransactionOutput::Legacy(o) => { + return validate_payment_output_key_helper(&o.address.to_vec(), validation_report, &witness, txn_idx); + }, + pallas::ledger::primitives::conway::PseudoTransactionOutput::PostAlonzo(o) => { + return validate_payment_output_key_helper(&o.address.to_vec(), validation_report, &witness, txn_idx); + }, + }; + } + validation_report.push( + "Role payment key reference index is not found in transaction outputs" + .to_string(), + ); + return None; + } + // Positive indicates reference to tx input + let inputs = &tx.transaction_body.inputs; + let index = match decremented_index(payment_key) { + Ok(value) => value, + Err(e) => { + validation_report.push(format!("Failed to get index of payment key: {e}")); + return None; + }, + }; + // Check whether the index exists in transaction inputs + if inputs.get(index).is_none() { + validation_report.push( + "Role payment key reference index is not found in transaction inputs" + .to_string(), + ); + return None; + } + Some(true) + } else { + validation_report.push( + "Unsupported transaction era for stake payment key validation".to_string(), + ); + None + } + } else { + Some(false) + } + } +} + +/// Helper function for auxiliary data validation. +fn validate_aux_helper( + original_aux: &[u8], aux_data_hash: &Bytes, validation_report: &mut Vec, +) -> Option { + // Compare the hash + match blake2b_256(original_aux) { + Ok(original_hash) => { + return Some(aux_data_hash.as_ref() == original_hash); + }, + Err(e) => { + validation_report.push(format!("Cannot hash auxiliary data {e}")); + None + }, + } +} + +/// Helper function for validating payment output key. +fn validate_payment_output_key_helper( + output_address: &[u8], validation_report: &mut Vec, witness: &TxWitness, txn_idx: usize, +) -> Option { + let idx = match u16::try_from(txn_idx) { + Ok(value) => value, + Err(e) => { + validation_report.push(format!("Transaction index conversion failed: {e}")); + return None; + }, + }; + // Extract the key hash from the output address + if let Some(key) = extract_key_hash(output_address) { + // Compare the key hash and return the result + return Some(compare_key_hash(&[key], witness, idx).is_ok()); + } + validation_report.push("Failed to extract payment key hash from address".to_string()); + None +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::cardano::transaction::raw_aux_data::RawAuxData; + fn conway_1() -> Vec { + hex::decode(include_str!("../../test_data/cardano/conway_1.block")) + .expect("Failed to decode hex block.") + } + + fn conway_2() -> Vec { + hex::decode(include_str!("../../test_data/cardano/conway_2.block")) + .expect("Failed to decode hex block.") + } + + fn conway_3() -> Vec { + hex::decode(include_str!("../../test_data/cardano/conway_3.block")) + .expect("Failed to decode hex block.") + } + + fn cip_509_aux_data(tx: &MultiEraTx<'_>) -> Vec { + let raw_auxiliary_data = tx + .as_conway() + .unwrap() + .clone() + .auxiliary_data + .map(|aux| aux.raw_cbor()); + + let raw_cbor_data = match raw_auxiliary_data { + pallas::codec::utils::Nullable::Some(data) => Ok(data), + _ => Err("Auxiliary data not found"), + }; + + let auxiliary_data = RawAuxData::new(raw_cbor_data.expect("Failed to get raw cbor data")); + auxiliary_data + .get_metadata(LABEL) + .expect("Failed to get metadata") + .to_vec() + } + + #[test] + fn test_decode_cip509() { + // This data is from conway_1.block + let cip_509 = "a50050ca7a1457ef9f4c7f9c747f8c4a4cfa6c0150226d126819472b7afad7d0b8c7b89aa20258204d3f576f26db29139981a69443c2325daa812cc353a31b5a4db794a5bcbb06c20b9458401b03060066006fd5b67002167882eac0b5f2b11da40788a39bfa0324c494f7003a6b4c1c4bac378e322cb280230a4002f5b2754e863806f7e524afc99996aa28584032f02b600cbf04c6a09e05100880a09ee59b6627dc78d68175469b8c5b1fac141a6da5c6c2ea446597b6f0b6efea00a04ac0c1756455589908a5e089ba604a1258405917d6ee2b2535959d806c00eb2958929ababb40d681b5245751538e915d3d90f561ddcaa9aaa9cd78a30882a22a99c742c4f7610b43750a0d6651e8640a8d4c58402167427cfa933d6430c026640888210cd0c4e93e7015100300dcaef47b9c155ea4ccb27773c27f5d6a44fbf98065a14e5f0eca530e57082a971cbf22fa9065585840ae72e2a061eb558d3fd7727e87a8f07b5faf0d3cedf8d99ab6e0c845f5dd3ce78d31d7365c523b5a4dfe5d35bfafaefb2f60dd7473cbe8d6aa6bf557b1fbdf775840bf96bcd3ffdbfc7d20b65be7f5c7dba1cf635e3b449bb644bdfc73e0e49a5db73edddc7ed331220ba732f62f3aee8503f5c6f9bd5f7fedb37dc6580196052e50584027fdd7e8bfe9146561ad1ebc79ecef0ee1df7081cf9cd1fd929569ef3d55972d5b7ff882ce2213f789fc08787164f14aa86d55e98e332b220a07fa464aaa7c335840ce4bcfb268ed577f72e87fdea4442107bf2da93fe05121d5befa7ae5aecc5f3f9c732e82108003166380198c0146b0e214114a31d7c62b0ec18afd5834034c2b58402b2c515b350d8980a16932071b6d8d125ea1eb53dc28a8aee1787a1670b9e8c4c8cb00c726f3515a39ca1689f870295752820a64721e05e1a234710583416316584031d80291ac9a2b66a04cba844b85f9928a4a04a9928b2805124a25b3aaa4422e45e5d422a9b88a028ba4a5123ac244b8b472164b86085ac21357c3aae7696be25840f1104878009b03813d9e6c53255722402090206058a009d2b808aff772fb712d75f1dea09507fd83838e045dd9ce3eb59e4554f5ed02b8aeb60700f4b39dd9fe584064e1d5a137de0fa4c6cccfd71f831bee372756d72990b357a44e2f9eaf3854db65379db466cfcb55517ba71550acade564f4b7efd1fd95fa57228cee6fa9ae3458405ce1ae79b77f7cd5bdecfcb800fbdb7eaf720eae5995176d94a07c326c71aaf5e6f8439e577edb2d1ed64959324b5a7476e9159bf37bdf226edb747787b79b9e5840bc6ab5b84714eefa4a8c2df4aba37a36757d8b39dd79ec41b4a2f3ee96eabdc0e1f65b37264bdbfdf79eebbc820a7deab4e39f7e1cbf6610402fd8fb55fbef3d584038226e4d37c42970c830184b2e1c5026eadb9677ae8f6d300975ca6ceec5c8920382e827c1f636f7dd9f8d492737f4520a944bfeebba5ca2d5efa80ad453a43f584004c357ecccfc4dab75ce560b0300db9092ced52625d0c8df6fc89da9a45b6dc9c2461f21e6ee7b7afd877fbd8c1a1fa7ff38fa506e14749ebb68e24571c6220c584004208c284d628c2148b252f91b8b50014b080b040554095b52ca862bb974218222d412112ae5d2584c54584ae157f22b183cb4ba9c5fc42ba6894ad074ffe0875840c69ee921211d0ce4cd0f89b7e708163b3ab9286fe26a8c68ed85930cabc5dbfed7f9681c535dbdbfeb56f7a2b32d1f43de1dbcc934676edefacb3df7c1210067584064a1b8d94448b7f22a77dc736edb12f7c2c52b2eb8d4a80b78147d89f9a3a0659c03e10bbb336e391b3961f1afbfa08af3de2a817fceddea0cb57f438b0f8947581e9782ee92e890df65636d835d2d465cc5521c0ec05470e002800015eecf5818635840e0427f23196c17cf13f030595335343030c11d914bc7a84b56af7040930af4110fd4ca29b0bc0e83789adb8668ea2ef28c1dd10dc1fd35ea6ae8c06ee769540d"; + let binding = hex::decode(cip_509).unwrap(); + let mut decoder = Decoder::new(binding.as_slice()); + let decoded_cip509 = Cip509::decode(&mut decoder, &mut ()).unwrap(); + + let purpose: [u8; 16] = hex::decode("ca7a1457ef9f4c7f9c747f8c4a4cfa6c") + .unwrap() + .try_into() + .unwrap(); + let txn_inputs_hash: [u8; 16] = hex::decode("226d126819472b7afad7d0b8c7b89aa2") + .unwrap() + .try_into() + .unwrap(); + let prv_tx_id: [u8; 32] = + hex::decode("4d3f576f26db29139981a69443c2325daa812cc353a31b5a4db794a5bcbb06c2") + .unwrap() + .try_into() + .unwrap(); + let validation_signature = hex::decode("e0427f23196c17cf13f030595335343030c11d914bc7a84b56af7040930af4110fd4ca29b0bc0e83789adb8668ea2ef28c1dd10dc1fd35ea6ae8c06ee769540d").unwrap(); + + assert_eq!(decoded_cip509.purpose, purpose); + assert_eq!(decoded_cip509.txn_inputs_hash, txn_inputs_hash); + assert_eq!(decoded_cip509.prv_tx_id, Some(prv_tx_id)); + assert_eq!(decoded_cip509.validation_signature, validation_signature); + } + + #[test] + fn test_validate_txn_inputs_hash() { + let mut validation_report = Vec::new(); + let conway_block_data = conway_1(); + let multi_era_block = pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data) + .expect("Failed to decode MultiEraBlock"); + + let transactions = multi_era_block.txs(); + // Forth transaction of this test data contains the CIP509 auxiliary data + let tx = transactions + .get(3) + .expect("Failed to get transaction index"); + let aux_data = cip_509_aux_data(tx); + + let mut decoder = Decoder::new(aux_data.as_slice()); + let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); + assert!(cip509 + .validate_txn_inputs_hash(tx, &mut validation_report) + .unwrap()); + } + + #[test] + fn test_validate_aux() { + let mut validation_report = Vec::new(); + let conway_block_data = conway_1(); + let multi_era_block = pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data) + .expect("Failed to decode MultiEraBlock"); + + let transactions = multi_era_block.txs(); + // Forth transaction of this test data contains the CIP509 auxiliary data + let tx = transactions + .get(3) + .expect("Failed to get transaction index"); + + let aux_data = cip_509_aux_data(tx); + + let mut decoder = Decoder::new(aux_data.as_slice()); + let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); + cip509.validate_aux(tx, &mut validation_report); + assert!(cip509.validate_aux(tx, &mut validation_report).unwrap()); + } + + #[test] + fn test_validate_public_key_success() { + let mut validation_report = Vec::new(); + let conway_block_data = conway_1(); + let multi_era_block = pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data) + .expect("Failed to decode MultiEraBlock"); + + let transactions = multi_era_block.txs(); + // Forth transaction of this test data contains the CIP509 auxiliary data + let tx = transactions + .get(3) + .expect("Failed to get transaction index"); + + let aux_data = cip_509_aux_data(tx); + + let mut decoder = Decoder::new(aux_data.as_slice()); + let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); + assert!(cip509 + .validate_stake_public_key(tx, 0, &mut validation_report) + .unwrap()); + } + + #[test] + fn test_validate_payment_key_success_positive_ref() { + let mut validation_report = Vec::new(); + let conway_block_data = conway_1(); + let multi_era_block = pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data) + .expect("Failed to decode MultiEraBlock"); + + let transactions = multi_era_block.txs(); + // Forth transaction of this test data contains the CIP509 auxiliary data + let tx = transactions + .get(3) + .expect("Failed to get transaction index"); + + let aux_data = cip_509_aux_data(tx); + + let mut decoder = Decoder::new(aux_data.as_slice()); + let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); + + if let Some(role_set) = &cip509.x509_chunks.0.role_set { + for role in role_set { + if role.role_number == 0 { + assert!(cip509 + .validate_payment_key(tx, 0, role, &mut validation_report,) + .unwrap()); + } + } + } + } + + #[test] + fn test_validate_payment_key_success_negative_ref() { + let mut validation_report = Vec::new(); + let conway_block_data = conway_3(); + let multi_era_block = pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data) + .expect("Failed to decode MultiEraBlock"); + + let transactions = multi_era_block.txs(); + // First transaction of this test data contains the CIP509 auxiliary data + let tx = transactions + .first() + .expect("Failed to get transaction index"); + + let aux_data = cip_509_aux_data(tx); + + let mut decoder = Decoder::new(aux_data.as_slice()); + let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); + + if let Some(role_set) = &cip509.x509_chunks.0.role_set { + for role in role_set { + if role.role_number == 0 { + println!( + "{:?}", + cip509.validate_payment_key(tx, 0, role, &mut validation_report,) + ); + } + } + } + } + + #[test] + fn test_validate_public_key_fail() { + let mut validation_report = Vec::new(); + let conway_block_data = conway_2(); + let multi_era_block = pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data) + .expect("Failed to decode MultiEraBlock"); + + let transactions = multi_era_block.txs(); + // Forth transaction of this test data contains the CIP509 auxiliary data + let tx = transactions + .get(3) + .expect("Failed to get transaction index"); + + let aux_data = cip_509_aux_data(tx); + + let mut decoder = Decoder::new(aux_data.as_slice()); + let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); + assert!(!cip509 + .validate_stake_public_key(tx, 0, &mut validation_report) + .unwrap()); + } +} diff --git a/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs b/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs new file mode 100644 index 00000000000..a139b8b859b --- /dev/null +++ b/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs @@ -0,0 +1,99 @@ +//! Certificates for the RBAC metadata. + +use c509_certificate::c509::C509; +use minicbor::{decode, Decode, Decoder}; +use x509_cert::{der::Decode as x509Decode, Certificate}; + +use crate::utils::decode_helper::{decode_array_len, decode_bytes, decode_helper}; + +// ------------------x509------------------------ + +/// A struct of X509 certificate. +#[derive(Debug, PartialEq, Clone)] +pub struct X509DerCert(pub Vec); + +impl Decode<'_, ()> for X509DerCert { + fn decode(d: &mut Decoder, _ctx: &mut ()) -> Result { + let data = decode_bytes(d, "X509DerCert")?; + Certificate::from_der(&data) + .map_err(|_| decode::Error::message("Invalid x509 certificate"))?; + Ok(Self(data.clone())) + } +} + +// ------------------c509----------------------- + +/// Enum of possible c509 certificate. +#[derive(Debug, PartialEq, Clone)] +pub enum C509Cert { + /// A c509 certificate in metadatum reference. + C509CertInMetadatumReference(C509CertInMetadatumReference), + /// A c509 certificate. + C509Certificate(Box), +} + +impl Decode<'_, ()> for C509Cert { + fn decode(d: &mut Decoder, ctx: &mut ()) -> Result { + if d.datatype()? == minicbor::data::Type::Array { + let arr_len = decode_array_len(d, "C509Cert")?; + // C509CertInMetadatumReference must have 3 items + if arr_len == 3 { + Ok(Self::C509CertInMetadatumReference( + C509CertInMetadatumReference::decode(d, ctx)?, + )) + } else { + Err(decode::Error::message( + "Invalid length C509CertInMetadatumReference, expected 3", + )) + } + } else { + // Consuming the c509 bytes + let c509 = decode_bytes(d, "C509Cert")?; + let mut c509_d = Decoder::new(&c509); + Ok(Self::C509Certificate(Box::new(C509::decode( + &mut c509_d, + ctx, + )?))) + } + } +} + +/// A struct of c509 certificate in metadatum reference. +#[derive(Debug, PartialEq, Clone)] +pub struct C509CertInMetadatumReference { + /// Transaction output field. + txn_output_field: u8, + /// Transaction output index. + txn_output_index: u64, + /// Optional certificate reference. + cert_ref: Option>, +} + +impl Decode<'_, ()> for C509CertInMetadatumReference { + fn decode(d: &mut Decoder, ctx: &mut ()) -> Result { + let txn_output_field: u8 = + decode_helper(d, "txn output field in C509CertInMetadatumReference", ctx)?; + let txn_output_index: u64 = + decode_helper(d, "txn output index in C509CertInMetadatumReference", ctx)?; + let cert_ref = match d.datatype()? { + minicbor::data::Type::Array => { + let len = decode_array_len(d, "cert ref in C509CertInMetadatumReference")?; + let arr: Result, _> = (0..len).map(|_| d.u64()).collect(); + arr.map(Some) + }, + minicbor::data::Type::Null => Ok(None), + _ => { + Ok(Some(vec![decode_helper( + d, + "C509CertInMetadatumReference", + ctx, + )?])) + }, + }?; + Ok(Self { + txn_output_field, + txn_output_index, + cert_ref, + }) + } +} diff --git a/rust/rbac-registration/src/cardano/cip509/rbac/mod.rs b/rust/rbac-registration/src/cardano/cip509/rbac/mod.rs new file mode 100644 index 00000000000..51a96378a77 --- /dev/null +++ b/rust/rbac-registration/src/cardano/cip509/rbac/mod.rs @@ -0,0 +1,167 @@ +//! Role Based Access Control (RBAC) metadata for CIP509. +//! Doc Reference: +//! CDDL Reference: + +pub mod certs; +pub mod pub_key; +pub mod role_data; + +use std::collections::HashMap; + +use certs::{C509Cert, X509DerCert}; +use minicbor::{decode, Decode, Decoder}; +use pub_key::SimplePublicKeyType; +use role_data::RoleData; +use strum_macros::FromRepr; + +use crate::utils::decode_helper::{ + decode_any, decode_array_len, decode_bytes, decode_helper, decode_map_len, +}; + +/// Struct of Cip509 RBAC metadata. +#[derive(Debug, PartialEq, Clone, Default)] +pub struct Cip509RbacMetadata { + /// Optional list of x509 certificates. + pub x509_certs: Option>, + /// Optional list of c509 certificates. + /// The value can be either the c509 certificate or c509 metadatum reference. + pub c509_certs: Option>, + /// Optional list of Public keys. + pub pub_keys: Option>, + /// Optional list of revocation list. + pub revocation_list: Option>, + /// Optional list of role data. + pub role_set: Option>, + /// Optional map of purpose key data. + /// Empty map if no purpose key data is present. + pub purpose_key_data: HashMap>, +} + +/// The first valid purpose key. +const FIRST_PURPOSE_KEY: u16 = 200; +/// The last valid purpose key. +const LAST_PURPOSE_KEY: u16 = 299; + +/// Enum of CIP509 RBAC metadata with its associated unsigned integer value. +#[derive(FromRepr, Debug, PartialEq)] +#[repr(u16)] +pub enum Cip509RbacMetadataInt { + /// x509 certificates. + X509Certs = 10, + /// c509 certificates. + C509Certs = 20, + /// Public keys. + PubKeys = 30, + /// Revocation list. + RevocationList = 40, + /// Role data set. + RoleSet = 100, +} + +impl Cip509RbacMetadata { + /// Create a new instance of `Cip509RbacMetadata`. + pub(crate) fn new() -> Self { + Self { + x509_certs: None, + c509_certs: None, + pub_keys: None, + revocation_list: None, + role_set: None, + purpose_key_data: HashMap::new(), + } + } + + /// Set the x509 certificates. + fn set_x509_certs(&mut self, x509_certs: Vec) { + self.x509_certs = Some(x509_certs); + } + + /// Set the c509 certificates. + fn set_c509_certs(&mut self, c509_certs: Vec) { + self.c509_certs = Some(c509_certs); + } + + /// Set the public keys. + fn set_pub_keys(&mut self, pub_keys: Vec) { + self.pub_keys = Some(pub_keys); + } + + /// Set the revocation list. + fn set_revocation_list(&mut self, revocation_list: Vec<[u8; 16]>) { + self.revocation_list = Some(revocation_list); + } + + /// Set the role data set. + fn set_role_set(&mut self, role_set: Vec) { + self.role_set = Some(role_set); + } +} + +impl Decode<'_, ()> for Cip509RbacMetadata { + fn decode(d: &mut Decoder, ctx: &mut ()) -> Result { + let map_len = decode_map_len(d, "Cip509RbacMetadata")?; + + let mut x509_rbac_metadata = Cip509RbacMetadata::new(); + + for _ in 0..map_len { + let key: u16 = decode_helper(d, "key in Cip509RbacMetadata", ctx)?; + if let Some(key) = Cip509RbacMetadataInt::from_repr(key) { + match key { + Cip509RbacMetadataInt::X509Certs => { + let x509_certs = decode_array_rbac(d, "x509 certificate")?; + x509_rbac_metadata.set_x509_certs(x509_certs); + }, + Cip509RbacMetadataInt::C509Certs => { + let c509_certs = decode_array_rbac(d, "c509 certificate")?; + x509_rbac_metadata.set_c509_certs(c509_certs); + }, + Cip509RbacMetadataInt::PubKeys => { + let pub_keys = decode_array_rbac(d, "public keys")?; + x509_rbac_metadata.set_pub_keys(pub_keys); + }, + Cip509RbacMetadataInt::RevocationList => { + let revocation_list = decode_revocation_list(d)?; + x509_rbac_metadata.set_revocation_list(revocation_list); + }, + Cip509RbacMetadataInt::RoleSet => { + let role_set = decode_array_rbac(d, "role set")?; + x509_rbac_metadata.set_role_set(role_set); + }, + } + } else { + if !(FIRST_PURPOSE_KEY..=LAST_PURPOSE_KEY).contains(&key) { + return Err(decode::Error::message(format!("Invalid purpose key set, should be with the range {FIRST_PURPOSE_KEY} - {LAST_PURPOSE_KEY}"))); + } + x509_rbac_metadata + .purpose_key_data + .insert(key, decode_any(d, "purpose key")?); + } + } + Ok(x509_rbac_metadata) + } +} + +/// Decode an array of type T. +fn decode_array_rbac<'b, T>(d: &mut Decoder<'b>, from: &str) -> Result, decode::Error> +where T: Decode<'b, ()> { + let len = decode_array_len(d, &format!("{from} Cip509RbacMetadata"))?; + let mut vec = Vec::with_capacity(usize::try_from(len).map_err(decode::Error::message)?); + for _ in 0..len { + vec.push(T::decode(d, &mut ())?); + } + Ok(vec) +} + +/// Decode an array of revocation list. +fn decode_revocation_list(d: &mut Decoder) -> Result, decode::Error> { + let len = decode_array_len(d, "revocation list Cip509RbacMetadata")?; + let mut revocation_list = + Vec::with_capacity(usize::try_from(len).map_err(decode::Error::message)?); + for _ in 0..len { + let arr: [u8; 16] = decode_bytes(d, "revocation list Cip509RbacMetadata")? + .try_into() + .map_err(|_| decode::Error::message("Invalid revocation list size"))?; + revocation_list.push(arr); + } + Ok(revocation_list) +} diff --git a/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs b/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs new file mode 100644 index 00000000000..a7aa072ed53 --- /dev/null +++ b/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs @@ -0,0 +1,69 @@ +//! Public key type for RBAC metadata + +use minicbor::{data::Tag, decode, Decode, Decoder}; + +use crate::utils::decode_helper::{decode_bytes, decode_tag}; + +/// Enum of possible public key type. +#[derive(Debug, PartialEq, Clone, Default)] +pub enum SimplePublicKeyType { + /// Undefined indicates skipped element. + #[default] + Undefined, + /// Deleted indicates the key is deleted. + Deleted, + /// Ed25519 key. + Ed25519([u8; 32]), +} + +/// Enum of possible public key tag. +enum PublicKeyTag { + /// Deleted Key tag 31. + Deleted, + /// Ed25519 Key tag 32773. + Ed25519, +} + +impl PublicKeyTag { + /// Get the tag value. + fn tag(self) -> Tag { + match self { + PublicKeyTag::Deleted => Tag::new(0x31), + PublicKeyTag::Ed25519 => Tag::new(0x8005), + } + } +} + +impl Decode<'_, ()> for SimplePublicKeyType { + fn decode(d: &mut Decoder, _ctx: &mut ()) -> Result { + match d.datatype()? { + minicbor::data::Type::Tag => { + let tag = decode_tag(d, "SimplePublicKeyType")?; + match tag { + t if t == PublicKeyTag::Deleted.tag() => Ok(SimplePublicKeyType::Deleted), + t if t == PublicKeyTag::Ed25519.tag() => { + let bytes = decode_bytes(d, "Ed25519 SimplePublicKeyType")?; + let mut ed25519 = [0u8; 32]; + if bytes.len() == 32 { + ed25519.copy_from_slice(&bytes); + Ok(SimplePublicKeyType::Ed25519(ed25519)) + } else { + Err(decode::Error::message("Invalid length for Ed25519 key")) + } + }, + _ => { + Err(decode::Error::message( + "Unknown tag for SimplePublicKeyType", + )) + }, + } + }, + minicbor::data::Type::Undefined => Ok(SimplePublicKeyType::Undefined), + _ => { + Err(decode::Error::message( + "Invalid datatype for SimplePublicKeyType", + )) + }, + } + } +} diff --git a/rust/rbac-registration/src/cardano/cip509/rbac/role_data.rs b/rust/rbac-registration/src/cardano/cip509/rbac/role_data.rs new file mode 100644 index 00000000000..9d3a0bd908f --- /dev/null +++ b/rust/rbac-registration/src/cardano/cip509/rbac/role_data.rs @@ -0,0 +1,141 @@ +//! Role data for RBAC metadata. + +use std::collections::HashMap; + +use minicbor::{decode, Decode, Decoder}; +use strum_macros::FromRepr; + +use super::{decode_any, decode_map_len, Cip509RbacMetadataInt}; +use crate::utils::decode_helper::{decode_array_len, decode_bytes, decode_helper}; + +/// Struct of role data. +#[derive(Debug, PartialEq, Clone, Default)] +pub struct RoleData { + /// Role number. + pub role_number: u8, + /// Optional role signing key. + pub role_signing_key: Option, + /// Optional role encryption key. + pub role_encryption_key: Option, + /// Optional payment key. + pub payment_key: Option, + /// Optional role extended data keys. + /// Empty map if no role extended data keys. + pub role_extended_data_keys: HashMap>, +} + +/// The first valid role extended data key. +const FIRST_ROLE_EXT_KEY: u8 = 10; +/// The last valid role extended data key. +const LAST_ROLE_EXT_KEY: u8 = 99; + +/// Enum of role data with its associated unsigned integer value. +#[allow(clippy::module_name_repetitions)] +#[derive(FromRepr, Debug, PartialEq)] +#[repr(u8)] +pub enum RoleDataInt { + /// Role number. + RoleNumber = 0, + /// Role signing key. + RoleSigningKey = 1, + /// Role encryption key. + RoleEncryptionKey = 2, + /// Payment key. + PaymentKey = 3, +} + +impl Decode<'_, ()> for RoleData { + fn decode(d: &mut Decoder, ctx: &mut ()) -> Result { + let map_len = decode_map_len(d, "RoleData")?; + let mut role_data = RoleData::default(); + for _ in 0..map_len { + let key: u8 = decode_helper(d, "key in RoleData", ctx)?; + if let Some(key) = RoleDataInt::from_repr(key) { + match key { + RoleDataInt::RoleNumber => { + role_data.role_number = decode_helper(d, "RoleNumber in RoleData", ctx)?; + }, + RoleDataInt::RoleSigningKey => { + role_data.role_signing_key = Some(KeyReference::decode(d, ctx)?); + }, + RoleDataInt::RoleEncryptionKey => { + role_data.role_encryption_key = Some(KeyReference::decode(d, ctx)?); + }, + RoleDataInt::PaymentKey => { + role_data.payment_key = + Some(decode_helper(d, "PaymentKey in RoleData", ctx)?); + }, + } + } else { + if !(FIRST_ROLE_EXT_KEY..=LAST_ROLE_EXT_KEY).contains(&key) { + return Err(decode::Error::message(format!("Invalid role extended data key, should be with the range {FIRST_ROLE_EXT_KEY} - {LAST_ROLE_EXT_KEY}"))); + } + role_data + .role_extended_data_keys + .insert(key, decode_any(d, "Role extended data keys")?); + } + } + Ok(role_data) + } +} + +/// Enum of key reference. +#[derive(Debug, PartialEq, Clone)] +pub enum KeyReference { + /// Key local reference. + KeyLocalRef(KeyLocalRef), + /// Key hash. + KeyHash(Vec), +} + +impl Default for KeyReference { + fn default() -> Self { + KeyReference::KeyHash(Vec::new()) + } +} + +impl Decode<'_, ()> for KeyReference { + fn decode(d: &mut Decoder, ctx: &mut ()) -> Result { + match d.datatype()? { + minicbor::data::Type::Array => Ok(Self::KeyLocalRef(KeyLocalRef::decode(d, ctx)?)), + minicbor::data::Type::Bytes => { + Ok(Self::KeyHash(decode_bytes(d, "KeyHash in KeyReference")?)) + }, + _ => Err(decode::Error::message("Invalid data type for KeyReference")), + } + } +} + +/// Struct of key local reference. +#[derive(Debug, PartialEq, Clone)] +pub struct KeyLocalRef { + /// Local reference. + pub local_ref: LocalRefInt, + /// Key offset. + pub key_offset: u64, +} + +/// Enum of local reference with its associated unsigned integer value. +#[derive(FromRepr, Debug, PartialEq, Clone)] +#[repr(u8)] +pub enum LocalRefInt { + /// x509 certificates. + X509Certs = Cip509RbacMetadataInt::X509Certs as u8, // 10 + /// c509 certificates. + C509Certs = Cip509RbacMetadataInt::C509Certs as u8, // 20 + /// Public keys. + PubKeys = Cip509RbacMetadataInt::PubKeys as u8, // 30 +} + +impl Decode<'_, ()> for KeyLocalRef { + fn decode(d: &mut Decoder, ctx: &mut ()) -> Result { + decode_array_len(d, "KeyLocalRef")?; + let local_ref = LocalRefInt::from_repr(decode_helper(d, "LocalRef in KeyLocalRef", ctx)?) + .ok_or(decode::Error::message("Invalid local reference"))?; + let key_offset: u64 = decode_helper(d, "KeyOffset in KeyLocalRef", ctx)?; + Ok(Self { + local_ref, + key_offset, + }) + } +} diff --git a/rust/rbac-registration/src/cardano/cip509/x509_chunks.rs b/rust/rbac-registration/src/cardano/cip509/x509_chunks.rs new file mode 100644 index 00000000000..55a0642e913 --- /dev/null +++ b/rust/rbac-registration/src/cardano/cip509/x509_chunks.rs @@ -0,0 +1,122 @@ +//! X509 chunks handler where compressed chunks are decompressed and decoded. + +use std::io::Read; + +use minicbor::{decode, Decode, Decoder}; +use strum_macros::FromRepr; + +use super::rbac::Cip509RbacMetadata; +use crate::utils::decode_helper::{decode_array_len, decode_bytes, decode_helper}; + +/// Enum of compression algorithms used to compress chunks. +#[derive(FromRepr, Debug, PartialEq, Clone, Default)] +#[repr(u8)] +pub enum CompressionAlgorithm { + /// Raw data, no compression. + #[default] + Raw = 10, + /// Brotli compression. + Brotli = 11, + /// Zstd compression. + Zstd = 12, +} + +/// Struct of x509 chunks. +#[derive(Debug, PartialEq, Clone, Default)] +pub struct X509Chunks(pub Cip509RbacMetadata); + +#[allow(dead_code)] +impl X509Chunks { + /// Create new instance of `X509Chunks`. + fn new(chunk_data: Cip509RbacMetadata) -> Self { + Self(chunk_data) + } +} + +impl Decode<'_, ()> for X509Chunks { + fn decode(d: &mut Decoder, ctx: &mut ()) -> Result { + // Determine the algorithm + let algo: u8 = decode_helper(d, "algorithm in X509Chunks", ctx)?; + let algorithm = CompressionAlgorithm::from_repr(algo) + .ok_or(decode::Error::message("Invalid chunk data type"))?; + + // Decompress the data + let decompressed = decompress(d, &algorithm) + .map_err(|e| decode::Error::message(format!("Failed to decompress {e}")))?; + + // Decode the decompressed data. + let mut decoder = Decoder::new(&decompressed); + let chunk_data = Cip509RbacMetadata::decode(&mut decoder, &mut ()) + .map_err(|e| decode::Error::message(format!("Failed to decode {e}")))?; + + Ok(X509Chunks(chunk_data)) + } +} + +/// Decompress the data using the given algorithm. +fn decompress(d: &mut Decoder, algorithm: &CompressionAlgorithm) -> anyhow::Result> { + let chunk_len = decode_array_len(d, "decompression in X509Chunks")?; + // Vector containing the concatenated chunks + let mut concat_chunk = vec![]; + for _ in 0..chunk_len { + let chunk_data = decode_bytes(d, "decompression in X509Chunks")?; + concat_chunk.extend_from_slice(&chunk_data); + } + + let mut buffer = vec![]; + + match algorithm { + CompressionAlgorithm::Raw => { + buffer.extend_from_slice(concat_chunk.as_slice()); + }, + CompressionAlgorithm::Zstd => { + zstd::stream::copy_decode(concat_chunk.as_slice(), &mut buffer)?; + }, + CompressionAlgorithm::Brotli => { + let mut decoder = brotli::Decompressor::new(concat_chunk.as_slice(), 4096); + decoder + .read_to_end(&mut buffer) + .map_err(|_| anyhow::anyhow!("Failed to decompress using Brotli algorithm"))?; + }, + } + Ok(buffer) +} + +#[cfg(test)] +mod tests { + use super::*; + + // RAW data: 10 + const RAW: &str = "0a98195840a50a815904a8308204a43082038ca00302010202141fec832800975bc0ae18b21985e0a97418b5334e300d06092a864886f70d01010b0500307c310b300906035840550406130255533113301106035504080c0a43616c69666f726e69613116301406035504070c0d53616e204672616e636973636f31123010060355040a0c094d584079436f6d70616e7931153013060355040b0c0c4d794465706172746d656e743115301306035504030c0c6d79646f6d61696e2e636f6d301e170d32343038323558403132343132315a170d3235303832353132343132315a307c310b30090603550406130255533113301106035504080c0a43616c69666f726e696131163014060358405504070c0d53616e204672616e636973636f31123010060355040a0c094d79436f6d70616e7931153013060355040b0c0c4d794465706172746d656e7431153058401306035504030c0c6d79646f6d61696e2e636f6d30820122300d06092a864886f70d01010105000382010f003082010a0282010100ba58de3aa83b075a69ee9e584074fe8a0c06259f41a64628f4c17a55c36b736ca4c7d475f05b176a0ac8c11785d50efcd65dfb450a718debad42cb27f32134a0c4d10dbb6529b14dd6b0d20cc558406e31fb6d9fbe7c8bfd012180d9b495b56dd67dd582dff7bbeb77ee15fd83f7904e8b12ebf1db46e74deb264c1b844be9fcc0c9acbadaf2d9b7d927495b000a965840fe4c4fe82633d8cfbdf5fabe23e636a8026942a00d07ff0e8fd9284b1e210d44b396fdb874fd48733d9e18df67a957ef2f0ba6a6afc3d012a21a2d98604930a358404a66a90386aa68b8cca84962908e94676d3e59cf78cd383487d5f93a35fcec554b70c1f6a47dc02e6b7811d5eba9ba5afaf8295dd10203010001a382011c3082584001183081ca0603551d110481c23081bf820c6d79646f6d61696e2e636f6d82107777772e6d79646f6d61696e2e636f6d820b6578616d706c652e636f6d820f77584077772e6578616d706c652e636f6d867f7765622b63617264616e6f3a2f2f616464722f616464725f74657374317172346a723975757130686477677273797a6558403270647234766b363634376336397375796e6476367237357764333071776b6c70706d7a75326177326c376d676b7a78727a336378766d32307578687735703758407064346a383879707332307565766e300b0603551d0f0404030205e0301d0603551d250416301406082b0601050507030106082b06010505070302301d06035558401d0e04160414e9b7c7100d76bd43a9c1404443c443d4b739caaf300d06092a864886f70d01010b050003820101008a86e00103f9873787e4a1d592ec3ce07819584037f3d7e302b0df83526e7c8170cdd47828e8e7a8c27b8a249b53acbce9aa4bbd9b81034e231f994cf6f74c8f6c15d44918dfb46e6c17a82d0fdd8f97aade5c6058404f4125db3fea5dc1acaa5f4c185109c947a35621b350c22fd4fb24a3931255a49d07a2cda5d422cfbded702cb613bb48871bc9d520a38c75482bea7682a13f7c5840332f808af64dcb89c6e04c8189ddb46ebd01c387dad899688740bdce89d472743d7c1150e5f197d616413561bdf511fdde30494fac8b033a61265916965462905840090615236a89af4e7e1a46285a838d823177c0734ce4442522df2de6967899b08d32610967ff132ad2347db86d42148158e10248722b0278f30067890c470101584023456789ab1a6703a2a0f647010123456789ab0a5820b45b8295e2701d2dbc8d4093fae94b979735f8c5e17a1843cf64a298e86659a2820201038206785377655840622b63617264616e6f3a2f2f616464722f7374616b655f7465737431757165686b636b306c616a713867723238743975786e757667637172633630373078336b58403972383034387a3879356773737274766e584004441e56b190be1238456c493c5ac1adbcaad398ba28e82dd4d63b250ff3436fc3a2f2c9f243992102beefaec45840d750bf51f84025c7fb4b8431dc77a7c2d0a907181e81d9800558203b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da2918288250eb5840f47281cf3c0a42fcceda894625ec34508cb997d509e0937f945813355e26a700186481a5000001820a000250ebf47281cf3c0a42fcceda894625ec3403200a644454657374"; + // Brotli data: 11 + const BROTLI: &str = "0b9458401b0306006678baf9873001af26aafd1674e5b11bf549d4bea0a39bfa0324c494f7003a6b4c1c4bac378e322cb280230a4002f5b2754e863806f7e524afc9995658406829cb08ab014e580e707468318ac0086262ffa6d3039a557178a3c556cbee076b44163b02b2812e49d3ab27b5e7bb2e0c6b1321d042d1069a24b4884032468a58400a28c6c0b0af3474b476028a193051b0641d5d778162040c24d7d2d1cc925f3d504c8171e5b4757466bc5104a11147eeb110f17d6067ae4b0d04a1d4200a3590584042a59443fca0277bc8e0b053c8101ca338ac0fe93e702d048761686fe9bdf0d511e472c6bb85a27f033a24d745712b933dbe1c51141d6309d9cb4fa9c41f2acc5840995aa78f9877abf5fe4c55fd4ed2120cbfde107fcefdb363e0e2e35774f7357a6ec99ada7c55e70497f29bb3e860cbe05fd8b1e3e6f6193b3853ad6afcfef77d5840afa5ef4cff767e1fcb1e347afdf176f2f3acd76e99d65d192fff1c3eb37eefad4f3777dd744fab80b466fecbcc79e11670e3d281afbf0e3a3f0b5e8d30e217eb584092ffeb8ddef4c8b073d44ddc36f3ef6ed1df5461d4428bfbf43525effdb557aedc74ecb2d1522bdfd9b5696059fab43568cfdaa6dde757a7d58f8d4ca773a2cb58402ec92e8406f6aa7f8607fd795394c13ff26d79eb613f96cc50fd7acddef25f3f3cabae20280cc1cb70d806e0b005509e25a145b68604e551a03c844733c00db25840c212b5b5d3900818fa2673b0d1d32e6dacf77e8a1b755c5eb8bf7f1d8d864d8c9a0c706c8a000b64626162b10034d1a4744c28573452f9342c50c20a0e0e0c695840080e138ae55c9a24180b0992d2028040ca62f3f91c85985a27a5b243387496428629021a64120e1588654dd2207e089f16c80c0d95f3852d69947081f679dafa584004028a101f005b207025906daae14d828944320a0f0b31417a17114c5eee3a65a02b3990b0e6486c62c2f104d5aeb0b39b5844b5df2e0c0df43c80d19fbd21bd58404f96a827de443e9059867cbefe18d97cbf339fdba2e45f50c93c5e3c5f7db479c0655ec1fafd2fd7661c98a744b39dede7647efb9e39ca3655a559dcdfce659b5840aff6d5bf3b3a6bedbdcada9c38d7db31afaa8eac5f5b936991a779266559b1e3b6dca3feaadf2ecb268d8a962f202fbdb042e574e9c05bbecf4ee37da9bdd6675840d40ecb86c4a9deaf24f892989600ff8e816f59e7fa4f3ec854f6dfddce3d001febbd75634e536fec818bfd2a4c14d56298fbf4e3ac29b3b8a0ba035f0dffde0358406939eb07d1f03ab732b39985f5639a24536766ffa6ec36ab648ff2ce619c223d2ccc7c92e8ea74dff7d94cd99ccdc3d43a4dfa7f63afab81adbb39f126cad2875840482ae68dc83e43f47e9d1418764ea2f7afb3a2a34b177f43ee4cabd4617b053ee311dfd677ff70ece4af9719b36605fd38f15061917089b674f68b69654b71045840467192ac80318630c94ae56f2c163436b11a58805dc71484d2316aa8284c2ce38a25f40601d6100c42802c8015868582c05045a83c882e14622209b734969068584057bc65eca05168123b2db2fcc886fd6bafcddeebf1c2573515e1aaff3981776ce9a7339f12e6382207df6f3c7e3df750de8f58d753bf33ba2877a4ab8e5e5e435840b6b053deec20963a4430ddf75fdcb9dcb77ed9eacb5e3c5d6123d5546afb286165f8e1d4258365b73c2d3cf0dcd75f30e5a548adf83f176ff527bbbe09cc1dda5820334badf960b27d7aa97150b5db2ac882a65c014130ae05210b0075d0a2fd8c05"; + // Zstd data: 12 + const ZSTD: &str= "0c95584028b52ffd601605ad2800b44ca50a815904a8308204a43082038ca00302010202141fec832800975bc0ae18b21985e0a97418b5334e300d06092a864886f70d015840010b0500307c310b300906035504061302555331133011080c0a43616c69666f726e696131163014070c0d53616e204672616e636973636f311230100a0c094d584079436f6d70616e79311530130b0c0c4d794465706172746d656e74030c0c6d79646f6d61696e2e636f6d301e170d3234303832353132343132315a170d323582584001220105000382010f003082010a0282010100ba58de3aa83b075a69ee9e74fe8a0c06259f41a64628f4c17a55c36b736ca4c7d475f05b176a0ac8c11785d50e5840fcd65dfb450a718debad42cb27f32134a0c4d10dbb6529b14dd6b0d20cc56e31fb6d9fbe7c8bfd012180d9b495b56dd67dd582dff7bbeb77ee15fd83f7904e8b584012ebf1db46e74deb264c1b844be9fcc0c9acbadaf2d9b7d927495b000a96fe4c4fe82633d8cfbdf5fabe23e636a8026942a00d07ff0e8fd9284b1e210d44b3965840fdb874fd48733d9e18df67a957ef2f0ba6a6afc3d012a21a2d98604930a34a66a90386aa68b8cca84962908e94676d3e59cf78cd383487d5f93a35fcec554b705840c1f6a47dc02e6b7811d5eba9ba5afaf8295dd10203010001a382011c308201183081ca0603551d110481c23081bf8282107777772e0b6578616d706c65820f8658407f7765622b63617264616e6f3a2f2f616464725f74657374317172346a723975757130686477677273797a653270647234766b363634376336397375796e64765840367237357764333071776b6c70706d7a75326177326c376d676b7a78727a336378766d3230757868773570377064346a383879707332307565766e300b06035558401d0f0404030205e0301d2504082b06010505070301020e04160414e9b7c7100d76bd43a9c1404443c443d4b739caaf0b01008a86e00103f9873787e4a1d592ec58403ce0781937f3d7e302b0df83526e7c8170cdd47828e8e7a8c27b8a249b53acbce9aa4bbd9b81034e231f994cf6f74c8f6c15d44918dfb46e6c17a82d0fdd8f975840aade5c604f4125db3fea5dc1acaa5f4c185109c947a35621b350c22fd4fb24a3931255a49d07a2cda5d422cfbded702cb613bb48871bc9d520a38c75482bea76584082a13f7c332f808af64dcb89c6e04c8189ddb46ebd01c387dad899688740bdce89d472743d7c1150e5f197d616413561bdf511fdde30494fac8b033a61265916584096546290090615236a89af4e7e1a46285a838d823177c0734ce4442522df2de6967899b08d32610967ff132ad2347db86d42148158e10248722b0278f300678958400c47010123456789ab1a6703a2a0f60a5820b45b8295e2701d2dbc8d4093fae94b979735f8c5e17a1843cf64a298e86659a282020103820678537374616b657558407165686b636b306c616a713867723238743975786e757667637172633630373078336b3972383034387a3879356773737274766e584004441e56b190be12384558406c493c5ac1adbcaad398ba28e82dd4d63b250ff3436fc3a2f2c9f243992102beefaec4d750bf51f84025c7fb4b8431dc77a7c2d0a907181e81d9800558203b6a584027bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da2918288250ebf47281cf3c0a42fcceda894625ec34508cb997d509e0937f945813355e584026a700186481a5000001820a000203200a6454657374001900b8f93ca19a5286c0f46288547ec4a40117ab88e058a8215aea28148137cc3850eed1b425543405581f22883507dcb24ec794216e78e8254099f1d1f9d5cd0576ee8d73c102eb8c02"; + + #[test] + fn test_decode_x509_chunks_raw() { + let raw_bytes = hex::decode(RAW).unwrap(); + let mut decoder = Decoder::new(raw_bytes.as_slice()); + let x509_chunks = X509Chunks::decode(&mut decoder, &mut ()); + // Decode the decompressed data should success. + assert!(x509_chunks.is_ok()); + } + + #[test] + fn test_decode_x509_chunks_brotli() { + let brotli_bytes = hex::decode(BROTLI).unwrap(); + let mut decoder = Decoder::new(brotli_bytes.as_slice()); + let x509_chunks = X509Chunks::decode(&mut decoder, &mut ()); + // Decode the decompressed data should success. + assert!(x509_chunks.is_ok()); + } + + #[test] + fn test_decode_x509_chunks_zstd() { + let zstd_bytes = hex::decode(ZSTD).unwrap(); + let mut decoder = Decoder::new(zstd_bytes.as_slice()); + let x509_chunks = X509Chunks::decode(&mut decoder, &mut ()); + // Decode the decompressed data should success. + assert!(x509_chunks.is_ok()); + } +} diff --git a/rust/rbac-registration/src/cardano/mod.rs b/rust/rbac-registration/src/cardano/mod.rs new file mode 100644 index 00000000000..f8c6b996d89 --- /dev/null +++ b/rust/rbac-registration/src/cardano/mod.rs @@ -0,0 +1,4 @@ +//! Cardano module + +pub mod cip509; +pub mod transaction; diff --git a/rust/rbac-registration/src/cardano/transaction/mod.rs b/rust/rbac-registration/src/cardano/transaction/mod.rs new file mode 100644 index 00000000000..b5faf4ab48b --- /dev/null +++ b/rust/rbac-registration/src/cardano/transaction/mod.rs @@ -0,0 +1,4 @@ +//! Cardano transaction module + +pub(crate) mod raw_aux_data; +pub(crate) mod witness; diff --git a/rust/rbac-registration/src/cardano/transaction/raw_aux_data.rs b/rust/rbac-registration/src/cardano/transaction/raw_aux_data.rs new file mode 100644 index 00000000000..6117c039752 --- /dev/null +++ b/rust/rbac-registration/src/cardano/transaction/raw_aux_data.rs @@ -0,0 +1,273 @@ +//! Raw Auxiliary Data Decoding + +use std::sync::Arc; + +use anyhow::bail; +use dashmap::DashMap; +use minicbor::{data::Type, Decoder}; +use tracing::{error, warn}; + +/// What type of smart contract is this list. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, strum_macros::Display, Hash)] +#[allow(dead_code)] +pub enum SmartContractType { + /// Native smart contracts + Native, + /// Plutus smart contracts (with version number 1-x) + Plutus(u64), +} + +// We CAN NOT use the Pallas library metadata decoding because it does not preserve raw +// metadata values which are critical for performing operations like signature checks on +// data. So we have a bespoke metadata decoder here. +#[derive(Debug)] +#[allow(dead_code)] +pub(crate) struct RawAuxData { + /// Metadata: key = label, value = raw metadata bytes + metadata: DashMap>>, + /// Scripts: 1 = Native, 2 = Plutus V1, 3 = Plutus V2, 4 = Plutus V3 + scripts: DashMap>>>, +} + +impl RawAuxData { + /// Create a new `RawDecodedMetadata`. + #[allow(dead_code)] + pub(crate) fn new(aux_data: &[u8]) -> Self { + let mut raw_decoded_data = Self { + metadata: DashMap::new(), + scripts: DashMap::new(), + }; + + let mut decoder = Decoder::new(aux_data); + + match decoder.datatype() { + Ok(minicbor::data::Type::Map) => { + if let Err(error) = Self::decode_shelley_map(&mut raw_decoded_data, &mut decoder) { + error!("Failed to Deserialize Shelley Metadata: {error}: {aux_data:02x?}"); + } + }, + Ok(minicbor::data::Type::Array) => { + if let Err(error) = + Self::decode_shelley_ma_array(&mut raw_decoded_data, &mut decoder) + { + error!("Failed to Deserialize Shelley-MA Metadata: {error}: {aux_data:02x?}"); + } + }, + Ok(minicbor::data::Type::Tag) => { + if let Err(error) = + Self::decode_alonzo_plus_map(&mut raw_decoded_data, &mut decoder) + { + error!("Failed to Deserialize Alonzo+ Metadata: {error}: {aux_data:02x?}"); + } + }, + Ok(unexpected) => { + error!("Unexpected datatype for Aux data: {unexpected}: {aux_data:02x?}"); + }, + Err(error) => { + error!("Error decoding metadata: {error}: {aux_data:02x?}"); + }, + } + + raw_decoded_data + } + + /// Decode the Shelley map of metadata. + fn decode_shelley_map( + raw_decoded_data: &mut Self, decoder: &mut minicbor::Decoder, + ) -> anyhow::Result<()> { + let entries = match decoder.map() { + Ok(Some(entries)) => entries, + Ok(None) => { + // Sadly... Indefinite Maps are allowed in Cardano CBOR Encoding. + u64::MAX + }, + Err(error) => { + bail!("Error decoding metadata: {error}"); + }, + }; + + // debug!("Decoding shelley metadata map with {} entries", entries); + + let raw_metadata = decoder.input(); + + for _ in 0..entries { + let key = match decoder.u64() { + Ok(key) => key, + Err(error) => { + bail!("Error decoding metadata key: {error}"); + }, + }; + let value_start = decoder.position(); + if let Err(error) = decoder.skip() { + bail!("Error decoding metadata value: {error}"); + } + let value_end = decoder.position(); + let Some(value_slice) = raw_metadata.get(value_start..value_end) else { + bail!("Invalid metadata value found. Unable to extract raw value slice."); + }; + let value = value_slice.to_vec(); + + // debug!("Decoded metadata key: {key}, value: {value:?}"); + + let _unused = raw_decoded_data.metadata.insert(key, Arc::new(value)); + + // Look for End Sentinel IF its an indefinite MAP (which we know because entries is + // u64::MAX). + if entries == u64::MAX { + match decoder.datatype() { + Ok(Type::Break) => { + // Skip over the break token. + let _unused = decoder.skip(); + break; + }, + Ok(_) => (), // Not break, so do next loop, should be the next key. + Err(error) => { + bail!("Error checking indefinite metadata map end sentinel: {error}"); + }, + } + } + } + + Ok(()) + } + + /// Decode a Shelley-MA Auxiliary Data Array + fn decode_shelley_ma_array( + raw_decoded_data: &mut Self, decoder: &mut minicbor::Decoder, + ) -> anyhow::Result<()> { + match decoder.array() { + Ok(Some(entries)) => { + if entries != 2 { + bail!( + "Invalid number of entries in Metadata Array. Expected 2, found {entries}." + ); + } + }, + Ok(None) => { + bail!("Indefinite Array found decoding Metadata. Invalid."); + }, + Err(error) => { + bail!("Error decoding metadata: {error}"); + }, + }; + + // First entry is the metadata map, so just decode that now. + Self::decode_shelley_map(raw_decoded_data, decoder)?; + // Second entry is an array of native scripts. + Self::decode_script_array(raw_decoded_data, decoder, SmartContractType::Native)?; + + Ok(()) + } + + /// Decode a Shelley-MA Auxiliary Data Array + fn decode_alonzo_plus_map( + raw_decoded_data: &mut Self, decoder: &mut minicbor::Decoder, + ) -> anyhow::Result<()> { + match decoder.tag() { + Ok(tag) => { + if tag.as_u64() != 259 { + bail!("Invalid tag for alonzo+ aux data. Expected 259, found {tag}."); + } + }, + Err(error) => { + bail!("Error decoding tag for alonzo+ aux data: {error}"); + }, + } + + let entries = match decoder.map() { + Ok(Some(entries)) => entries, + Ok(None) => bail!("Indefinite Map found decoding Alonzo+ Metadata. Invalid."), + Err(error) => bail!("Error decoding Alonzo+ Metadata: {error}"), + }; + + // iterate the map + for _ in 0..entries { + let aux_type_key = match decoder.u64() { + Ok(key) => key, + Err(error) => { + bail!("Error decoding Alonzo+ Metadata Aux Data Type Key: {error}"); + }, + }; + + let contract_type = match aux_type_key { + 0 => { + if raw_decoded_data.metadata.is_empty() { + Self::decode_shelley_map(raw_decoded_data, decoder)?; + continue; + } + bail!("Multiple Alonzo+ Metadata entries found. Invalid."); + }, + 1 => SmartContractType::Native, + _ => { + if aux_type_key > 4 { + warn!( + "Auxiliary Type Key > 4 detected, assuming its a plutus script > V3." + ); + } + SmartContractType::Plutus(aux_type_key - 1) + }, + }; + + if raw_decoded_data.scripts.contains_key(&contract_type) { + bail!("Multiple Alonzo+ Scripts of type {contract_type} found. Invalid."); + } + + Self::decode_script_array(raw_decoded_data, decoder, contract_type)?; + } + Ok(()) + } + + /// Decode an array of smart contract scripts + fn decode_script_array( + raw_decoded_data: &mut Self, decoder: &mut minicbor::Decoder, + contract_type: SmartContractType, + ) -> anyhow::Result<()> { + let mut scripts: Vec> = Vec::new(); + + let entries = match decoder.array() { + Ok(Some(entries)) => entries, + Ok(None) => { + bail!("Indefinite Script Array found decoding Metadata. Invalid."); + }, + Err(error) => { + bail!("Error decoding metadata: {error}"); + }, + }; + + let raw_metadata = decoder.input(); + + for _entry in 0..entries { + if contract_type == SmartContractType::Native { + // Native Scripts are actually CBOR arrays, so capture their data as bytes for + // later processing. + let value_start = decoder.position(); + if let Err(error) = decoder.skip() { + bail!("Error decoding native script value: {error}"); + } + let value_end = decoder.position(); + let Some(value_slice) = raw_metadata.get(value_start..value_end) else { + bail!("Invalid metadata value found. Unable to extract native script slice."); + }; + scripts.push(value_slice.to_vec()); + } else { + let script = match decoder.bytes() { + Ok(script) => script, + Err(error) => bail!("Error decoding script data from metadata: {error}"), + }; + scripts.push(script.to_vec()); + } + } + + let _unused = raw_decoded_data + .scripts + .insert(contract_type, Arc::new(scripts)); + + Ok(()) + } + + /// Get Raw metadata for a given metadata label, if it exists. + #[allow(dead_code)] + pub(crate) fn get_metadata(&self, label: u64) -> Option>> { + self.metadata.get(&label).map(|v| v.value().clone()) + } +} diff --git a/rust/rbac-registration/src/cardano/transaction/witness.rs b/rust/rbac-registration/src/cardano/transaction/witness.rs new file mode 100644 index 00000000000..2ba2377583e --- /dev/null +++ b/rust/rbac-registration/src/cardano/transaction/witness.rs @@ -0,0 +1,123 @@ +//! Transaction Witness +use std::fmt::{Display, Formatter}; + +use dashmap::DashMap; +use pallas::{codec::utils::Bytes, ledger::traverse::MultiEraTx}; + +use crate::utils::hashing::blake2b_244; + +/// `WitnessMap` type of `DashMap` with +/// key as [u8; 28] = (`blake2b_244` hash of the public key) +/// value as `(Bytes, Vec) = (public key, tx index within the block)` +pub(crate) type WitnessMap = DashMap<[u8; 28], (Bytes, Vec)>; + +#[derive(Debug)] +/// `TxWitness` struct to store the witness data. +pub(crate) struct TxWitness(WitnessMap); + +impl TxWitness { + /// Create a new `TxWitness` from a list of `MultiEraTx`. + pub(crate) fn new(txs: &[MultiEraTx]) -> anyhow::Result { + let map: WitnessMap = DashMap::new(); + for (i, tx) in txs.iter().enumerate() { + match tx { + MultiEraTx::AlonzoCompatible(tx, _) => { + let witness_set = &tx.transaction_witness_set; + if let Some(vkey_witness_set) = witness_set.vkeywitness.clone() { + for vkey_witness in vkey_witness_set { + let vkey_hash = blake2b_244(&vkey_witness.vkey)?; + let tx_num = u16::try_from(i)?; + map.entry(vkey_hash) + .and_modify(|entry: &mut (_, Vec)| entry.1.push(tx_num)) + .or_insert((vkey_witness.vkey.clone(), vec![tx_num])); + } + }; + }, + MultiEraTx::Babbage(tx) => { + let witness_set = &tx.transaction_witness_set; + if let Some(vkey_witness_set) = witness_set.vkeywitness.clone() { + for vkey_witness in vkey_witness_set { + let vkey_hash = blake2b_244(&vkey_witness.vkey)?; + let tx_num = u16::try_from(i)?; + map.entry(vkey_hash) + .and_modify(|entry: &mut (_, Vec)| entry.1.push(tx_num)) + .or_insert((vkey_witness.vkey.clone(), vec![tx_num])); + } + } + }, + MultiEraTx::Conway(tx) => { + let witness_set = &tx.transaction_witness_set; + if let Some(vkey_witness_set) = &witness_set.vkeywitness.clone() { + for vkey_witness in vkey_witness_set { + let vkey_hash = blake2b_244(&vkey_witness.vkey)?; + let tx_num = u16::try_from(i)?; + map.entry(vkey_hash) + .and_modify(|entry: &mut (_, Vec)| entry.1.push(tx_num)) + .or_insert((vkey_witness.vkey.clone(), vec![tx_num])); + } + } + }, + _ => { + return Err(anyhow::anyhow!("Unsupported transaction type")); + }, + }; + } + Ok(Self(map)) + } + + /// Check whether the public key hash is in the given transaction number. + pub(crate) fn check_witness_in_tx(&self, vkey_hash: &[u8; 28], tx_num: u16) -> bool { + self.0 + .get(vkey_hash) + .map_or(false, |entry| entry.1.contains(&tx_num)) + } + + /// Get the actual address from the given public key hash. + #[allow(dead_code)] + pub(crate) fn get_witness_pk_addr(&self, vkey_hash: &[u8; 28]) -> Option { + self.0.get(vkey_hash).map(|entry| entry.0.clone()) + } +} + +impl Display for TxWitness { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + for data in &self.0 { + let vkey_hash = hex::encode(data.key()); + let vkey: Vec = data.0.clone().into(); + let vkey_encoded = hex::encode(&vkey); + writeln!( + f, + "Key Hash: {}, PublicKey: {}, Tx: {:?}", + vkey_hash, vkey_encoded, data.1 + )?; + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + + use super::*; + fn conway() -> Vec { + hex::decode(include_str!("../../test_data/cardano/conway_1.block")) + .expect("Failed to decode hex block.") + } + + #[test] + fn tx_witness() { + let conway = conway(); + let conway_block = pallas::ledger::traverse::MultiEraBlock::decode(&conway) + .expect("Failed to decode MultiEraBlock"); + let txs_conway = conway_block.txs(); + let tx_witness_conway = TxWitness::new(&txs_conway).expect("Failed to create TxWitness"); + let vkey1_hash: [u8; 28] = + hex::decode("f8e2356e21af3563556c167c31bcc00f9fc061a8a852a16a42ffcc79") + .expect("Failed to decode vkey1_hash") + .try_into() + .expect("Invalid length of vkey1_hash"); + println!("{tx_witness_conway}"); + assert!(tx_witness_conway.get_witness_pk_addr(&vkey1_hash).is_some()); + assert!(tx_witness_conway.check_witness_in_tx(&vkey1_hash, 0)); + } +} diff --git a/rust/rbac-registration/src/lib.rs b/rust/rbac-registration/src/lib.rs new file mode 100644 index 00000000000..c32ccedd42f --- /dev/null +++ b/rust/rbac-registration/src/lib.rs @@ -0,0 +1,4 @@ +//! This crate provides functionalities for RBAC registration. + +pub mod cardano; +pub mod utils; diff --git a/rust/rbac-registration/src/test_data/cardano/conway_1.block b/rust/rbac-registration/src/test_data/cardano/conway_1.block new file mode 100644 index 00000000000..ee2341dbf79 --- /dev/null +++ b/rust/rbac-registration/src/test_data/cardano/conway_1.block @@ -0,0 +1 @@ +820785828a1a002a250b1a0453dd635820de837235735da44fef4d3dd7c810e36df43a56741f36488d43c7724844bdb93a5820087932a594533b96a2abd339715dd222835d36856ac2c0b8fcd171e3ec944b8e5820b5b14e8eb25a9366e0c1a43afcd8d0859249202f3a8dc03b13f416c70d4de7d78258408f9c0c36c36241dc497cf218bbaa28569b20da43d0dde22c42a06c7dbf9086f67a2724d3743749893edb73027bffb9382b3534e48e0210c50755b7e1c53faf885850a981acb25e4fe8018eb69799e8625a7b415d3acf8c7935a32b010018f29415ad7a0f48738dda9bb34fbaff868f777670e7b7b512b3fbe5ba3878ff16adda7f5a12c7f45f4f9a8c20cf29867534c6830619156858202de40ebb5bb7974675746077e043354f77856687144dcbeba15b5b039bc757438458208cc744c58c7a05709e02beb11b63c311687a1c1308b11e483d1c728f4f6fbf7d041901fd5840ba3364505403cd1f409595472bbf1e9e020fa331b39cf9c8a9652841ad6fe609de243d2368861351e83c685eff105f8b7bb7389d11c898cb94876202803115008209015901c080e7950464897eace2060d54656948ba519f9ce3e78eb93b29798f77940c49d17b310966cc580cd96162aa138d71c119cd8e76118daa8c3d32bf714fa6d8a409b5a92156fd374d4d912307b8652f38df7faf5608febf3599bca862c578ef7b68e1dfffb9be2a836067727899337e2be78b79913b35f7097ffb20a0972c0c5aee723debf8f922e55b0f93a194c9e8cdb21a1bcffa1ab6341e1d8cbff21c1f4d60d59a5bfc24176fc987241cfddd27e0882c3f8dc958a2a181829fcce9415554a96b5f62d71578479dd496ab0feddd385e3bfaa86229bacd1977799b7d4039f918659c7a8d73c6168d9708a4a6c10060dd58907423f0da8e79bc1024c7942fa9a20144540af7e596892aeb3057cab0ad6d2fc676d90bb4eca2bc42c84dadb719ddad18f036f0563f29eb67d7fee36b700117a243dae2732ecaafc1629cdcfb0a80c94d4f89321969289f0852d693c0b899f8f698465d7cff645879cb54c50eaa24d44fc48a79066c73c54d9438145136d161f1efdaefa9a544d3e3c35244ed941f968e917cc68a627df194e19d08bfc18482638deb4f2da42f7096d2cbc7e8b4f472f1027f53cca668252e30946f6e9ca4c3e224cd40f764ea59fe85d420b0bbe785a90082825820a8ee092381312ff15f5af09c8de5d9f75e661982873a92bf14b80c7a64d10c6400825820a8ee092381312ff15f5af09c8de5d9f75e661982873a92bf14b80c7a64d10c64010d81825820a8ee092381312ff15f5af09c8de5d9f75e661982873a92bf14b80c7a64d10c64011281825820db62a0577b70a84aadf81883f9314e5ed3f01a79429010c81875d719dcb3eb05010182a300581d7055f73a4556031cccd599cac389b4a98457c44cef77ef8647639ad5fe01821a001b9f18a1581c0c64e02d195a9a4a86a3e080ccc643cd79223b7fc45676d07cf3a802a14d446a65644f7261636c654e465401028201d81858a6d8799f5840d4d88111d26121908711b6c375d659e0f9bb1bc320912715324644f802fb01f0b9ef630c309aa38e26c779eadb66abe906d7755645ebbc73d23ed23d03b59407d8799fd8799f1a0007a1201a0002c5a3ffd8799fd8799fd87a9f1b0000019266006960ffd87a80ffd8799fd87a9f1b00000192660e2500ffd87a80ffff43555344ff581c0c64e02d195a9a4a86a3e080ccc643cd79223b7fc45676d07cf3a802ff82583900f8e2356e21af3563556c167c31bcc00f9fc061a8a852a16a42ffcc7927ce10b87147e3e19a5dc622b6a535ab9ce01cb2d586a3a690a74c0e1a8acb1ea0021a0003fa8a031a0453e0a0081a0453dd1c0e81581cf8e2356e21af3563556c167c31bcc00f9fc061a8a852a16a42ffcc790b5820d342f73527d192f78a35b8b6517964bdab319c68ad9b15ae024d323656675848a90082825820551cc8d750767bb04fec1cefed55b2ba230d769d3d42907de8dd4e2fdca901ec00825820551cc8d750767bb04fec1cefed55b2ba230d769d3d42907de8dd4e2fdca901ec010d81825820551cc8d750767bb04fec1cefed55b2ba230d769d3d42907de8dd4e2fdca901ec0112818258205de304d9c8884dd62ad8535d529e3d8fd5f212cc3c0c39410e4f3bccfca6e46b010182a300581d7039a4c3afe97b4c2d3385fefd5206d1865c74786b7ce955ebb6532e7a01821a001b9f18a1581cdab1406f1c769fbdb00514c494eed47a54c7ffc5d7aafc524cca069aa14d446a65644f7261636c654e465401028201d81858a6d8799f5840908c7e221021da9411c2951209f58ec0e5b00e2c6dbb5e64476ab9e185afd51b9c929893d36b0a8f727103fb31f981505483fbc4d3a9e95bc080362862fc5001d8799fd8799f1a000f42401a00058b45ffd8799fd8799fd87a9f1b0000019266006960ffd87a80ffd8799fd87a9f1b00000192660e2500ffd87a80ffff43555344ff581cdab1406f1c769fbdb00514c494eed47a54c7ffc5d7aafc524cca069aff82583900f0a26fc170ad82b64a3d43dede08e78ea6e2028b5101058d0263a80a4c0ec23eba3aa27a4b8d61107f59f3e0d24b7bb6d7ae1a4bbd689c8d1ab75f0348021a0003fa8a031a0453e0a0081a0453dd1c0e81581cf0a26fc170ad82b64a3d43dede08e78ea6e2028b5101058d0263a80a0b5820d342f73527d192f78a35b8b6517964bdab319c68ad9b15ae024d323656675848ab0084825820157dcc88aa4a0278d4eebd6764fdd3a7f7d53716d298d5366cb7fff5b124553901825820d8c2aa33753209ba095bb811a8cfe92a32539bdfae5bec771eca3ecd996d269100825820d8c2aa33753209ba095bb811a8cfe92a32539bdfae5bec771eca3ecd996d269101825820d8c2aa33753209ba095bb811a8cfe92a32539bdfae5bec771eca3ecd996d2691020184a300581d703f89c436844cd0acd2714201817836b2cd16b082e03e3a21bb69e5ad01821a002dc6c0a2581c2b556df9f37c04ef31b8f7f581c4e48174adcf5041e8e52497d81556a148416767537461746501581cc6f192a236596e2bbaac5900d67e9700dec7c77d9da626c98e0ab2aca14c5061796d656e74546f6b656e1a3890bbd1028201d81858eed87b9fd8799fd8799f9f581c59184749e2d67a7ea2ca31ef48fc0befb3c3fad5a88af7264531ae07581c1be17c0feca072beb5a395a286ca4bf269f74f33db80509337ff3dbd581c564b2400e0901e336538bea2eb48f0bac8aa77b0c3a948f07cd15ff0581cbd05c7354d160113edb39a32a2ce6045f48f51d5728ffafdae2decef581cbdebe65210d74725fffa6635f28ddeae6cbcd1a8aa6898ab75cfd5e3ff1917701a001b77401a001b774018c81927101a000493e0d8799f186418c81864ff021905dcd8799f9f581c1a550d5f572584e1add125b5712f709ac3b9828ad86581a4759022baff01ffffffffa300581d703f89c436844cd0acd2714201817836b2cd16b082e03e3a21bb69e5ad01821a001e8480a1581c2b556df9f37c04ef31b8f7f581c4e48174adcf5041e8e52497d81556a14a4f7261636c654665656401028201d8185823d8799fd87b9fa3001a0005c7a4011b000001926600b8ef021b00000192661c302fffffa300581d703f89c436844cd0acd2714201817836b2cd16b082e03e3a21bb69e5ad01821a002dc6c0a2581c2b556df9f37c04ef31b8f7f581c4e48174adcf5041e8e52497d81556a14652657761726401581cc6f192a236596e2bbaac5900d67e9700dec7c77d9da626c98e0ab2aca14c5061796d656e74546f6b656e1a02a74bc3028201d81858d2d87c9fd8799f9fd8799f581c59184749e2d67a7ea2ca31ef48fc0befb3c3fad5a88af7264531ae071a00838e05ffd8799f581c1be17c0feca072beb5a395a286ca4bf269f74f33db80509337ff3dbd1a00822d24ffd8799f581c564b2400e0901e336538bea2eb48f0bac8aa77b0c3a948f07cd15ff01a0081fd52ffd8799f581cbd05c7354d160113edb39a32a2ce6045f48f51d5728ffafdae2decef1a00a66f7cffd8799f581cbdebe65210d74725fffa6635f28ddeae6cbcd1a8aa6898ab75cfd5e31a0014151cffff1a00650eb0ffff82581d6059184749e2d67a7ea2ca31ef48fc0befb3c3fad5a88af7264531ae071a006e2d88021a001b26b8031a0453dd94081a0453dd1c0b5820d04e736b347a342ad321aab49d0a731d1fe97b8cadc5cfad8d0954ffd826a22f0d81825820157dcc88aa4a0278d4eebd6764fdd3a7f7d53716d298d5366cb7fff5b1245539010e81581c59184749e2d67a7ea2ca31ef48fc0befb3c3fad5a88af7264531ae071082581d6059184749e2d67a7ea2ca31ef48fc0befb3c3fad5a88af7264531ae071a00524801111a00370c3f128782582056859125fa6486f85015233788e398546004743bd6af30692e51ce7d464cc9b100825820c9a9d5b5b100c69c3eafa6cf5b9d48c8b49251bd5cf7f78496f4b90b852fbd2d008258204d3550e32ba844e8e11df2bdca827cf11eaad9f35aaee2b925365de452ceda4900825820157dcc88aa4a0278d4eebd6764fdd3a7f7d53716d298d5366cb7fff5b1245539008258201a28375b01a8928f21a87598e24165ab5806e7c3e77a5eb4d78c56e3cf07894c00825820a9baf90b688b11a30dfb0c29b8cdfda8447476a5a87dff05b451aa0d6c40be6600825820991b8a8b8f3d2423d5c7bba99b7698a7a4d68fc62681b4d8a52ff7ca2822edf801a600818258206fcd3774965658d24f131cea65bca5230b8014f0022652a978fbc4b1fffcd831000181a200583900eb21979c03eed7207020b2a0b47565b5aafb1a2c3849b59a1fa8e6c5e075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d6473903011b000000024fd8978e021a0003997d0758209d9ab1f617ed24eda3f6acc4b6cb4dfc0792ef85da7cda5123d9809e3d16101b0e81581ce075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d64739030f00ab00838258205a3450aa523f773eb21e3a047c61aea074d74396ca65f216bd358840d7babb11018258205c03aab3f3c954d6ffcdda26f2a84cca50170484773b15ba0c6b26f1f34dcfeb00825820862cf9c5c614b27d95f2893c77f8cba673b273d3be714401cbf1eb3fc0079bd7020183a300581d70a74ce540e819f246e6f65f3b4597daaaf9d37c0443fbcb13618a10c901821a001e8480a1581c1116903479e7320b8e4592207aaebf627898267fcd80e2d9646cbf07a1484e6f64654665656401028201d818583cd87a9fd8799f581cc91e84382cbd1f0b5e407232743d80894e7aae5bec7c835334841e72d8799fd8799f1a00058ac01b0000019266010bc2ffffffff82581d60c91e84382cbd1f0b5e407232743d80894e7aae5bec7c835334841e721a0089544082581d60c91e84382cbd1f0b5e407232743d80894e7aae5bec7c835334841e721a00a6ab3d021a0009ce9c031a0453dd94081a0453dd1c0b582091b0d6dba1d88661433fb0ed0097faa20ddc563980b6b66039fdb211cc8a30ee0d818258205c03aab3f3c954d6ffcdda26f2a84cca50170484773b15ba0c6b26f1f34dcfeb020e81581cc91e84382cbd1f0b5e407232743d80894e7aae5bec7c835334841e721082581d60c91e84382cbd1f0b5e407232743d80894e7aae5bec7c835334841e721a0079705c111a00370c3f1281825820b31179a89f31d9b08bc0f075a4661c72a31c2270eb27ce42f9d4170180118def0085a20081825820c58b6e67ad72d3e4de04c0c143b15e0e60573ea9b740cf0a8fbfafb0475d50fd584076b53453d37a56ba653f93a741805181973fb19a62e6668481e82a82296cbaf539cdc2f15d2678499abe776cccb27bdec9a0937c63f6cf3846c611aa2287de040581840000d87980821a000669651a0a708998a200818258205401b7f67442e6cc870fbcffe921d24ab03e97e03bb655a24b4f424fd832c615584023858e80006b6c668029a3de035a1e993f887f1a525ece7c3fc27491cff587655271f3ed74f9fb240aa0643091cc2ac6caccc7cdf9ef66bbbd732213409e480f0581840000d87980821a000669651a0a708998a2008182582047075e86572a64490e20b0d020f6e7a4cfa74c591ac057ec59839d30c64471745840d0bede6b68fd1dbe68b61eeb6d6c13b6f0dd36cc56ef7becd3c6e9349fa10c4ff5d6c96595b99373cb067a744f297f5cb2b1bea456e32d3c37396e43360d0a0505a382000182d87c80821a009f8e921a7f561c3082000282d87c80821a00091aa91a08e642dc82000382d87c80821a000bfb2c1a0b89b757a100828258208469288efa6f9cb49040b43dcff93f40969d72433f751afde50235012c016020584038522b52e1abe30dd9d587abbee42ffe62aaa3a7f99cdbb07a9c3e9b73a365252ac2cf78aa26cb117802ac6d7fa34f716060d526f3eabb9979e58e3b4de1dd0782582076af5530fa318a370820270031d1838545a4ceed8696510627563d1114d4182b584007230be41958bfe1a41c9de16f804f1f82cb6ed1de7947b07eb4683bd94204c07f2b02ff9c8b74a5420a664c56764d4983154ec93b946778707882b839aca805a200818258206b7e1b2d051ed3f4651688ffb184e6d0b415b0b3025ef63c57d231404a73564458403e7e6eb19d2377c2603e1df5f5ca96bf9443203547d60ab19494ece629785fac0dc7c4786fc1f7b8248246a356e8a867cb23fbce48d1984c0187335ba1fb7e0a0581840001d87980821a0012c8941a106a99c6a103a11901fda50050ca7a1457ef9f4c7f9c747f8c4a4cfa6c0150226d126819472b7afad7d0b8c7b89aa20258204d3f576f26db29139981a69443c2325daa812cc353a31b5a4db794a5bcbb06c20b9458401b03060066006fd5b67002167882eac0b5f2b11da40788a39bfa0324c494f7003a6b4c1c4bac378e322cb280230a4002f5b2754e863806f7e524afc99996aa28584032f02b600cbf04c6a09e05100880a09ee59b6627dc78d68175469b8c5b1fac141a6da5c6c2ea446597b6f0b6efea00a04ac0c1756455589908a5e089ba604a1258405917d6ee2b2535959d806c00eb2958929ababb40d681b5245751538e915d3d90f561ddcaa9aaa9cd78a30882a22a99c742c4f7610b43750a0d6651e8640a8d4c58402167427cfa933d6430c026640888210cd0c4e93e7015100300dcaef47b9c155ea4ccb27773c27f5d6a44fbf98065a14e5f0eca530e57082a971cbf22fa9065585840ae72e2a061eb558d3fd7727e87a8f07b5faf0d3cedf8d99ab6e0c845f5dd3ce78d31d7365c523b5a4dfe5d35bfafaefb2f60dd7473cbe8d6aa6bf557b1fbdf775840bf96bcd3ffdbfc7d20b65be7f5c7dba1cf635e3b449bb644bdfc73e0e49a5db73edddc7ed331220ba732f62f3aee8503f5c6f9bd5f7fedb37dc6580196052e50584027fdd7e8bfe9146561ad1ebc79ecef0ee1df7081cf9cd1fd929569ef3d55972d5b7ff882ce2213f789fc08787164f14aa86d55e98e332b220a07fa464aaa7c335840ce4bcfb268ed577f72e87fdea4442107bf2da93fe05121d5befa7ae5aecc5f3f9c732e82108003166380198c0146b0e214114a31d7c62b0ec18afd5834034c2b58402b2c515b350d8980a16932071b6d8d125ea1eb53dc28a8aee1787a1670b9e8c4c8cb00c726f3515a39ca1689f870295752820a64721e05e1a234710583416316584031d80291ac9a2b66a04cba844b85f9928a4a04a9928b2805124a25b3aaa4422e45e5d422a9b88a028ba4a5123ac244b8b472164b86085ac21357c3aae7696be25840f1104878009b03813d9e6c53255722402090206058a009d2b808aff772fb712d75f1dea09507fd83838e045dd9ce3eb59e4554f5ed02b8aeb60700f4b39dd9fe584064e1d5a137de0fa4c6cccfd71f831bee372756d72990b357a44e2f9eaf3854db65379db466cfcb55517ba71550acade564f4b7efd1fd95fa57228cee6fa9ae3458405ce1ae79b77f7cd5bdecfcb800fbdb7eaf720eae5995176d94a07c326c71aaf5e6f8439e577edb2d1ed64959324b5a7476e9159bf37bdf226edb747787b79b9e5840bc6ab5b84714eefa4a8c2df4aba37a36757d8b39dd79ec41b4a2f3ee96eabdc0e1f65b37264bdbfdf79eebbc820a7deab4e39f7e1cbf6610402fd8fb55fbef3d584038226e4d37c42970c830184b2e1c5026eadb9677ae8f6d300975ca6ceec5c8920382e827c1f636f7dd9f8d492737f4520a944bfeebba5ca2d5efa80ad453a43f584004c357ecccfc4dab75ce560b0300db9092ced52625d0c8df6fc89da9a45b6dc9c2461f21e6ee7b7afd877fbd8c1a1fa7ff38fa506e14749ebb68e24571c6220c584004208c284d628c2148b252f91b8b50014b080b040554095b52ca862bb974218222d412112ae5d2584c54584ae157f22b183cb4ba9c5fc42ba6894ad074ffe0875840c69ee921211d0ce4cd0f89b7e708163b3ab9286fe26a8c68ed85930cabc5dbfed7f9681c535dbdbfeb56f7a2b32d1f43de1dbcc934676edefacb3df7c1210067584064a1b8d94448b7f22a77dc736edb12f7c2c52b2eb8d4a80b78147d89f9a3a0659c03e10bbb336e391b3961f1afbfa08af3de2a817fceddea0cb57f438b0f8947581e9782ee92e890df65636d835d2d465cc5521c0ec05470e002800015eecf5818635840e0427f23196c17cf13f030595335343030c11d914bc7a84b56af7040930af4110fd4ca29b0bc0e83789adb8668ea2ef28c1dd10dc1fd35ea6ae8c06ee769540d80 \ No newline at end of file diff --git a/rust/rbac-registration/src/test_data/cardano/conway_2.block b/rust/rbac-registration/src/test_data/cardano/conway_2.block new file mode 100644 index 00000000000..d38c51b885e --- /dev/null +++ b/rust/rbac-registration/src/test_data/cardano/conway_2.block @@ -0,0 +1 @@ +820785828a1a002a259a1a0453ede35820b2acff53e862876989980fe9cac80548c2c3ed417951324c54d69b4d86daf5da58202f5ad4772c00767b6d75c8c1fae31e3479d91921abf41b1989346d2896b61de258201003b81af09c4acb552e0fbb33ffe12415360f2842c0e020b11b09cb82f7c5e48258406db3ca895414ace8710f345f8e93536ef2f24d7d6d2fb29e50a8844c83fe2eb5f64aa72968d70d681f9b01f4fe7332ce675a6a809448bd6b95c1f5cb2c86011c58503bb668495b649f05239672b7b8eecf33bec1cb060d1bdf5a1676fdd4b03d4218e6e6e41c87ff6f22759ccf4da65caffff14c1099ea7d4e2b56f76de67f33d80e72212e7a709db122a7ca33ea53690c021914ae58206fc671bd90b0d6955b8f48e9f8195291ffaedadd191d77884adc695edadb4853845820873bdd3584228477e9d9ee6b60a9cd6000627acf154126162178860ba752e9cd0619020d5840e461305c25b0690c1001e96f882d51c3a12ca32bae4cd25f9f46a98a0e9a7fe9b43ed108f230393c474444ce5ea88cc1bd299b94e7a60795d883e0a16004c0068209015901c06bb09e2dc5ca2e8e8b4124323489a64dcfacb9f80db18c6ef99f74a700d033b44fa89ebd120443c94312a92b9cf6e59c49541649c9e2e1d51d3ea16679d9f4078efa53d0f18fde0a05b87670b6f58d8d1e749f02f193d4d6ce790c21ae001f1550d19b5a842060a4d5d0120e311886a172b74585f7dac0ed59d1ec45f66ed0c760458b7616e67b0df1b5539d0eaea60f0230b5c9e50097c918559a85d57111b13b230b6f222b03cf501ad53911235154a7f0dd6970e7a3a16036d351b4aaba08cf2dff5f669ab4795ae5484935ba59e01116e16dc84b1eed1a4613ebc17d3f8ddda8113b492fcfbc85edeee2191051abff6a6f91d4295964764e8696623ac78ee14e248a3bdda80e04c28e0024f3922993bd4642d92de6c907ed6f0f9371d8d7a7c4c45091f33b1389b0aeedf05f33c9355196326f06b6264c3504d60c2fc790044f7d3ecfbd9dbe013ea5dbd3c2b7d572dab25512dad0dae1cb96a49a0d3b1b6f5c6f71d4118b6b032db7096e4044d6fb78861cfd0187475ae7720782a881555e93cb843f7db6a80ae06161a8d373325fd19a0efcf6bc4c76635513353213c1d9d07e1fa563adabd17558a2ff1fece92c75a0957b3592ac2816f4fd9ad8773d87a500d9010282825820020363f4cfc9f3a1b7594e064e74f5d9de740e25b246de6b01d4f18c931c99b318198258204567c47047668e8f0bd8f536bbca83b1de2fefa9148fb25c45d08859e29122a00001828258390069241e2a942d7b68cb729426261c1b0826982443a6b0702a7665e98979e7d3f2262b8b84c28cd004f11c89d98bd515ee0af810b9c369034e1a00596d488258390019c0438fe64e704bb98508e75b156fddc62d6e579c77d86ab67ca5af79e7d3f2262b8b84c28cd004f11c89d98bd515ee0af810b9c369034e1a007974e7021a0002bd51031a0454098f075820e84a468c26df3ac2f928642aeb3b85bf2609ff7f48888cb37b5a7d7edfd0ad8ca500d901028182582098069e04919bc65917e47a4e9027e0f65cd56ecc9779eccb0a9cc29fe17e171f182201828258390069241e2a942d7b68cb729426261c1b0826982443a6b0702a7665e98979e7d3f2262b8b84c28cd004f11c89d98bd515ee0af810b9c369034e1a00596d4882583900b1d55f9f64c24cfcc88aa5b49db76ea43386b35b7f2e80966bf37ace79e7d3f2262b8b84c28cd004f11c89d98bd515ee0af810b9c369034e1a002d4133021a0002a5c5031a04540993075820fc22a7f998d4a138fbca3bc375f6f2ec040152b965369c8997b90d3439b6c9b6a900d9010283825820b3458ce9b7e9f45d5a6c34ed20105fa899fcd5b8fe19d82a7468c88cdc64fc1504825820ca98a9e90895b7e8af44e48ea7b0c3ed837c5ed0c0802bc3f5bb4c35543351a001825820ca98a9e90895b7e8af44e48ea7b0c3ed837c5ed0c0802bc3f5bb4c35543351a00501828358391044376a5f63342097a4f20401088c62da272639e60644a9ec1d70f444358a4e4105c08f59e4779070699c7a72566893332f9857db4e742beb821a005d1420a3581c53827a77e4ed3d5c211706708c0aa9b9a3be19db901b1cbf7fa515b8a15820c76aa2e728cac2a3b9f61f88f0fdc6c603c367696596fc1bb6746983bb9f135701581cc6e65ba7878b2f8ea0ad39287d3e2fd256dc5c4160fc19bdf4c4d87ea1457447454e531a02a8ec66581cf03eca9ebdd30db76c5d5c0b450c97bbb4faccbc67cbf683e483197ca145745553444d1a03db7e7258204e86ed215442ac3225e8b68e43d4942be391c1c165e224ea1cdcaaaac3d070c78258390078886065c99d810efddc589ffb760821e57314fd3760263038a12096f3213e19402ff9bd59d0c506a727c34ae4b12ad5eb9c9706def94db8821a35a33db7a2581cc6e65ba7878b2f8ea0ad39287d3e2fd256dc5c4160fc19bdf4c4d87ea1457447454e531a00d74746581cf03eca9ebdd30db76c5d5c0b450c97bbb4faccbc67cbf683e483197ca145745553444d1a1cbd7691021a000615140758209f7a0691b5cd8c61203de22560c3290776c33d6f4de92717d6ef9a813d9228620b582057dd2ba2828bbe242b69bdd76b11474b997107d19691d9f518b1be9954fe7d210dd9010281825820f9e68746ebb05beba703c12fe31c97a9605196a5bcbf470b4d66210bb4122aac04108258390078886065c99d810efddc589ffb760821e57314fd3760263038a12096f3213e19402ff9bd59d0c506a727c34ae4b12ad5eb9c9706def94db81a00432ba2111a00091f9e12d901028282582016647d6365020555d905d6e0edcf08b90a567886f875b40b3d7cec1c704826240082582016647d6365020555d905d6e0edcf08b90a567886f875b40b3d7cec1c7048262402a60081825820e33e183249d63e516c7f5ec8b41c89d4fc9d24b2626c441407a6e78d0784e95d000181a200583900eb21979c03eed7207020b2a0b47565b5aafb1a2c3849b59a1fa8e6c5e075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d6473903011b000000024fcdca3b021a000399d5075820377701c2f4f80a44c251a3bc8e5e12244cbeffa5c26d9a959a93642916d2bf890e81581ce075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d64739030f00a300d9010281825820beb5f158de66dda9a234e887c27eb6aa0961afb9d142445bb30a05f0a0604bec183d01818258393040fc34adeda87d8eb4c7f3bf1b811b95fa835d356aa129a0089822a4d448965f62c0afafd791b02435b2dca9f6215df84d3c30a5ae35da261b00000002540be400021a00030d40a500d9010281825820fe63bacd187869d23260b844e7c6ffab508b411786156c0f0265ecc99593ff81184201828258390069241e2a942d7b68cb729426261c1b0826982443a6b0702a7665e98979e7d3f2262b8b84c28cd004f11c89d98bd515ee0af810b9c369034e1a00596d4882583900dbf48c2c653ad449fcb447670692bad9d9e28689f59d2f5aff908f4d79e7d3f2262b8b84c28cd004f11c89d98bd515ee0af810b9c369034e1a0091e8d7021a0002a5c5031a045409ff07582065e215890ab4d0ed1e1c1386d3d4b56fa1db85cabed224fcf4da3fe90b8d5f9da300d9010281825820beb5f158de66dda9a234e887c27eb6aa0961afb9d142445bb30a05f0a0604bec183e0181825839007eb505be585918dea709480a7b3ff664c72b77f28e8337e7c92d71e16de8be2814a24b0f2bd8918faf3d07685078b94e14303d5333ff4cb11b00000002540be400021a00030d4087a100d9010282825820081397fd90c2cd717f732e85b9df6a60f258b468e633583b6e847f27c6238fcc584067d605eed13606c8298ea57176e0396af8a601e4321866a59c6b970c16ab777faf192a20471278d7e118704a0d13d7d1449b5b9082bdad0b8c6a343a6cbdfc0082582079d1ab518df51b5584ab6710e9b06f72d7d292008252aaf20b1f00821ba392655840f3e981bec4eee7d8d9afa2c9d662b4071f46db5a8c9384a63a766a8acb355cc60faae6b50b2ab64772ef65cc756c40b16174368fde166ec1fe08ac240bc21d0ba100d901028182582079d1ab518df51b5584ab6710e9b06f72d7d292008252aaf20b1f00821ba392655840fbe829ffb25dd94bbed898eb5c6f7baec81731641073c864c516348ea9bc98e1b60e1d33859df31291c11bd1c0bfff5d79988681657117c6f9651fa97c72600da300d901028282582025333b67631e94b6c5cf329fd664eb8952d9521901863b07930892b5b1b793c65840d216f8ff58a379a5671bd741b0791682a5729426d41dd1feec26ea3dd1e739fa9e9c53ef7542fdd74c2da7e1e4fda98180b82e4b6a4909bb09ba70b7c243a307825820e8ad851eecf7d7af03934a076dde98241f081b7dd2a482309018fcbb24404ec5584093cd8d4fffe095596f008e5a5aa301a59768ad3a799cba3b2e2724e8bbc3ec9617b4c90e393555792a7ea85616840f5a3f38d3148cb025ec9a343e7afd1c6f0c04d9010282d8799f581c25195af85c41b9d97da7f4f215d3e74c9cef7f04739d6ba473ba72a2d8799fd8799f581c25195af85c41b9d97da7f4f215d3e74c9cef7f04739d6ba473ba72a2ffd8799fd8799fd8799f581c358a4e4105c08f59e4779070699c7a72566893332f9857db4e742bebffffffffd8799f581cc6e65ba7878b2f8ea0ad39287d3e2fd256dc5c4160fc19bdf4c4d87e457447454e53ff1a077359401a02a3338ed8799f581cf03eca9ebdd30db76c5d5c0b450c97bbb4faccbc67cbf683e483197c45745553444dffd8799f0405ff5820c76aa2e728cac2a3b9f61f88f0fdc6c603c367696596fc1bb6746983bb9f1357d87a80d87a80041a000f42401a000f4240d8799f1a003d09001a0005b8d81a0001c6b0ff1a03d9b7c2ffd8799f581c25195af85c41b9d97da7f4f215d3e74c9cef7f04739d6ba473ba72a2d8799fd8799f581c25195af85c41b9d97da7f4f215d3e74c9cef7f04739d6ba473ba72a2ffd8799fd8799fd8799f581c358a4e4105c08f59e4779070699c7a72566893332f9857db4e742bebffffffffd8799f581cc6e65ba7878b2f8ea0ad39287d3e2fd256dc5c4160fc19bdf4c4d87e457447454e53ff1a077359401a0361efaed8799f581cf03eca9ebdd30db76c5d5c0b450c97bbb4faccbc67cbf683e483197c45745553444dffd8799f0405ff5820c76aa2e728cac2a3b9f61f88f0fdc6c603c367696596fc1bb6746983bb9f1357d87a80d87a80031a000f42401a000f4240d8799f1a002dc6c01a0005b8d81a00015180ff1a03412142ff05a182000182d87a9f1a00bebc20ff821a000f49ff1a13bd0568a100828258208469288efa6f9cb49040b43dcff93f40969d72433f751afde50235012c01602058404a54c5c6b967a7c10fb561b63b898759faad058302fdc8b964d68b550cc5ece049b379ade714d07170e19c030c93c809bfd8568b24a9183f1e6939ef368e1f0182582076af5530fa318a370820270031d1838545a4ceed8696510627563d1114d4182b584057bf74e35d877b23ddfa939d6efcf157c09e3df9a4d5960f30fc64b6931e15e7d9caaa02451c9895644bafd3f36b5caf4a64a80668d21b97099943ad11eaec0aa100d901028182582045a35a111726f809cf2c33980ca06e45d29db1b06153c54e6eaafb6e4abfb2e958405909ae97282b146c27d506eca06e4a986703e4a5171c2a11c4896649768df5fb08a2f5b91db0766f2fca122ad981b271281e074f6fc4a8c36c804702e4fa4009a100d9010281825820d8229d65d9939ea5113366904b94715a816b9f21b128c4a95b6d15fc29032d265840455baffb3b01684ce9b461456addb02a3b7c35a182b92b7557178d161585236d19c682696c2839df4f513ee2906c5fea516f8c0d460a6c5e5e6cd170b0195d03a100d901028182582045a35a111726f809cf2c33980ca06e45d29db1b06153c54e6eaafb6e4abfb2e9584077feddb21c3d1ccbb7a23489febea4456c541f63fb7f32ba2ae0d79765c9f540edc229677fb2c067a08f2396fef7fc04c670e58267848efea4344fa977258d04a500d90103a100a11a000bb3f4a16376696478403938333831333164336462346535316531626237616232656334613661383037386334393266663637383062393932366435383030646466306464346633613601d90103a100a11a000bb3f4a16376696478406136336433636264333238643834326465373031633132363339393964613066633831393534336564326363636437363363643530633032383764373466623802d90103a100a11902a2a1636d736781781947656e6975735969656c643a204f726465722066696c6c656403a11901fda50050ca7a1457ef9f4c7f9c747f8c4a4cfa6c0150616f4c5a8adc62d5634102eeb7faea6f0258204d3f576f26db29139981a69443c2325daa812cc353a31b5a4db794a5bcbb06c20b9458401b0306006678baf9873001af26aafd1674e5b11bf549d4bea0a39bfa0324c494f7003a6b4c1c4bac378e322cb280230a4002f5b2754e863806f7e524afc9995658406829cb08ab014e580e707468318ac0086262ffa6d3039a557178a3c556cbee076b44163b02b2812e49d3ab27b5e7bb2e0c6b1321d042d1069a24b4884032468a58400a28c6c0b0af3474b476028a193051b0641d5d778162040c24d7d2d1cc925f3d504c8171e5b4757466bc5104a11147eeb110f17d6067ae4b0d04a1d4200a3590584042a59443fca0277bc8e0b053c8101ca338ac0fe93e702d048761686fe9bdf0d511e472c6bb85a27f033a24d745712b933dbe1c51141d6309d9cb4fa9c41f2acc5840995aa78f9877abf5fe4c55fd4ed2120cbfde107fcefdb363e0e2e35774f7357a6ec99ada7c55e70497f29bb3e860cbe05fd8b1e3e6f6193b3853ad6afcfef77d5840afa5ef4cff767e1fcb1e347afdf176f2f3acd76e99d65d192fff1c3eb37eefad4f3777dd744fab80b466fecbcc79e11670e3d281afbf0e3a3f0b5e8d30e217eb584092ffeb8ddef4c8b073d44ddc36f3ef6ed1df5461d4428bfbf43525effdb557aedc74ecb2d1522bdfd9b5696059fab43568cfdaa6dde757a7d58f8d4ca773a2cb58402ec92e8406f6aa7f8607fd795394c13ff26d79eb613f96cc50fd7acddef25f3f3cabae20280cc1cb70d806e0b005509e25a145b68604e551a03c844733c00db25840c212b5b5d3900818fa2673b0d1d32e6dacf77e8a1b755c5eb8bf7f1d8d864d8c9a0c706c8a000b64626162b10034d1a4744c28573452f9342c50c20a0e0e0c695840080e138ae55c9a24180b0992d2028040ca62f3f91c85985a27a5b243387496428629021a64120e1588654dd2207e089f16c80c0d95f3852d69947081f679dafa584004028a101f005b207025906daae14d828944320a0f0b31417a17114c5eee3a65a02b3990b0e6486c62c2f104d5aeb0b39b5844b5df2e0c0df43c80d19fbd21bd58404f96a827de443e9059867cbefe18d97cbf339fdba2e45f50c93c5e3c5f7db479c0655ec1fafd2fd7661c98a744b39dede7647efb9e39ca3655a559dcdfce659b5840aff6d5bf3b3a6bedbdcada9c38d7db31afaa8eac5f5b936991a779266559b1e3b6dca3feaadf2ecb268d8a962f202fbdb042e574e9c05bbecf4ee37da9bdd6675840d40ecb86c4a9deaf24f892989600ff8e816f59e7fa4f3ec854f6dfddce3d001febbd75634e536fec818bfd2a4c14d56298fbf4e3ac29b3b8a0ba035f0dffde0358406939eb07d1f03ab732b39985f5639a24536766ffa6ec36ab648ff2ce619c223d2ccc7c92e8ea74dff7d94cd99ccdc3d43a4dfa7f63afab81adbb39f126cad2875840482ae68dc83e43f47e9d1418764ea2f7afb3a2a34b177f43ee4cabd4617b053ee311dfd677ff70ece4af9719b36605fd38f15061917089b674f68b69654b71045840467192ac80318630c94ae56f2c163436b11a58805dc71484d2316aa8284c2ce38a25f40601d6100c42802c8015868582c05045a83c882e14622209b734969068584057bc65eca05168123b2db2fcc886fd6bafcddeebf1c2573515e1aaff3981776ce9a7339f12e6382207df6f3c7e3df750de8f58d753bf33ba2877a4ab8e5e5e435840b6b053deec20963a4430ddf75fdcb9dcb77ed9eacb5e3c5d6123d5546afb286165f8e1d4258365b73c2d3cf0dcd75f30e5a548adf83f176ff527bbbe09cc1dda5820334badf960b27d7aa97150b5db2ac882a65c014130ae05210b0075d0a2fd8c05186358404d9ecc0d2e40dba79d29e8c1f8b264b93ccbbb60a49ef419e12827196991aedfddfe44ca0388415b708579c060fd8047d96e6d68a0d7b8f2e6a3b9448fd53e0905d90103a100a11a000bb3f4a16376696478403638393139396437303165383961333138336536336230383336653230373062343131313538333139396562326237393934633038663762663332636365383380 \ No newline at end of file diff --git a/rust/rbac-registration/src/test_data/cardano/conway_3.block b/rust/rbac-registration/src/test_data/cardano/conway_3.block new file mode 100644 index 00000000000..09dd13e194f --- /dev/null +++ b/rust/rbac-registration/src/test_data/cardano/conway_3.block @@ -0,0 +1 @@ +820785828a1a002a25421a0453e3515820ab3904be7f5116a7ae4ffb8749b06ed1a6a09f2abdc0828b2b9f6b9621e45bc75820ac16ce463e6697d7b49bd22add462f092d9f6ceb808f74a4ecdbd8ed4ae1bbd758202596aa11710a0e31e9b3a8602005df3560a1d78e2f57e0dac408d205ead1835c825840765bb637312728cc374a634017e65c5fa8f6a477106d5c5ffb4620c847d417d0be21f88b8effdf14aec3cc632bd8016f504b6cd1a28595fd079bb64237d294cf5850e2be622ef7ed6d351b064f6f7eac04b2487a3320d3b1a7fc10481463abcf642e3b0b3e3e6aa88dc2adef6ab24ece460921bc5fc087d398a17c3bd89c93a825a68ec451ec589b199cb0019d20fd765801190726582036eca374937cb8fe6e5fa8c30a1af65ab42e2c18be80fdc793e9b224ae3efa788458206f897ea416c6817010ce15d184b1692da860fa1efce2dc87edf9ff517447c1b80919021c5840803cf49927cd22d4a68a6f4c91177d0478de2c33974870e20168ca5ebd6e7e49de2a615a3aeb3f38d9906ee20e6d86ef70f82a98247fa2d6998bcf252884260d8209015901c055a847c305997bad4458d8e08f53fa97fa99520ac46fc80834888485ed928476bc1c5b2a21d8db8fc85f258b8e09d1b45db0688edbba85ce836d7dd40345d40c5ecc67ae0dd6446c97071b3ba1ec6b71fa8f44de0d157c07180a83c043d1c813437ae50016f77e01a105d0e189020138f0b3427785c40a716e4310fae99bba5cd0d981a0ca5e9ca103216d71f4c03da7dde73b3d532a0c72d1986db62b4aeb037a8fba11119ec1154db04df51bc8c62a8ef1d035057970109c78378b47debdfe9ae2ecf9a35d15804db55ac2749c48e80b2e05312b734333484401eede425978e7ae6994c271c92f392a89ca2a6e419d4f9044aadc74baf32568067520db3f74543165ee22b69b57acd21ba4f3c762196a045d34c37274f33ce483666e42d149afc97e8495eb1ea5804c38d3fd728c5550513d1f20f26090dc67325315b71a0740a81c259dfd387e7dabb52d89baf37c5ed526a52b8171974221794d0db0213d224a7e68209452f3eed343dc3ac76107681efc39126d4133ae07f43232cd3fab7369a4238b74638c7ea0aa0c92468f2b9fb09275f8099fef584b15c62f700d91f857670868cbea6fd6bc409b37448efa78cc6b6b25a9ab4a8bdc6ed7d1d6bed481a60081825820a87b4642a59eba961124544a483d98568711284d8652e65105b051a6777e134e000181a200583900eb21979c03eed7207020b2a0b47565b5aafb1a2c3849b59a1fa8e6c5e075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d6473903011b000000024fd4fde5021a000399a907582079af25e1de0c8377fe74ffda579a5a121b58511f78c6cb80dd37dea97cd123d40e81581ce075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d64739030f0081a100828258208469288efa6f9cb49040b43dcff93f40969d72433f751afde50235012c0160205840fbfdebfa5d695157394d1306fb369abd4b8d541f7686d8f5ec0cdf00262cedce6379b9fdd18fdef149be17f5ad215b08ec0e96cd9f7ebda8a6ce77fdeb84e90a82582076af5530fa318a370820270031d1838545a4ceed8696510627563d1114d4182b5840ee532d8f465da42639109a93a21a4f693d65ba71e5326f8a34ec2f5d82c1b29fa23ae9213da417f3ba1507c592b7087819183722630fce4503c90f6953cb1d0ea100a11901fda50050ca7a1457ef9f4c7f9c747f8c4a4cfa6c015060ab06a8774b60aa1b1389f7a307a15f0258204d3f576f26db29139981a69443c2325daa812cc353a31b5a4db794a5bcbb06c20b9458401b03060066006fd5b67002167882eac0b5f2b11da40788a39bfa0324c494f7003a6b4c1c4bac378e322cb280230a4002f5b2754e863806f7e524afc99996aa28584032f02b600cbf04c6a09e05100880a09ee59b6627dc78d68175469b8c5b1fac141a6da5c6c2ea446597b6f0b6efea00a04ac0c1756455589908a5e089ba604a1258405917d6ee2b2535959d806c00eb2958929ababb40d681b5245751538e915d3d90f561ddcaa9aaa9cd78a30882a22a99c742c4f7610b43750a0d6651e8640a8d4c58402167427cfa933d6430c026640888210cd0c4e93e7015100300dcaef47b9c155ea4ccb27773c27f5d6a44fbf98065a14e5f0eca530e57082a971cbf22fa9065585840ae72e2a061eb558d3fd7727e87a8f07b5faf0d3cedf8d99ab6e0c845f5dd3ce78d31d7365c523b5a4dfe5d35bfafaefb2f60dd7473cbe8d6aa6bf557b1fbdf775840bf96bcd3ffdbfc7d20b65be7f5c7dba1cf635e3b449bb644bdfc73e0e49a5db73edddc7ed331220ba732f62f3aee8503f5c6f9bd5f7fedb37dc6580196052e50584027fdd7e8bfe9146561ad1ebc79ecef0ee1df7081cf9cd1fd929569ef3d55972d5b7ff882ce2213f789fc08787164f14aa86d55e98e332b220a07fa464aaa7c335840ce4bcfb268ed577f72e87fdea4442107bf2da93fe05121d5befa7ae5aecc5f3f9c732e82108003166380198c0146b0e214114a31d7c62b0ec18afd5834034c2b58402b2c515b350d8980a16932071b6d8d125ea1eb53dc28a8aee1787a1670b9e8c4c8cb00c726f3515a39ca1689f870295752820a64721e05e1a234710583416316584031d80291ac9a2b66a04cba844b85f9928a4a04a9928b2805124a25b3aaa4422e45e5d422a9b88a028ba4a5123ac244b8b472164b86085ac21357c3aae7696be25840f1104878009b03813d9e6c53255722402090206058a009d2b808aff772fb712d75f1dea09507fd83838e045dd9ce3eb59e4554f5ed02b8aeb60700f4b39dd9fe584064e1d5a137de0fa4c6cccfd71f831bee372756d72990b357a44e2f9eaf3854db65379db466cfcb55517ba71550acade564f4b7efd1fd95fa57228cee6fa9ae3458405ce1ae79b77f7cd5bdecfcb800fbdb7eaf720eae5995176d94a07c326c71aaf5e6f8439e577edb2d1ed64959324b5a7476e9159bf37bdf226edb747787b79b9e5840bc6ab5b84714eefa4a8c2df4aba37a36757d8b39dd79ec41b4a2f3ee96eabdc0e1f65b37264bdbfdf79eebbc820a7deab4e39f7e1cbf6610402fd8fb55fbef3d584038226e4d37c42970c830184b2e1c5026eadb9677ae8f6d300975ca6ceec5c8920382e827c1f636f7dd9f8d492737f4520a944bfeebba5ca2d5efa80ad453a43f584004c357ecccfc4dab75ce560b0300db9092ced52625d0c8df6fc89da9a45b6dc9c2461f21e6ee7b7afd877fbd8c1a1fa7ff38fa506e14749ebb68e24571c6220c584004208c284d628c2148b252f91b8b50014b080b040554095b52ca862bb974218222d412112ae5d2584c54584ae157f22b183cb4ba9c5fc42ba6894ad074ffe0875840c69ee921211d0ce4cd0f89b7e708163b3ab9286fe26a8c68ed85930cabc5dbfed7f9681c535dbdbfeb56f7a2b32d1f43de1dbcc934676edefacb3df7c1210067584064a1b8d94448b7f22a77dc736edb12f7c2c52b2eb8d4a80b78147d89f9a3a0659c03e10bbb336e391b3961f1afbfa08af3de2a817fceddea0cb57f438b0f8947581f9782ee92e890df65636d835d2d465cc5521c0ec05470e00280ac54b83f630118635840353ab6bd8dee46d19c9f0f19304e5088a9d866cdfbb13fb8c517d3129978b8a78d447201df75b604ad0b144a7e7f11cfe6ba70980c7bab8d4af158c90d39d40980820785828a1a00291dbb1a043840285820160ea19fb19f00088e2ef986fa354c958f0f40db89d29c32f188e5247a410736582054ca56226c90d5544bf6f558026b814027b634e60de0bc215237944110569c6c582037d6147adbf2907948d66f9e4751774bbe9d6c4ad562ce04ef66f0fc1e7ac301825840f4db3a5ec9aabd655c9f651071036626a368e855152897a01115661660f32b19bb406669ef359e30745c48dbe4872e1f5d110191de27d750230243f6150494f758505af67e52747541a74c5b13d1d24a4706fe7c84ce254b1eab283e1f38fe1398d055ce36a4ba4268d67ce1ef84580f093bc671dbbbf5887f96c2bf192ad1bb3d8b97765cdb927219f3e0988c93bbb0510c190f2b58202f98cae65753f272fa96ba2a7ab566f4aa81356e97c1800b57f6246fc193aeb5845820579c45732473db77b8acd430585aeb79ccf7e76b7b118f1e7302291a2cb3d958071901ff5840cad68c6ebff25b637335d3ea1647707cc0e91ecdbc00f1e4a8e3639f1879a5560d50bc948792d43dd2d7cbf54c3e188df16c7ad79f1f78ad1ec760b0725fbe0a820a005901c0f98f0569ea6ff5df6d3483aaa68e195ac1866edb28b03499a0adc8b2655a06855d69b15c39e94d13db695281a6249a65d175d37bcc029c422ba3cadf2afdde070f5658ef64058ab83e1435b5066a132195fba0981f76c4badaa93dd5cbe25a7028efed14eefadf9988557610d4e875a2d9375d25e5fe2939ebf23a5c44c0e4be7b107dfdc93a3b21c6a87ff9b46f69b65c53a204e5ead4fe9b1ae756e7a2755fd5a6dd9b4b67bc511479247eab6089e0eb17fea1eca7ac5fcd03dad404798a3067a2669d5eba8c3d762de40b42157486cd99746c0a021c213054b868a1f35f747933368ec3bd901b18ddddc09918d9d3562dfb425c9a3f442929c71ea09984f9a2d1f9a5f67016b07c406f319e9bda78158204f8641c215e9d6a726f1bba21ccf82a241e31ecf24624e95d5c82b42bea18435946706671969b91157f2071609c3cbb4b5e944e4829392e85b6c20920072ef246d1c9aec109331fc83c9f1d4b07d0d012547f82da0b83d514e2981186cb2d6b6e392c57465010414ae32bcbaf60f5e77e321a6a78b24e4ad9c44d5e6f1dfebb74d11d470cfd77d05848bab980308cdfd157edbc572c75a2f2c6462c9351607e1e7a944e9d0b41e1bfd1ef2dbdb387ab0083825820c3ad11a79c6b2b131fd37fec1f85be7dda6d5e8891f7523377008205ad6122b002825820ddebcb766e4902dc1b2537178875ff80da782402858254e994c9b782aa1dee3b01825820fe7258dd047bd4bb15641c1589ba9c9e5ebeccc689d2b01ae4210613f4d52d5b000183a300581d703f89c436844cd0acd2714201817836b2cd16b082e03e3a21bb69e5ad01821a001e8480a1581c2b556df9f37c04ef31b8f7f581c4e48174adcf5041e8e52497d81556a1484e6f64654665656401028201d818583cd87a9fd8799f581c564b2400e0901e336538bea2eb48f0bac8aa77b0c3a948f07cd15ff0d8799fd8799f1a0004fc6f1b00000191fa22918effffffff82581d60564b2400e0901e336538bea2eb48f0bac8aa77b0c3a948f07cd15ff01a0089544082581d60564b2400e0901e336538bea2eb48f0bac8aa77b0c3a948f07cd15ff01a006f2857021a0009d160031a0438406d081a04383ff50b5820d5adb9576273dc98ae443e7248d3ba5a35ba7befa01b37ead68c0810aa40882e0d81825820fe7258dd047bd4bb15641c1589ba9c9e5ebeccc689d2b01ae4210613f4d52d5b010e81581c564b2400e0901e336538bea2eb48f0bac8aa77b0c3a948f07cd15ff01082581d60564b2400e0901e336538bea2eb48f0bac8aa77b0c3a948f07cd15ff01a00524801111a00370c3f12818258201a28375b01a8928f21a87598e24165ab5806e7c3e77a5eb4d78c56e3cf07894c00a6008182582030133b1ce7dd59fedcf79adbd0012f7fab64bc54d08ea9621e0df25cf18ac656000181a200583900eb21979c03eed7207020b2a0b47565b5aafb1a2c3849b59a1fa8e6c5e075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d6473903011b000000024fdc310b021a0003372d075820aa05878c56074cd1241e91b49a63ef718988ff544c596e44521f1a8dc6b387650e81581ce075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d64739030f00a500818258202d9559bceafc74514c59ce55bbd9e2bec6dc31790f277d4aba4e042e85f4112a000181825839009fbdc6a75e10012d8c42017e375337cbc3760e96f911c87aacb6dfbe74b1d8380c8d4a9dfe1781ce3616c5f099b74854da5aa69532235ba91a04b04d51021a00029a6d031a04385c0e075820880443667460ae3b3016366d5bf66aca62c8149d67a53e94dde37120adffa624a500818258209e911e840789b2727a194f4d00959b4ee1f94db95542a16f2b0fde5cc14e03ff010182825839006f2c165bfb9e28a313da02d395eafe57ee1081bb14b8faac1856b0473907b2a702a6dcd0d937d4cbdd2ea8db46c335f9c6980ee93b0ee9b31a00a542f782583900fe33b43ae102d26c495a616187fd535a8ab31cf0cb0d7bc4f2ac35343f05a30548ea2f467860ff3ea675fcebf50bccf21eb99c9fc92d854a1b00000001d6236a82021a000295c9031a04385b0f081a005c5a2da40082825820139e2c770f1c836164cd9a04b756dc34622a672289f579efd42b48e929c3bf0a01825820bf1f24ac9ffcd9938f71a5dc2ce457f496d877440c57de1134334dc66378cc6d000182825839006d874798dee894262bb80ff32409e1ccb5c662f438d0832f7e83635b93cfb33930fd28cae34fd9a508f9a1c98bdd52561297e062983583dc821a001226b8a1581cf0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9aa150000de14068616e646c65746f73656e640182583900f3cb671c88d464460e064deca0dcdbdb68a5753a1f08a09deeed716a7fc7000fdaeb72ac4f46ead88304fb4b036d953184cd114522c60f2b821a034df563a1581c63f01fe6cd68ec6438c95a46cea4a6cd27efb791b5e8cc1fa92af329a1524c616365204e46542050726570726f64313501021a0002a98d031a04385c15a400d9010281825820c5fc4abb70cb78e2dd81ede78769187a5e62a2cbe61e89e3241cc87bf49d3c9901018282584f82d818584583581cb0c95c61063a9438de2cadd7835078da4fd3d967b325b0ef49a98be2a201581e581c0e3885905f8f51eb31f58ee644d9adbf925e914c7953bcd14859471e024101001ac50f88b51a0010602782584f82d818584583581cacdf62c4488e78c07e28c4c80539f4aa0bcf0f7d14b37c9eeef45173a201581e581c0e3885905f8f5185d980efe6b5befe429093a12e9751ccae2633a713024101001afd4bab7e1a17d16bed021a0002a6a1031a04385c2fa30082825820c0ded58d1f20a8b7e1c01087ebb1d1ebb94af490e8a93e3b9e5bb20a98ebf41e01825820ffb031c142583f453bb67564b3994f38edcaf175ef66edb778d447b3faffa558010183a300581d70c05492bb753dd4750beaf12356ce4e8252f3930cbf92d06854f6852d011a00b71b00028201d8185842d8799f43323435581cdf95da5f1f11e588bad36eef8c9ea171a706e4cdf529fd97156e74ebd8799f1a01312d00d879801a0007a12000d87980d8798001d87a80ffff82583900df95da5f1f11e588bad36eef8c9ea171a706e4cdf529fd97156e74eb758ab9d2ac7b679542638ee8024dfea41e77d462389977872454f2cc821a00129c92a1581c8a746b4319053b7855c68ac5ed9c943525702afa94c0b303684bc814a2463234305f6e6f1a012b1280473234305f7965731a012b128082583900df95da5f1f11e588bad36eef8c9ea171a706e4cdf529fd97156e74eb758ab9d2ac7b679542638ee8024dfea41e77d462389977872454f2cc1a9925a57b021a0002b3dd87a2008182582002205785797b8efa694d21d7dbc3a290d2ae09538957920c9409999c261ecb2f584084d1baa591107d76dc8d2c0ee9780ebe3840b0f249ab8a00539aa25002adabd96e36f081241eeafa071a02c4982c756ce2dd6e077c9cdd380c9dbd96feb076030581840002d87980821a0012eff61a1085253da100828258208469288efa6f9cb49040b43dcff93f40969d72433f751afde50235012c016020584047f575a45ba7a644eb8131f0c2a85475873fd3ea71d0a5104905dcd637013407b113b1cbf4b7af8cfc2922de2a416168a1276839946b293c0aa6dda61b87960d82582076af5530fa318a370820270031d1838545a4ceed8696510627563d1114d4182b584019db9a32be1507c0c13b2177086c17ddc39d0d0c7956258da66dfab937cc57d8c39f6384ef2a07ccffddbea56b1afbbfd0d2cf82446d099f4658bcd2989a0506a10081825820f398d21bc567394b5e2670bf2b8088a36c0355a60786f83033aaee90c51a0652584088064631469320af98bae7571a1f6ea50f733c46dd65c5cd1e79153096063a4b4b3f508dbfb715f80742bcf751d1d913a8e082b4a5be73b4dce608160c355000a1008182582046c7b1430923a9aa57971324819633d7f0dc984ae03ba37b2c58631ea9f18f1d584096465f0d4f1b715d26f027edf5be6624558aefe6d3d2dec6f3cd1b3f7b62269e2f578b1c8f1e539652f5d9f0758536a2fa860b199771e53e1705b139ad2b5f07a10081825820fa198ff264f1191a846af5f6067afa341b36b9c882988810b209613ef4686d585840ebb8757498a9209c4ff1b7e92ec69e44f89df18ea1093d978dbf0cc1a2ca6edcf4d1950887acc0c710f8828d2988854eb15e34b64325b6b1cfcec53d5155e400a102d9010281845820847953db74a03327eecedf38f424d510bd6dbfb0da2be989d5be80251b76319b58400789ea001bac725e24ee88673706771013c356f864f3a268544ff985f72d1dcc26a4a9992a367ad9e9ae9ba83325f73dcb37f76d6f262031837e94256431d90b582053ecfa2c8bc4ab302ec166256aa48a95d549c4a7c01f1e0a98153c654e94c16e5825a201581e581c0e3885905f8f51949c09b5e6d340fe92426bdf4a912d78ef9422b32a024101a10081825820975277c2fe1db3f260850e3f4946f15affa868a6df0388dbb9b4fcb40725e1c858402f68e3b0716c37dc105d6cee678ed5f734f1d906dcd37a72f7da4768629e42bd9b345f8d356733ee85a9231d45d7eed711acc9d11fa3c6f8a14928897441c002a201a11901fda50050ca7a1457ef9f4c7f9c747f8c4a4cfa6c0150cb6c2b83991a305eb48f60c933913acf0258204d3f576f26db29139981a69443c2325daa812cc353a31b5a4db794a5bcbb06c20b8b58401b09048064084f8b2b0dc2dd44a61d5c0c4ef620f87f37f5000ff1946fb706d0f7268e255684615920797acf734fc8224eb7197419d8ab868cb5be7696f3994258406c0360b135c350c9221c43314ccf2774cfee070b8dd79c709dd157d779a7e943d4cc0580a47057652be88502c0a3f05c825260b9d9500164bee00af9dd011a005840bde49a23144104280752cbf8425e6ad77503d4078a850542e1442bd2109e3675a1a42c9f030b4391970f08845e1082406f581492be6f46e7b990742b6e8df4735840dd542ee353bdda6ecea406fcce7c29a5d5bb02d12abec73a636fb8f257db52e62360de0166816f9bcb08660660a6b2dd6256fa1ae6194ff0fc58604bcad3446d5840447687b2ca55828795cd2d419e9e9534adf6d4c8c52e97be31a8536b02b440a3a9f4ee08eca80d048db4afb655ddea5da35377d23e01fe6a6dad575b635b839f584052dd5cdf56ad54f9e86ad44080af2921081c231f01f319db13048eb9ae144a921c1cdd2e5646e28b849ebdf9fd3cb95fee9fd53e137f87584cb9107cb8c8999b5840874722f9f71256ae0dec4a9c23183b07b9bdad2439b33e63ad6a7dbc38f3787ee16164d9cdf5a6e3f6dc06a9572ce04ddf7b5bf1cd2b53a79ec765b2b94b73125840ea47ede0e931058f9184839d037d07ab7326c7a3a86d6ccde8b526aa2727743fa20f1dbfc06a5b313bfd49abb9fbbef11153ffbc4e9e39d3f7d7f1c7dd46d1175840e925b35ea90a97b0188ab354276d7fb6b020a264c330a4c768e5a7a4691fc7e20ffef38e8df03dfeaae0cfdca04393824cdcc71e497c71ff95b0d6ebb945f8c6584095b2439d0d3d275295cddcd143765ce1d8159df9989105737b902cb00aae77dc7761c732f7aaa5ab2eb9b488344a2ffd0ef327d12b820e252c1e5b78c7d9c8895834cd009d9daf3f3b4d35d7d7db36e139f4389ae1b2cb2779eacbe08f974e9eb2d9b1ffef48239a598e2028cb47b0c3805bf1e9bf9118635840466ebf1f9df0f881cdaafa8a876b55ae6c38c958f9242261df29f48be13dfd9226f44fd82e60bc868353d5e790a920b006a42d36b63e3405d891ad297123500502a1191acea165706f6f6c7381a26269647838353136366636393434336666376433356136356637356330333565653765626637663964626531376334396234643861383262613439623566776569676874186480 \ No newline at end of file diff --git a/rust/rbac-registration/src/utils/cip19.rs b/rust/rbac-registration/src/utils/cip19.rs new file mode 100644 index 00000000000..8c378705bf3 --- /dev/null +++ b/rust/rbac-registration/src/utils/cip19.rs @@ -0,0 +1,136 @@ +//! Utility functions for CIP-19 address. + +use regex::Regex; + +use crate::cardano::transaction::witness::TxWitness; + +/// Extracts the CIP-19 bytes from a URI. +/// Example input: `web+cardano://addr/` +/// +/// URI = scheme ":" ["//" authority] path ["?" query] ["#" fragment] +#[must_use] +pub fn extract_cip19_hash(uri: &str, prefix: Option<&str>) -> Option> { + // Regex pattern to match the expected URI format + let r = Regex::new("^.+://addr/(.+)$").ok()?; + + // Apply the regex pattern to capture the CIP-19 address string + let address = r + .captures(uri) + .and_then(|cap| cap.get(1).map(|m| m.as_str().to_string())); + + match address { + Some(addr) => { + if let Some(prefix) = prefix { + if !addr.starts_with(prefix) { + return None; + } + } + let addr = bech32::decode(&addr).ok()?.1; + // As in CIP19, the first byte is the header, so extract only the payload + extract_key_hash(&addr) + }, + None => None, + } +} + +/// Extract the first 28 bytes from the given key +/// Refer to for more information. +pub(crate) fn extract_key_hash(key: &[u8]) -> Option> { + key.get(1..29).map(<[u8]>::to_vec) +} + +/// Compare the given public key bytes with the transaction witness set. +pub(crate) fn compare_key_hash( + pk_addrs: &[Vec], witness: &TxWitness, txn_idx: u16, +) -> anyhow::Result<()> { + if pk_addrs.is_empty() { + return Err(anyhow::anyhow!("No public key addresses provided")); + } + + pk_addrs.iter().try_for_each(|pk_addr| { + let pk_addr: [u8; 28] = pk_addr.as_slice().try_into().map_err(|_| { + anyhow::anyhow!( + "Invalid length for vkey, expected 28 bytes but got {}", + pk_addr.len() + ) + })?; + + // Key hash not found in the transaction witness set + if !witness.check_witness_in_tx(&pk_addr, txn_idx) { + return Err(anyhow::anyhow!( + "Public key hash not found in transaction witness set given {:?}", + pk_addr + )); + } + + Ok(()) + }) +} + +#[cfg(test)] +mod tests { + use super::*; + + // Test data from https://cips.cardano.org/cip/CIP-19 + // cSpell:disable + const STAKE_ADDR: &str = "stake_test1uqehkck0lajq8gr28t9uxnuvgcqrc6070x3k9r8048z8y5gssrtvn"; + const PAYMENT_ADDR: &str = "addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3n0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgs68faae"; + // cSpell:enable + + #[test] + fn test_extract_cip19_hash_with_stake() { + // Additional tools to check for bech32 https://slowli.github.io/bech32-buffer/ + let uri = &format!("web+cardano://addr/{STAKE_ADDR}"); + // Given: + // e0337b62cfff6403a06a3acbc34f8c46003c69fe79a3628cefa9c47251 + // The first byte is the header, so extract only the payload + let bytes = hex::decode("337b62cfff6403a06a3acbc34f8c46003c69fe79a3628cefa9c47251") + .expect("Failed to decode bytes"); + assert_eq!( + extract_cip19_hash(uri, Some("stake")).expect("Failed to extract CIP-19 hash"), + bytes + ); + } + + #[test] + fn test_extract_cip19_hash_with_addr_with_prefix_set() { + let uri = &format!("web+cardano://addr/{PAYMENT_ADDR}"); + let result = extract_cip19_hash(uri, Some("stake")); + assert_eq!(result, None); + } + + #[test] + fn test_extract_cip19_hash_with_addr_without_prefix_set() { + let uri = &format!("web+cardano://addr/{PAYMENT_ADDR}"); + let result = extract_cip19_hash(uri, None); + assert!(result.is_some()); + } + + #[test] + fn test_extract_cip19_hash_invalid_uri() { + let uri = "invalid_uri"; + let result = extract_cip19_hash(uri, None); + assert_eq!(result, None); + } + + #[test] + fn test_extract_cip19_hash_non_bech32_address() { + let uri = "example://addr/not_bech32"; + let result = extract_cip19_hash(uri, None); + assert_eq!(result, None); + } + + #[test] + fn test_extract_cip19_hash_empty_uri() { + let uri = ""; + let result = extract_cip19_hash(uri, None); + assert_eq!(result, None); + } + + #[test] + fn test_extract_cip19_hash_no_address() { + let uri = "example://addr/"; + let result = extract_cip19_hash(uri, None); + assert_eq!(result, None); + } +} diff --git a/rust/rbac-registration/src/utils/decode_helper.rs b/rust/rbac-registration/src/utils/decode_helper.rs new file mode 100644 index 00000000000..5c64cb12f75 --- /dev/null +++ b/rust/rbac-registration/src/utils/decode_helper.rs @@ -0,0 +1,215 @@ +//! CBOR decoding helper functions. + +use minicbor::{data::Tag, decode, Decoder}; + +/// Generic helper function for decoding different types. +pub(crate) 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. +pub(crate) 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. +pub(crate) 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. +pub(crate) 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. +pub(crate) 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 +pub(crate) 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 + println!("{result:?}"); + assert!(result.is_err()); + } +} diff --git a/rust/rbac-registration/src/utils/general.rs b/rust/rbac-registration/src/utils/general.rs new file mode 100644 index 00000000000..3596b668070 --- /dev/null +++ b/rust/rbac-registration/src/utils/general.rs @@ -0,0 +1,27 @@ +//! General utility functions + +/// Getting the index by decrementing by 1. +/// e.g. 1 should refers to index 0 +pub(crate) fn decremented_index(int: i16) -> anyhow::Result { + match usize::try_from(int) { + Ok(value) => Ok(value - 1), + Err(e) => { + Err(anyhow::Error::msg(format!( + "Failed to convert to usize: {e}" + ))) + }, + } +} + +/// Decode the given UTF-8 content. +pub(crate) fn decode_utf8(content: &[u8]) -> anyhow::Result { + // Decode the UTF-8 string + std::str::from_utf8(content) + .map(std::string::ToString::to_string) + .map_err(|_| { + anyhow::anyhow!( + "Invalid UTF-8 string, expected valid UTF-8 string but got {:?}", + content + ) + }) +} diff --git a/rust/rbac-registration/src/utils/hashing.rs b/rust/rbac-registration/src/utils/hashing.rs new file mode 100644 index 00000000000..2425f40ad0b --- /dev/null +++ b/rust/rbac-registration/src/utils/hashing.rs @@ -0,0 +1,29 @@ +//! Hashing utility function. + +use blake2b_simd::{self, Params}; + +/// Convert the given value to `blake2b_244` array. +#[allow(dead_code)] // Its OK if we don't use this general utility function. +pub(crate) fn blake2b_244(value: &[u8]) -> anyhow::Result<[u8; 28]> { + let h = Params::new().hash_length(28).hash(value); + let b = h.as_bytes(); + b.try_into() + .map_err(|_| anyhow::anyhow!("Invalid length of blake2b_244, expected 28 got {}", b.len())) +} + +/// Convert the given value to `blake2b_256` array. +#[allow(dead_code)] // Its OK if we don't use this general utility function. +pub(crate) fn blake2b_256(value: &[u8]) -> anyhow::Result<[u8; 32]> { + let h = Params::new().hash_length(32).hash(value); + let b = h.as_bytes(); + b.try_into() + .map_err(|_| anyhow::anyhow!("Invalid length of blake2b_256, expected 32 got {}", b.len())) +} + +/// Convert the given value to `blake2b_128` array. +pub(crate) fn blake2b_128(value: &[u8]) -> anyhow::Result<[u8; 16]> { + let h = Params::new().hash_length(16).hash(value); + let b = h.as_bytes(); + b.try_into() + .map_err(|_| anyhow::anyhow!("Invalid length of blake2b_128, expected 16 got {}", b.len())) +} diff --git a/rust/rbac-registration/src/utils/mod.rs b/rust/rbac-registration/src/utils/mod.rs new file mode 100644 index 00000000000..52c004e1a62 --- /dev/null +++ b/rust/rbac-registration/src/utils/mod.rs @@ -0,0 +1,6 @@ +//! Utility functions for the RBAC registration module. + +pub mod cip19; +pub(crate) mod decode_helper; +pub(crate) mod general; +pub(crate) mod hashing; From 05c5ae3a8318669760781953816178a9aa32bee7 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Mon, 11 Nov 2024 15:41:11 +0700 Subject: [PATCH 02/45] fix: remove key ref and use key local ref Signed-off-by: bkioshn --- .../src/cardano/cip509/rbac/role_data.rs | 40 +++---------------- 1 file changed, 6 insertions(+), 34 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/rbac/role_data.rs b/rust/rbac-registration/src/cardano/cip509/rbac/role_data.rs index 9d3a0bd908f..98512e77e00 100644 --- a/rust/rbac-registration/src/cardano/cip509/rbac/role_data.rs +++ b/rust/rbac-registration/src/cardano/cip509/rbac/role_data.rs @@ -6,7 +6,7 @@ use minicbor::{decode, Decode, Decoder}; use strum_macros::FromRepr; use super::{decode_any, decode_map_len, Cip509RbacMetadataInt}; -use crate::utils::decode_helper::{decode_array_len, decode_bytes, decode_helper}; +use crate::utils::decode_helper::{decode_array_len, decode_helper}; /// Struct of role data. #[derive(Debug, PartialEq, Clone, Default)] @@ -14,9 +14,9 @@ pub struct RoleData { /// Role number. pub role_number: u8, /// Optional role signing key. - pub role_signing_key: Option, + pub role_signing_key: Option, /// Optional role encryption key. - pub role_encryption_key: Option, + pub role_encryption_key: Option, /// Optional payment key. pub payment_key: Option, /// Optional role extended data keys. @@ -56,10 +56,10 @@ impl Decode<'_, ()> for RoleData { role_data.role_number = decode_helper(d, "RoleNumber in RoleData", ctx)?; }, RoleDataInt::RoleSigningKey => { - role_data.role_signing_key = Some(KeyReference::decode(d, ctx)?); + role_data.role_signing_key = Some(KeyLocalRef::decode(d, ctx)?); }, RoleDataInt::RoleEncryptionKey => { - role_data.role_encryption_key = Some(KeyReference::decode(d, ctx)?); + role_data.role_encryption_key = Some(KeyLocalRef::decode(d, ctx)?); }, RoleDataInt::PaymentKey => { role_data.payment_key = @@ -78,34 +78,6 @@ impl Decode<'_, ()> for RoleData { Ok(role_data) } } - -/// Enum of key reference. -#[derive(Debug, PartialEq, Clone)] -pub enum KeyReference { - /// Key local reference. - KeyLocalRef(KeyLocalRef), - /// Key hash. - KeyHash(Vec), -} - -impl Default for KeyReference { - fn default() -> Self { - KeyReference::KeyHash(Vec::new()) - } -} - -impl Decode<'_, ()> for KeyReference { - fn decode(d: &mut Decoder, ctx: &mut ()) -> Result { - match d.datatype()? { - minicbor::data::Type::Array => Ok(Self::KeyLocalRef(KeyLocalRef::decode(d, ctx)?)), - minicbor::data::Type::Bytes => { - Ok(Self::KeyHash(decode_bytes(d, "KeyHash in KeyReference")?)) - }, - _ => Err(decode::Error::message("Invalid data type for KeyReference")), - } - } -} - /// Struct of key local reference. #[derive(Debug, PartialEq, Clone)] pub struct KeyLocalRef { @@ -116,7 +88,7 @@ pub struct KeyLocalRef { } /// Enum of local reference with its associated unsigned integer value. -#[derive(FromRepr, Debug, PartialEq, Clone)] +#[derive(FromRepr, Debug, PartialEq, Clone, Eq, Hash)] #[repr(u8)] pub enum LocalRefInt { /// x509 certificates. From 7f502cae195f600d88008f4f62878b2d7ea8f698 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Mon, 11 Nov 2024 17:53:59 +0700 Subject: [PATCH 03/45] fix: structure of x509, c509, and public key Signed-off-by: bkioshn --- .../src/cardano/cip509/rbac/certs.rs | 111 ++++++++++++------ .../src/cardano/cip509/rbac/mod.rs | 1 + .../src/cardano/cip509/rbac/pub_key.rs | 44 ++----- .../src/cardano/cip509/rbac/tag.rs | 21 ++++ 4 files changed, 107 insertions(+), 70 deletions(-) create mode 100644 rust/rbac-registration/src/cardano/cip509/rbac/tag.rs diff --git a/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs b/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs index a139b8b859b..eaa843b17a3 100644 --- a/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs +++ b/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs @@ -4,28 +4,56 @@ use c509_certificate::c509::C509; use minicbor::{decode, Decode, Decoder}; use x509_cert::{der::Decode as x509Decode, Certificate}; -use crate::utils::decode_helper::{decode_array_len, decode_bytes, decode_helper}; +use crate::utils::decode_helper::{decode_array_len, decode_bytes, decode_helper, decode_tag}; + +use super::tag::KeyTag; // ------------------x509------------------------ -/// A struct of X509 certificate. -#[derive(Debug, PartialEq, Clone)] -pub struct X509DerCert(pub Vec); +/// Enum of possible X.509 DER certificate. +#[derive(Debug, PartialEq, Clone, Default)] +pub enum X509DerCert { + /// Undefined indicates skipped element. + #[default] + Undefined, + /// Deleted indicates the key is deleted. + Deleted, + /// X.509 certificate. + X509Cert(Vec), +} impl Decode<'_, ()> for X509DerCert { fn decode(d: &mut Decoder, _ctx: &mut ()) -> Result { - let data = decode_bytes(d, "X509DerCert")?; - Certificate::from_der(&data) - .map_err(|_| decode::Error::message("Invalid x509 certificate"))?; - Ok(Self(data.clone())) + match d.datatype()? { + minicbor::data::Type::Tag => { + let tag = decode_tag(d, "X509DerCert")?; + match tag { + t if t == KeyTag::Deleted.tag() => Ok(Self::Deleted), + _ => Err(decode::Error::message("Unknown tag for X509DerCert")), + } + }, + minicbor::data::Type::Undefined => Ok(Self::Undefined), + minicbor::data::Type::Bytes => { + let data = decode_bytes(d, "X509DerCert")?; + Certificate::from_der(&data) + .map_err(|_| decode::Error::message("Invalid x509 certificate"))?; + Ok(Self::X509Cert(data.clone())) + }, + _ => Err(decode::Error::message("Invalid datatype for X509DerCert")), + } } } // ------------------c509----------------------- -/// Enum of possible c509 certificate. -#[derive(Debug, PartialEq, Clone)] +/// Enum of possible X.509 DER certificate. +#[derive(Debug, PartialEq, Clone, Default)] pub enum C509Cert { + /// Undefined indicates skipped element. + #[default] + Undefined, + /// Deleted indicates the key is deleted. + Deleted, /// A c509 certificate in metadatum reference. C509CertInMetadatumReference(C509CertInMetadatumReference), /// A c509 certificate. @@ -34,26 +62,37 @@ pub enum C509Cert { impl Decode<'_, ()> for C509Cert { fn decode(d: &mut Decoder, ctx: &mut ()) -> Result { - if d.datatype()? == minicbor::data::Type::Array { - let arr_len = decode_array_len(d, "C509Cert")?; - // C509CertInMetadatumReference must have 3 items - if arr_len == 3 { - Ok(Self::C509CertInMetadatumReference( - C509CertInMetadatumReference::decode(d, ctx)?, - )) - } else { - Err(decode::Error::message( - "Invalid length C509CertInMetadatumReference, expected 3", - )) - } - } else { - // Consuming the c509 bytes - let c509 = decode_bytes(d, "C509Cert")?; - let mut c509_d = Decoder::new(&c509); - Ok(Self::C509Certificate(Box::new(C509::decode( - &mut c509_d, - ctx, - )?))) + match d.datatype()? { + minicbor::data::Type::Tag => { + let tag = decode_tag(d, "C509Cert")?; + match tag { + t if t == KeyTag::Deleted.tag() => Ok(Self::Deleted), + _ => Err(decode::Error::message("Unknown tag for C509Cert")), + } + }, + minicbor::data::Type::Array => { + let arr_len = decode_array_len(d, "C509Cert")?; + // C509CertInMetadatumReference must have 3 items + if arr_len == 3 { + Ok(Self::C509CertInMetadatumReference( + C509CertInMetadatumReference::decode(d, ctx)?, + )) + } else { + Err(decode::Error::message( + "Invalid length C509CertInMetadatumReference, expected 3", + )) + } + }, + minicbor::data::Type::Bytes => { + let c509 = decode_bytes(d, "C509Cert")?; + let mut c509_d = Decoder::new(&c509); + Ok(Self::C509Certificate(Box::new(C509::decode( + &mut c509_d, + ctx, + )?))) + }, + minicbor::data::Type::Undefined => Ok(Self::Undefined), + _ => Err(decode::Error::message("Invalid datatype for C509Cert")), } } } @@ -82,13 +121,11 @@ impl Decode<'_, ()> for C509CertInMetadatumReference { arr.map(Some) }, minicbor::data::Type::Null => Ok(None), - _ => { - Ok(Some(vec![decode_helper( - d, - "C509CertInMetadatumReference", - ctx, - )?])) - }, + _ => Ok(Some(vec![decode_helper( + d, + "C509CertInMetadatumReference", + ctx, + )?])), }?; Ok(Self { txn_output_field, diff --git a/rust/rbac-registration/src/cardano/cip509/rbac/mod.rs b/rust/rbac-registration/src/cardano/cip509/rbac/mod.rs index 51a96378a77..8d649b9d453 100644 --- a/rust/rbac-registration/src/cardano/cip509/rbac/mod.rs +++ b/rust/rbac-registration/src/cardano/cip509/rbac/mod.rs @@ -5,6 +5,7 @@ pub mod certs; pub mod pub_key; pub mod role_data; +pub(crate) mod tag; use std::collections::HashMap; diff --git a/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs b/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs index a7aa072ed53..4842ef6852a 100644 --- a/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs +++ b/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs @@ -1,9 +1,11 @@ //! Public key type for RBAC metadata -use minicbor::{data::Tag, decode, Decode, Decoder}; +use minicbor::{decode, Decode, Decoder}; use crate::utils::decode_helper::{decode_bytes, decode_tag}; +use super::tag::KeyTag; + /// Enum of possible public key type. #[derive(Debug, PartialEq, Clone, Default)] pub enum SimplePublicKeyType { @@ -16,54 +18,30 @@ pub enum SimplePublicKeyType { Ed25519([u8; 32]), } -/// Enum of possible public key tag. -enum PublicKeyTag { - /// Deleted Key tag 31. - Deleted, - /// Ed25519 Key tag 32773. - Ed25519, -} - -impl PublicKeyTag { - /// Get the tag value. - fn tag(self) -> Tag { - match self { - PublicKeyTag::Deleted => Tag::new(0x31), - PublicKeyTag::Ed25519 => Tag::new(0x8005), - } - } -} - impl Decode<'_, ()> for SimplePublicKeyType { fn decode(d: &mut Decoder, _ctx: &mut ()) -> Result { match d.datatype()? { minicbor::data::Type::Tag => { let tag = decode_tag(d, "SimplePublicKeyType")?; match tag { - t if t == PublicKeyTag::Deleted.tag() => Ok(SimplePublicKeyType::Deleted), - t if t == PublicKeyTag::Ed25519.tag() => { + t if t == KeyTag::Deleted.tag() => Ok(Self::Deleted), + t if t == KeyTag::Ed25519.tag() => { let bytes = decode_bytes(d, "Ed25519 SimplePublicKeyType")?; let mut ed25519 = [0u8; 32]; if bytes.len() == 32 { ed25519.copy_from_slice(&bytes); - Ok(SimplePublicKeyType::Ed25519(ed25519)) + Ok(Self::Ed25519(ed25519)) } else { Err(decode::Error::message("Invalid length for Ed25519 key")) } }, - _ => { - Err(decode::Error::message( - "Unknown tag for SimplePublicKeyType", - )) - }, + _ => Err(decode::Error::message("Unknown tag for Self")), } }, - minicbor::data::Type::Undefined => Ok(SimplePublicKeyType::Undefined), - _ => { - Err(decode::Error::message( - "Invalid datatype for SimplePublicKeyType", - )) - }, + minicbor::data::Type::Undefined => Ok(Self::Undefined), + _ => Err(decode::Error::message( + "Invalid datatype for SimplePublicKeyType", + )), } } } diff --git a/rust/rbac-registration/src/cardano/cip509/rbac/tag.rs b/rust/rbac-registration/src/cardano/cip509/rbac/tag.rs new file mode 100644 index 00000000000..7fc84c548db --- /dev/null +++ b/rust/rbac-registration/src/cardano/cip509/rbac/tag.rs @@ -0,0 +1,21 @@ +//! Tags used in CIP-0509 besides from the original tags defined in minicbor. + +use minicbor::data::Tag; + +/// Enum of possible key tag which are use in public key and certificates. +pub(crate) enum KeyTag { + /// Deleted Key tag 31. + Deleted, + /// Ed25519 Key tag 32773. + Ed25519, +} + +impl KeyTag { + /// Get the tag value. + pub(crate) fn tag(self) -> Tag { + match self { + KeyTag::Deleted => Tag::new(0x31), + KeyTag::Ed25519 => Tag::new(0x8005), + } + } +} From becf77584e8033a133058ae9d83ca612298876be Mon Sep 17 00:00:00 2001 From: bkioshn Date: Sun, 17 Nov 2024 14:32:22 +0700 Subject: [PATCH 04/45] fix: validation Signed-off-by: bkioshn --- .../src/cardano/cip509/mod.rs | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/mod.rs b/rust/rbac-registration/src/cardano/cip509/mod.rs index c55c1f93ef7..09ee28ddf1e 100644 --- a/rust/rbac-registration/src/cardano/cip509/mod.rs +++ b/rust/rbac-registration/src/cardano/cip509/mod.rs @@ -5,6 +5,7 @@ // cspell: words pkix pub mod rbac; +pub mod types; pub mod x509_chunks; use c509_certificate::{general_names::general_name::GeneralNameValue, C509ExtensionType}; @@ -150,19 +151,30 @@ impl Cip509 { /// * `txn` - Transaction data was attached to and to be validated/decoded against. /// * `txn_idx` - Transaction Index /// * `validation_report` - Validation report to store the validation result. - pub fn validate(&self, txn: &MultiEraTx, txn_idx: usize, validation_report: &mut Vec) { - self.validate_txn_inputs_hash(txn, validation_report); - self.validate_aux(txn, validation_report); + pub fn validate( + &self, txn: &MultiEraTx, txn_idx: usize, validation_report: &mut Vec, + ) -> bool { + let tx_input_validate = self + .validate_txn_inputs_hash(txn, validation_report) + .unwrap_or(false); + let aux_validate = self.validate_aux(txn, validation_report).unwrap_or(false); + let mut stake_key_validate = true; + let mut payment_key_validate = true; // Validate the role 0 if let Some(role_set) = &self.x509_chunks.0.role_set { // Validate only role 0 for role in role_set { if role.role_number == 0 { - self.validate_stake_public_key(txn, txn_idx, validation_report); - self.validate_payment_key(txn, txn_idx, role, validation_report); + stake_key_validate = self + .validate_stake_public_key(txn, txn_idx, validation_report) + .unwrap_or(false); + payment_key_validate = self + .validate_payment_key(txn, txn_idx, role, validation_report) + .unwrap_or(false); } } } + tx_input_validate && aux_validate && stake_key_validate && payment_key_validate } /// Transaction inputs hash validation. From a65cba410612c4a6303a63c626f4ff4d2ef47e26 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Sun, 17 Nov 2024 17:24:21 +0700 Subject: [PATCH 05/45] fix: update x509 and c509 certs Signed-off-by: bkioshn --- .../src/cardano/cip509/mod.rs | 128 ++++++++++-------- 1 file changed, 69 insertions(+), 59 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/mod.rs b/rust/rbac-registration/src/cardano/cip509/mod.rs index 09ee28ddf1e..a1fc9557ff2 100644 --- a/rust/rbac-registration/src/cardano/cip509/mod.rs +++ b/rust/rbac-registration/src/cardano/cip509/mod.rs @@ -260,82 +260,91 @@ impl Cip509 { if let MultiEraTx::Conway(_) = txn { // X509 certificate if let Some(x509_certs) = &self.x509_chunks.0.x509_certs { - for cert in x509_certs { - // Attempt to decode the DER certificate - let der_cert = match x509_cert::Certificate::from_der(&cert.0) { - Ok(cert) => cert, - Err(e) => { - validation_report - .push(format!("Failed to decode x509 certificate DER: {e}")); - return None; - }, - }; + for x509_cert in x509_certs { + match x509_cert { + rbac::certs::X509DerCert::X509Cert(cert) => { + // Attempt to decode the DER certificate + let der_cert = match x509_cert::Certificate::from_der(cert) { + Ok(cert) => cert, + Err(e) => { + validation_report.push(format!( + "Failed to decode x509 certificate DER: {e}" + )); + return None; + }, + }; - // Find the Subject Alternative Name extension - let san_ext = der_cert - .tbs_certificate - .extensions - .as_ref() - .and_then(|exts| { - exts.iter() - .find(|ext| ext.extn_id == ID_CE_SUBJECT_ALT_NAME) - }); - - // Subject Alternative Name extension if it exists - if let Some(san_ext) = san_ext { - match parse_der_sequence(san_ext.extn_value.as_bytes()) { - Ok((_, parsed_seq)) => { - for data in parsed_seq.ref_iter() { - // Check for context-specific primitive type with tag number - // 6 (raw_tag 134) - if data.header.raw_tag() == Some(&[URI]) { - match data.content.as_slice() { - Ok(content) => { - // Decode the UTF-8 string - let addr: String = match decode_utf8(content) { - Ok(addr) => addr, - Err(e) => { - validation_report.push(format!( + // Find the Subject Alternative Name extension + let san_ext = + der_cert + .tbs_certificate + .extensions + .as_ref() + .and_then(|exts| { + exts.iter() + .find(|ext| ext.extn_id == ID_CE_SUBJECT_ALT_NAME) + }); + + // Subject Alternative Name extension if it exists + if let Some(san_ext) = san_ext { + match parse_der_sequence(san_ext.extn_value.as_bytes()) { + Ok((_, parsed_seq)) => { + for data in parsed_seq.ref_iter() { + // Check for context-specific primitive type with tag number + // 6 (raw_tag 134) + if data.header.raw_tag() == Some(&[URI]) { + match data.content.as_slice() { + Ok(content) => { + // Decode the UTF-8 string + let addr: String = match decode_utf8( + content, + ) { + Ok(addr) => addr, + Err(e) => { + validation_report.push(format!( "Failed to decode UTF-8 string for context-specific primitive type with raw tag 134: {e}", ), ); + return None; + }, + }; + + // Extract the CIP19 hash and push into + // array + if let Some(h) = + extract_cip19_hash(&addr, Some("stake")) + { + pk_addrs.push(h); + } + }, + Err(e) => { + validation_report.push( + format!("Failed to process content for context-specific primitive type with raw tag 134: {e}")); return None; }, - }; - - // Extract the CIP19 hash and push into - // array - if let Some(h) = - extract_cip19_hash(&addr, Some("stake")) - { - pk_addrs.push(h); } - }, - Err(e) => { - validation_report.push( - format!("Failed to process content for context-specific primitive type with raw tag 134: {e}")); - return None; - }, + } } - } - } - }, - Err(e) => { - validation_report.push( + }, + Err(e) => { + validation_report.push( format!( "Failed to parse DER sequence for Subject Alternative Name extension: {e}" ) ); - return None; - }, - } + return None; + }, + } + } + }, + _ => continue, } } } // C509 Certificate if let Some(c509_certs) = &self.x509_chunks.0.c509_certs { - for cert in c509_certs { - match cert { + for c509_cert in c509_certs { + match c509_cert { C509Cert::C509CertInMetadatumReference(_) => { validation_report.push( "C509 metadatum reference is currently not supported".to_string(), @@ -383,6 +392,7 @@ impl Cip509 { } } }, + _ => continue, } } } From 94dcefc33b18599ff09378771ac31329dc3c75a0 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Sun, 17 Nov 2024 17:27:24 +0700 Subject: [PATCH 06/45] fix: key local ref --- .../src/cardano/cip509/rbac/role_data.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/rbac/role_data.rs b/rust/rbac-registration/src/cardano/cip509/rbac/role_data.rs index 98512e77e00..ca637890d31 100644 --- a/rust/rbac-registration/src/cardano/cip509/rbac/role_data.rs +++ b/rust/rbac-registration/src/cardano/cip509/rbac/role_data.rs @@ -14,9 +14,9 @@ pub struct RoleData { /// Role number. pub role_number: u8, /// Optional role signing key. - pub role_signing_key: Option, + pub role_signing_key: Option>, /// Optional role encryption key. - pub role_encryption_key: Option, + pub role_encryption_key: Option>, /// Optional payment key. pub payment_key: Option, /// Optional role extended data keys. @@ -56,10 +56,20 @@ impl Decode<'_, ()> for RoleData { role_data.role_number = decode_helper(d, "RoleNumber in RoleData", ctx)?; }, RoleDataInt::RoleSigningKey => { - role_data.role_signing_key = Some(KeyLocalRef::decode(d, ctx)?); + let arr_len = decode_array_len(d, "RoleSigningKey")?; + let mut role_signing_key = Vec::new(); + for _ in 0..arr_len { + role_signing_key.push(KeyLocalRef::decode(d, ctx)?); + } + role_data.role_signing_key = Some(role_signing_key); }, RoleDataInt::RoleEncryptionKey => { - role_data.role_encryption_key = Some(KeyLocalRef::decode(d, ctx)?); + let arr_len = decode_array_len(d, "RoleEncryptionKey")?; + let mut role_encryption_key = Vec::new(); + for _ in 0..arr_len { + role_encryption_key.push(KeyLocalRef::decode(d, ctx)?); + } + role_data.role_encryption_key = Some(role_encryption_key); }, RoleDataInt::PaymentKey => { role_data.payment_key = @@ -101,7 +111,6 @@ pub enum LocalRefInt { impl Decode<'_, ()> for KeyLocalRef { fn decode(d: &mut Decoder, ctx: &mut ()) -> Result { - decode_array_len(d, "KeyLocalRef")?; let local_ref = LocalRefInt::from_repr(decode_helper(d, "LocalRef in KeyLocalRef", ctx)?) .ok_or(decode::Error::message("Invalid local reference"))?; let key_offset: u64 = decode_helper(d, "KeyOffset in KeyLocalRef", ctx)?; From 2c5c0776e2bc81dd636fc6674fe00befe17a82b5 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Sun, 17 Nov 2024 18:02:39 +0700 Subject: [PATCH 07/45] fix: c509 cert Signed-off-by: bkioshn --- rust/rbac-registration/src/cardano/cip509/rbac/certs.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs b/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs index eaa843b17a3..4a3598c67d5 100644 --- a/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs +++ b/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs @@ -57,7 +57,7 @@ pub enum C509Cert { /// A c509 certificate in metadatum reference. C509CertInMetadatumReference(C509CertInMetadatumReference), /// A c509 certificate. - C509Certificate(Box), + C509Certificate(C509), } impl Decode<'_, ()> for C509Cert { @@ -86,10 +86,7 @@ impl Decode<'_, ()> for C509Cert { minicbor::data::Type::Bytes => { let c509 = decode_bytes(d, "C509Cert")?; let mut c509_d = Decoder::new(&c509); - Ok(Self::C509Certificate(Box::new(C509::decode( - &mut c509_d, - ctx, - )?))) + Ok(Self::C509Certificate(C509::decode(&mut c509_d, ctx)?)) }, minicbor::data::Type::Undefined => Ok(Self::Undefined), _ => Err(decode::Error::message("Invalid datatype for C509Cert")), From 33d1ad340c4e1cb10abfd42e4e0a5588c1fb2a4e Mon Sep 17 00:00:00 2001 From: bkioshn Date: Mon, 18 Nov 2024 21:13:35 +0700 Subject: [PATCH 08/45] fix: to string Signed-off-by: bkioshn --- .../src/cardano/cip509/mod.rs | 54 +++++++++---------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/mod.rs b/rust/rbac-registration/src/cardano/cip509/mod.rs index a1fc9557ff2..d763292aa49 100644 --- a/rust/rbac-registration/src/cardano/cip509/mod.rs +++ b/rust/rbac-registration/src/cardano/cip509/mod.rs @@ -5,7 +5,6 @@ // cspell: words pkix pub mod rbac; -pub mod types; pub mod x509_chunks; use c509_certificate::{general_names::general_name::GeneralNameValue, C509ExtensionType}; @@ -215,9 +214,9 @@ impl Cip509 { }; Some(inputs_hash == self.txn_inputs_hash) } else { - validation_report.push( - "Unsupported transaction era for transaction inputs hash validation".to_string(), - ); + validation_report.push(format!( + "Unsupported transaction era for transaction inputs hash validation" + )); None } } @@ -234,17 +233,18 @@ impl Cip509 { .as_ref() .or_else(|| { validation_report - .push("Auxiliary data hash not found in transaction".to_string()); + .push(format!("Auxiliary data hash not found in transaction")); None })?; validate_aux_helper(original_aux, aux_data_hash, validation_report) } else { - validation_report.push("Auxiliary data not found in transaction".to_string()); + validation_report.push(format!("Auxiliary data not found in transaction")); None } } else { - validation_report - .push("Unsupported transaction era for auxiliary data validation".to_string()); + validation_report.push(format!( + "Unsupported transaction era for auxiliary data validation" + )); None } } @@ -346,9 +346,9 @@ impl Cip509 { for c509_cert in c509_certs { match c509_cert { C509Cert::C509CertInMetadatumReference(_) => { - validation_report.push( - "C509 metadatum reference is currently not supported".to_string(), - ); + validation_report.push(format!( + "C509 metadatum reference is currently not supported" + )); }, C509Cert::C509Certificate(c509) => { for exts in c509.tbs_cert().extensions().extensions() { @@ -369,7 +369,7 @@ impl Cip509 { }, _ => { validation_report.push( - "Failed to get the value of subject alternative name".to_string(), + format!("Failed to get the value of subject alternative name"), ); } } @@ -378,14 +378,14 @@ impl Cip509 { }, c509_certificate::extensions::alt_name::GeneralNamesOrText::Text(_) => { validation_report.push( - "Failed to find C509 general names in subject alternative name".to_string(), + format!("Failed to find C509 general names in subject alternative name"), ); } } }, _ => { validation_report.push( - "Failed to get C509 subject alternative name".to_string() + format!("Failed to get C509 subject alternative name") ); } } @@ -397,8 +397,9 @@ impl Cip509 { } } } else { - validation_report - .push("Unsupported transaction era for stake public key validation".to_string()); + validation_report.push(format!( + "Unsupported transaction era for stake public key validation" + )); return None; } @@ -436,8 +437,7 @@ impl Cip509 { ) -> Option { if let Some(payment_key) = role_data.payment_key { if payment_key == 0 { - validation_report - .push("Invalid payment reference key, 0 is not allowed".to_string()); + validation_report.push(format!("Invalid payment reference key, 0 is not allowed")); return None; } // CIP-0509 is expected to be in conway era @@ -471,10 +471,9 @@ impl Cip509 { }, }; } - validation_report.push( + validation_report.push(format!( "Role payment key reference index is not found in transaction outputs" - .to_string(), - ); + )); return None; } // Positive indicates reference to tx input @@ -488,17 +487,16 @@ impl Cip509 { }; // Check whether the index exists in transaction inputs if inputs.get(index).is_none() { - validation_report.push( + validation_report.push(format!( "Role payment key reference index is not found in transaction inputs" - .to_string(), - ); + )); return None; } Some(true) } else { - validation_report.push( - "Unsupported transaction era for stake payment key validation".to_string(), - ); + validation_report.push(format!( + "Unsupported transaction era for stake payment key validation" + )); None } } else { @@ -539,7 +537,7 @@ fn validate_payment_output_key_helper( // Compare the key hash and return the result return Some(compare_key_hash(&[key], witness, idx).is_ok()); } - validation_report.push("Failed to extract payment key hash from address".to_string()); + validation_report.push(format!("Failed to extract payment key hash from address")); None } From f0835026591fd7ff152a41adf21745459fd25887 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Mon, 18 Nov 2024 21:56:35 +0700 Subject: [PATCH 09/45] fix: rearrange utils Signed-off-by: bkioshn --- rust/rbac-registration/src/cardano/cip509/mod.rs | 3 ++- rust/rbac-registration/src/{ => cardano/cip509}/utils/cip19.rs | 0 rust/rbac-registration/src/cardano/cip509/utils/mod.rs | 3 +++ rust/rbac-registration/src/lib.rs | 2 +- rust/rbac-registration/src/utils/mod.rs | 1 - 5 files changed, 6 insertions(+), 3 deletions(-) rename rust/rbac-registration/src/{ => cardano/cip509}/utils/cip19.rs (100%) create mode 100644 rust/rbac-registration/src/cardano/cip509/utils/mod.rs diff --git a/rust/rbac-registration/src/cardano/cip509/mod.rs b/rust/rbac-registration/src/cardano/cip509/mod.rs index d763292aa49..730f6ab9f2d 100644 --- a/rust/rbac-registration/src/cardano/cip509/mod.rs +++ b/rust/rbac-registration/src/cardano/cip509/mod.rs @@ -6,6 +6,7 @@ pub mod rbac; pub mod x509_chunks; +pub(crate) mod utils; use c509_certificate::{general_names::general_name::GeneralNameValue, C509ExtensionType}; use der_parser::der::parse_der_sequence; @@ -22,12 +23,12 @@ use pallas::{ }; use rbac::{certs::C509Cert, role_data::RoleData}; use strum_macros::FromRepr; +use utils::cip19::{compare_key_hash, extract_cip19_hash, extract_key_hash}; use x509_cert::{der::Decode as _, ext::pkix::ID_CE_SUBJECT_ALT_NAME}; use x509_chunks::X509Chunks; use super::transaction::witness::TxWitness; use crate::utils::{ - cip19::{compare_key_hash, extract_cip19_hash, extract_key_hash}, decode_helper::{decode_bytes, decode_helper, decode_map_len}, general::{decode_utf8, decremented_index}, hashing::{blake2b_128, blake2b_256}, diff --git a/rust/rbac-registration/src/utils/cip19.rs b/rust/rbac-registration/src/cardano/cip509/utils/cip19.rs similarity index 100% rename from rust/rbac-registration/src/utils/cip19.rs rename to rust/rbac-registration/src/cardano/cip509/utils/cip19.rs diff --git a/rust/rbac-registration/src/cardano/cip509/utils/mod.rs b/rust/rbac-registration/src/cardano/cip509/utils/mod.rs new file mode 100644 index 00000000000..27d0e520556 --- /dev/null +++ b/rust/rbac-registration/src/cardano/cip509/utils/mod.rs @@ -0,0 +1,3 @@ +//! Utility functions for CIP-509 + +pub(crate) mod cip19; diff --git a/rust/rbac-registration/src/lib.rs b/rust/rbac-registration/src/lib.rs index c32ccedd42f..bc4ab0a3234 100644 --- a/rust/rbac-registration/src/lib.rs +++ b/rust/rbac-registration/src/lib.rs @@ -1,4 +1,4 @@ //! This crate provides functionalities for RBAC registration. pub mod cardano; -pub mod utils; +pub(crate) mod utils; \ No newline at end of file diff --git a/rust/rbac-registration/src/utils/mod.rs b/rust/rbac-registration/src/utils/mod.rs index 52c004e1a62..9baae379402 100644 --- a/rust/rbac-registration/src/utils/mod.rs +++ b/rust/rbac-registration/src/utils/mod.rs @@ -1,6 +1,5 @@ //! Utility functions for the RBAC registration module. -pub mod cip19; pub(crate) mod decode_helper; pub(crate) mod general; pub(crate) mod hashing; From 890689dc977fd044a6f0391065994d0c75684b05 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Tue, 19 Nov 2024 14:44:43 +0700 Subject: [PATCH 10/45] fix: cargo toml Signed-off-by: bkioshn --- rust/Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 0a2fe3fbd51..bbd5a688415 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -7,7 +7,8 @@ members = [ "cbork", "cbork-abnf-parser", "cbork-cddl-parser", - "catalyst-voting", "jormungandr-vote-tx", "rbac-registration", + "catalyst-voting", + "jormungandr-vote-tx", "rbac-registration", ] From b92ecd45110c3c450450fb200b2b5e7b70eb483a Mon Sep 17 00:00:00 2001 From: bkioshn Date: Mon, 25 Nov 2024 10:59:21 +0700 Subject: [PATCH 11/45] fix: add new type + comment Signed-off-by: bkioshn --- .../src/cardano/cip509/mod.rs | 109 +++++++++++++++--- .../src/cardano/cip509/rbac/certs.rs | 17 +-- .../src/cardano/cip509/rbac/mod.rs | 20 +++- .../src/cardano/cip509/rbac/pub_key.rs | 27 +++-- .../src/cardano/cip509/rbac/role_data.rs | 4 +- .../src/cardano/cip509/x509_chunks.rs | 2 +- 6 files changed, 136 insertions(+), 43 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/mod.rs b/rust/rbac-registration/src/cardano/cip509/mod.rs index 730f6ab9f2d..4b01f3abc56 100644 --- a/rust/rbac-registration/src/cardano/cip509/mod.rs +++ b/rust/rbac-registration/src/cardano/cip509/mod.rs @@ -5,8 +5,8 @@ // cspell: words pkix pub mod rbac; -pub mod x509_chunks; pub(crate) mod utils; +pub mod x509_chunks; use c509_certificate::{general_names::general_name::GeneralNameValue, C509ExtensionType}; use der_parser::der::parse_der_sequence; @@ -45,17 +45,89 @@ pub(crate) const URI: u8 = 134; #[derive(Debug, PartialEq, Clone, Default)] pub struct Cip509 { /// `UUIDv4` Purpose . - pub purpose: [u8; 16], // (bytes .size 16) + pub purpose: UuidV4, // (bytes .size 16) /// Transaction inputs hash. - pub txn_inputs_hash: [u8; 16], // bytes .size 16 + pub txn_inputs_hash: TxInputHash, // bytes .size 16 /// Optional previous transaction ID. - pub prv_tx_id: Option<[u8; 32]>, // bytes .size 32 + pub prv_tx_id: Option, // bytes .size 32 /// x509 chunks. pub x509_chunks: X509Chunks, // chunk_type => [ + x509_chunk ] /// Validation signature. pub validation_signature: Vec, // bytes size (1..64) } +/// UUIDv4 representing in 16 bytes. +#[derive(Debug, PartialEq, Clone, Default)] +pub struct UuidV4([u8; 16]); + +impl From<[u8; 16]> for UuidV4 { + fn from(bytes: [u8; 16]) -> Self { + UuidV4(bytes) + } +} + +impl TryFrom> for UuidV4 { + type Error = &'static str; + + fn try_from(vec: Vec) -> Result { + if vec.len() == 16 { + let mut array = [0u8; 16]; + array.copy_from_slice(&vec); + Ok(UuidV4(array)) + } else { + Err("Input Vec must be exactly 16 bytes") + } + } +} + +/// Transaction hash representing in 32 bytes. +#[derive(Debug, PartialEq, Clone, Default)] +pub struct TxHash([u8; 32]); + +impl From<[u8; 32]> for TxHash { + fn from(bytes: [u8; 32]) -> Self { + TxHash(bytes) + } +} + +impl TryFrom> for TxHash { + type Error = &'static str; + + fn try_from(vec: Vec) -> Result { + if vec.len() == 32 { + let mut array = [0u8; 32]; + array.copy_from_slice(&vec); + Ok(TxHash(array)) + } else { + Err("Input Vec must be exactly 32 bytes") + } + } +} + +/// Transaction input hash representing in 16 bytes. +#[derive(Debug, PartialEq, Clone, Default)] +pub struct TxInputHash([u8; 16]); + +impl From<[u8; 16]> for TxInputHash { + fn from(bytes: [u8; 16]) -> Self { + TxInputHash(bytes) + } +} + +impl TryFrom> for TxInputHash { + type Error = &'static str; + + fn try_from(vec: Vec) -> Result { + if vec.len() == 16 { + let mut array = [0u8; 16]; + array.copy_from_slice(&vec); + Ok(TxInputHash(array)) + } else { + Err("Input Vec must be exactly 16 bytes") + } + } +} + /// Enum of CIP509 metadatum with its associated unsigned integer value. #[allow(clippy::module_name_repetitions)] #[derive(FromRepr, Debug, PartialEq)] @@ -83,25 +155,23 @@ impl Decode<'_, ()> for Cip509 { let _: u8 = decode_helper(d, "CIP509", ctx)?; match key { Cip509IntIdentifier::Purpose => { - cip509_metadatum.purpose = decode_bytes(d, "CIP509 purpose")? - .try_into() - .map_err(|_| decode::Error::message("Invalid data size of Purpose"))?; + cip509_metadatum.purpose = + UuidV4::try_from(decode_bytes(d, "CIP509 purpose")?).map_err(|_| { + decode::Error::message("Invalid data size of Purpose") + })?; }, Cip509IntIdentifier::TxInputsHash => { cip509_metadatum.txn_inputs_hash = - decode_bytes(d, "CIP509 txn inputs hash")? - .try_into() + TxInputHash::try_from(decode_bytes(d, "CIP509 txn inputs hash")?) .map_err(|_| { decode::Error::message("Invalid data size of TxInputsHash") })?; }, Cip509IntIdentifier::PreviousTxId => { cip509_metadatum.prv_tx_id = Some( - decode_bytes(d, "CIP509 previous tx ID")? - .try_into() - .map_err(|_| { - decode::Error::message("Invalid data size of PreviousTxId") - })?, + TxHash::try_from(decode_bytes(d, "CIP509 previous tx ID")?).map_err( + |_| decode::Error::message("Invalid data size of PreviousTxId"), + )?, ); }, Cip509IntIdentifier::ValidationSignature => { @@ -213,7 +283,7 @@ impl Cip509 { return None; }, }; - Some(inputs_hash == self.txn_inputs_hash) + Some(TxInputHash(inputs_hash) == self.txn_inputs_hash) } else { validation_report.push(format!( "Unsupported transaction era for transaction inputs hash validation" @@ -291,7 +361,8 @@ impl Cip509 { match parse_der_sequence(san_ext.extn_value.as_bytes()) { Ok((_, parsed_seq)) => { for data in parsed_seq.ref_iter() { - // Check for context-specific primitive type with tag number + // Check for context-specific primitive type with tag + // number // 6 (raw_tag 134) if data.header.raw_tag() == Some(&[URI]) { match data.content.as_slice() { @@ -605,9 +676,9 @@ mod tests { .unwrap(); let validation_signature = hex::decode("e0427f23196c17cf13f030595335343030c11d914bc7a84b56af7040930af4110fd4ca29b0bc0e83789adb8668ea2ef28c1dd10dc1fd35ea6ae8c06ee769540d").unwrap(); - assert_eq!(decoded_cip509.purpose, purpose); - assert_eq!(decoded_cip509.txn_inputs_hash, txn_inputs_hash); - assert_eq!(decoded_cip509.prv_tx_id, Some(prv_tx_id)); + assert_eq!(decoded_cip509.purpose, UuidV4(purpose)); + assert_eq!(decoded_cip509.txn_inputs_hash, TxInputHash(txn_inputs_hash)); + assert_eq!(decoded_cip509.prv_tx_id, Some(TxHash(prv_tx_id))); assert_eq!(decoded_cip509.validation_signature, validation_signature); } diff --git a/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs b/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs index 4a3598c67d5..39023aa53d6 100644 --- a/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs +++ b/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs @@ -4,9 +4,8 @@ use c509_certificate::c509::C509; use minicbor::{decode, Decode, Decoder}; use x509_cert::{der::Decode as x509Decode, Certificate}; -use crate::utils::decode_helper::{decode_array_len, decode_bytes, decode_helper, decode_tag}; - use super::tag::KeyTag; +use crate::utils::decode_helper::{decode_array_len, decode_bytes, decode_helper, decode_tag}; // ------------------x509------------------------ @@ -94,7 +93,7 @@ impl Decode<'_, ()> for C509Cert { } } -/// A struct of c509 certificate in metadatum reference. +/// C509 certificate in metadatum reference. #[derive(Debug, PartialEq, Clone)] pub struct C509CertInMetadatumReference { /// Transaction output field. @@ -118,11 +117,13 @@ impl Decode<'_, ()> for C509CertInMetadatumReference { arr.map(Some) }, minicbor::data::Type::Null => Ok(None), - _ => Ok(Some(vec![decode_helper( - d, - "C509CertInMetadatumReference", - ctx, - )?])), + _ => { + Ok(Some(vec![decode_helper( + d, + "C509CertInMetadatumReference", + ctx, + )?])) + }, }?; Ok(Self { txn_output_field, diff --git a/rust/rbac-registration/src/cardano/cip509/rbac/mod.rs b/rust/rbac-registration/src/cardano/cip509/rbac/mod.rs index 8d649b9d453..151110dff3b 100644 --- a/rust/rbac-registration/src/cardano/cip509/rbac/mod.rs +++ b/rust/rbac-registration/src/cardano/cip509/rbac/mod.rs @@ -19,7 +19,7 @@ use crate::utils::decode_helper::{ decode_any, decode_array_len, decode_bytes, decode_helper, decode_map_len, }; -/// Struct of Cip509 RBAC metadata. +/// Cip509 RBAC metadata. #[derive(Debug, PartialEq, Clone, Default)] pub struct Cip509RbacMetadata { /// Optional list of x509 certificates. @@ -30,7 +30,7 @@ pub struct Cip509RbacMetadata { /// Optional list of Public keys. pub pub_keys: Option>, /// Optional list of revocation list. - pub revocation_list: Option>, + pub revocation_list: Option>, /// Optional list of role data. pub role_set: Option>, /// Optional map of purpose key data. @@ -38,6 +38,16 @@ pub struct Cip509RbacMetadata { pub purpose_key_data: HashMap>, } +/// Certificate key hash use in revocation list. +#[derive(Debug, PartialEq, Clone, Default)] +pub struct CertKeyHash([u8; 16]); + +impl From<[u8; 16]> for CertKeyHash { + fn from(bytes: [u8; 16]) -> Self { + CertKeyHash(bytes) + } +} + /// The first valid purpose key. const FIRST_PURPOSE_KEY: u16 = 200; /// The last valid purpose key. @@ -88,7 +98,7 @@ impl Cip509RbacMetadata { } /// Set the revocation list. - fn set_revocation_list(&mut self, revocation_list: Vec<[u8; 16]>) { + fn set_revocation_list(&mut self, revocation_list: Vec) { self.revocation_list = Some(revocation_list); } @@ -154,7 +164,7 @@ where T: Decode<'b, ()> { } /// Decode an array of revocation list. -fn decode_revocation_list(d: &mut Decoder) -> Result, decode::Error> { +fn decode_revocation_list(d: &mut Decoder) -> Result, decode::Error> { let len = decode_array_len(d, "revocation list Cip509RbacMetadata")?; let mut revocation_list = Vec::with_capacity(usize::try_from(len).map_err(decode::Error::message)?); @@ -162,7 +172,7 @@ fn decode_revocation_list(d: &mut Decoder) -> Result, decode::Erro let arr: [u8; 16] = decode_bytes(d, "revocation list Cip509RbacMetadata")? .try_into() .map_err(|_| decode::Error::message("Invalid revocation list size"))?; - revocation_list.push(arr); + revocation_list.push(CertKeyHash::from(arr)); } Ok(revocation_list) } diff --git a/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs b/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs index 4842ef6852a..59aaf97d4f9 100644 --- a/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs +++ b/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs @@ -2,9 +2,8 @@ use minicbor::{decode, Decode, Decoder}; -use crate::utils::decode_helper::{decode_bytes, decode_tag}; - use super::tag::KeyTag; +use crate::utils::decode_helper::{decode_bytes, decode_tag}; /// Enum of possible public key type. #[derive(Debug, PartialEq, Clone, Default)] @@ -14,8 +13,18 @@ pub enum SimplePublicKeyType { Undefined, /// Deleted indicates the key is deleted. Deleted, - /// Ed25519 key. - Ed25519([u8; 32]), + /// Ed25519 public key. + Ed25519(Ed25519PublicKey), +} + +/// 32 bytes Ed25519 public key. +#[derive(Debug, PartialEq, Clone, Default)] +pub struct Ed25519PublicKey([u8; 32]); + +impl From<[u8; 32]> for Ed25519PublicKey { + fn from(bytes: [u8; 32]) -> Self { + Ed25519PublicKey(bytes) + } } impl Decode<'_, ()> for SimplePublicKeyType { @@ -30,7 +39,7 @@ impl Decode<'_, ()> for SimplePublicKeyType { let mut ed25519 = [0u8; 32]; if bytes.len() == 32 { ed25519.copy_from_slice(&bytes); - Ok(Self::Ed25519(ed25519)) + Ok(Self::Ed25519(Ed25519PublicKey(ed25519))) } else { Err(decode::Error::message("Invalid length for Ed25519 key")) } @@ -39,9 +48,11 @@ impl Decode<'_, ()> for SimplePublicKeyType { } }, minicbor::data::Type::Undefined => Ok(Self::Undefined), - _ => Err(decode::Error::message( - "Invalid datatype for SimplePublicKeyType", - )), + _ => { + Err(decode::Error::message( + "Invalid datatype for SimplePublicKeyType", + )) + }, } } } diff --git a/rust/rbac-registration/src/cardano/cip509/rbac/role_data.rs b/rust/rbac-registration/src/cardano/cip509/rbac/role_data.rs index ca637890d31..f07b1be9d94 100644 --- a/rust/rbac-registration/src/cardano/cip509/rbac/role_data.rs +++ b/rust/rbac-registration/src/cardano/cip509/rbac/role_data.rs @@ -8,7 +8,7 @@ use strum_macros::FromRepr; use super::{decode_any, decode_map_len, Cip509RbacMetadataInt}; use crate::utils::decode_helper::{decode_array_len, decode_helper}; -/// Struct of role data. +/// Role data. #[derive(Debug, PartialEq, Clone, Default)] pub struct RoleData { /// Role number. @@ -88,7 +88,7 @@ impl Decode<'_, ()> for RoleData { Ok(role_data) } } -/// Struct of key local reference. +/// Local key reference. #[derive(Debug, PartialEq, Clone)] pub struct KeyLocalRef { /// Local reference. diff --git a/rust/rbac-registration/src/cardano/cip509/x509_chunks.rs b/rust/rbac-registration/src/cardano/cip509/x509_chunks.rs index 55a0642e913..69797559fea 100644 --- a/rust/rbac-registration/src/cardano/cip509/x509_chunks.rs +++ b/rust/rbac-registration/src/cardano/cip509/x509_chunks.rs @@ -21,7 +21,7 @@ pub enum CompressionAlgorithm { Zstd = 12, } -/// Struct of x509 chunks. +/// x509 chunks. #[derive(Debug, PartialEq, Clone, Default)] pub struct X509Chunks(pub Cip509RbacMetadata); From c04eca4230a0316f261f2a35d783f100c0150692 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Mon, 25 Nov 2024 11:21:33 +0700 Subject: [PATCH 12/45] fix: linter Signed-off-by: bkioshn --- .../src/cardano/cip509/mod.rs | 257 +++++++++--------- rust/rbac-registration/src/utils/hashing.rs | 2 - 2 files changed, 132 insertions(+), 127 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/mod.rs b/rust/rbac-registration/src/cardano/cip509/mod.rs index 4b01f3abc56..68fbf136991 100644 --- a/rust/rbac-registration/src/cardano/cip509/mod.rs +++ b/rust/rbac-registration/src/cardano/cip509/mod.rs @@ -56,7 +56,7 @@ pub struct Cip509 { pub validation_signature: Vec, // bytes size (1..64) } -/// UUIDv4 representing in 16 bytes. +/// `UUIDv4` representing in 16 bytes. #[derive(Debug, PartialEq, Clone, Default)] pub struct UuidV4([u8; 16]); @@ -227,7 +227,7 @@ impl Cip509 { let tx_input_validate = self .validate_txn_inputs_hash(txn, validation_report) .unwrap_or(false); - let aux_validate = self.validate_aux(txn, validation_report).unwrap_or(false); + let aux_validate = validate_aux(txn, validation_report).unwrap_or(false); let mut stake_key_validate = true; let mut payment_key_validate = true; // Validate the role 0 @@ -238,9 +238,9 @@ impl Cip509 { stake_key_validate = self .validate_stake_public_key(txn, txn_idx, validation_report) .unwrap_or(false); - payment_key_validate = self - .validate_payment_key(txn, txn_idx, role, validation_report) - .unwrap_or(false); + payment_key_validate = + validate_payment_key(txn, txn_idx, role, validation_report) + .unwrap_or(false); } } } @@ -285,37 +285,9 @@ impl Cip509 { }; Some(TxInputHash(inputs_hash) == self.txn_inputs_hash) } else { - validation_report.push(format!( - "Unsupported transaction era for transaction inputs hash validation" - )); - None - } - } - - /// Validate the auxiliary data with the auxiliary data hash in the transaction. - fn validate_aux(&self, txn: &MultiEraTx, validation_report: &mut Vec) -> Option { - // CIP-0509 is expected to be in conway era - if let MultiEraTx::Conway(tx) = txn { - if let pallas::codec::utils::Nullable::Some(a) = &tx.auxiliary_data { - let original_aux = a.raw_cbor(); - let aux_data_hash = - tx.transaction_body - .auxiliary_data_hash - .as_ref() - .or_else(|| { - validation_report - .push(format!("Auxiliary data hash not found in transaction")); - None - })?; - validate_aux_helper(original_aux, aux_data_hash, validation_report) - } else { - validation_report.push(format!("Auxiliary data not found in transaction")); - None - } - } else { - validation_report.push(format!( - "Unsupported transaction era for auxiliary data validation" - )); + validation_report.push( + "Unsupported transaction era for transaction inputs hash validation".to_string(), + ); None } } @@ -418,9 +390,9 @@ impl Cip509 { for c509_cert in c509_certs { match c509_cert { C509Cert::C509CertInMetadatumReference(_) => { - validation_report.push(format!( - "C509 metadatum reference is currently not supported" - )); + validation_report.push( + "C509 metadatum reference is currently not supported".to_string(), + ); }, C509Cert::C509Certificate(c509) => { for exts in c509.tbs_cert().extensions().extensions() { @@ -441,7 +413,7 @@ impl Cip509 { }, _ => { validation_report.push( - format!("Failed to get the value of subject alternative name"), + "Failed to get the value of subject alternative name".to_string(), ); } } @@ -450,14 +422,14 @@ impl Cip509 { }, c509_certificate::extensions::alt_name::GeneralNamesOrText::Text(_) => { validation_report.push( - format!("Failed to find C509 general names in subject alternative name"), + "Failed to find C509 general names in subject alternative name".to_string(), ); } } }, _ => { validation_report.push( - format!("Failed to get C509 subject alternative name") + "Failed to get C509 subject alternative name".to_string() ); } } @@ -469,9 +441,8 @@ impl Cip509 { } } } else { - validation_report.push(format!( - "Unsupported transaction era for stake public key validation" - )); + validation_report + .push("Unsupported transaction era for stake public key validation".to_string()); return None; } @@ -501,79 +472,32 @@ impl Cip509 { .is_ok(), ) } +} - /// Validate the payment key - fn validate_payment_key( - &self, txn: &MultiEraTx, txn_idx: usize, role_data: &RoleData, - validation_report: &mut Vec, - ) -> Option { - if let Some(payment_key) = role_data.payment_key { - if payment_key == 0 { - validation_report.push(format!("Invalid payment reference key, 0 is not allowed")); - return None; - } - // CIP-0509 is expected to be in conway era - if let MultiEraTx::Conway(tx) = txn { - // Negative indicates reference to tx output - if payment_key < 0 { - let index = match decremented_index(payment_key.abs()) { - Ok(value) => value, - Err(e) => { - validation_report - .push(format!("Failed to get index of payment key: {e}")); - return None; - }, - }; - let outputs = tx.transaction_body.outputs.clone(); - let witness = match TxWitness::new(&[txn.clone()]) { - Ok(witnesses) => witnesses, - Err(e) => { - validation_report.push(format!("Failed to create TxWitness: {e}")); - return None; - }, - }; - - if let Some(output) = outputs.get(index) { - match output { - pallas::ledger::primitives::conway::PseudoTransactionOutput::Legacy(o) => { - return validate_payment_output_key_helper(&o.address.to_vec(), validation_report, &witness, txn_idx); - }, - pallas::ledger::primitives::conway::PseudoTransactionOutput::PostAlonzo(o) => { - return validate_payment_output_key_helper(&o.address.to_vec(), validation_report, &witness, txn_idx); - }, - }; - } - validation_report.push(format!( - "Role payment key reference index is not found in transaction outputs" - )); - return None; - } - // Positive indicates reference to tx input - let inputs = &tx.transaction_body.inputs; - let index = match decremented_index(payment_key) { - Ok(value) => value, - Err(e) => { - validation_report.push(format!("Failed to get index of payment key: {e}")); - return None; - }, - }; - // Check whether the index exists in transaction inputs - if inputs.get(index).is_none() { - validation_report.push(format!( - "Role payment key reference index is not found in transaction inputs" - )); - return None; - } - Some(true) - } else { - validation_report.push(format!( - "Unsupported transaction era for stake payment key validation" - )); - None - } +/// Validate the auxiliary data with the auxiliary data hash in the transaction. +fn validate_aux(txn: &MultiEraTx, validation_report: &mut Vec) -> Option { + // CIP-0509 is expected to be in conway era + if let MultiEraTx::Conway(tx) = txn { + if let pallas::codec::utils::Nullable::Some(a) = &tx.auxiliary_data { + let original_aux = a.raw_cbor(); + let aux_data_hash = tx + .transaction_body + .auxiliary_data_hash + .as_ref() + .or_else(|| { + validation_report + .push("Auxiliary data hash not found in transaction".to_string()); + None + })?; + validate_aux_helper(original_aux, aux_data_hash, validation_report) } else { - Some(false) + validation_report.push("Auxiliary data not found in transaction".to_string()); + None } + } else { + validation_report + .push("Unsupported transaction era for auxiliary data validation".to_string()); + None } } @@ -593,6 +517,91 @@ fn validate_aux_helper( } } +/// Validate the payment key +fn validate_payment_key( + txn: &MultiEraTx, txn_idx: usize, role_data: &RoleData, validation_report: &mut Vec, +) -> Option { + if let Some(payment_key) = role_data.payment_key { + if payment_key == 0 { + validation_report.push("Invalid payment reference key, 0 is not allowed".to_string()); + return None; + } + // CIP-0509 is expected to be in conway era + if let MultiEraTx::Conway(tx) = txn { + // Negative indicates reference to tx output + if payment_key < 0 { + let index = match decremented_index(payment_key.abs()) { + Ok(value) => value, + Err(e) => { + validation_report.push(format!("Failed to get index of payment key: {e}")); + return None; + }, + }; + let outputs = tx.transaction_body.outputs.clone(); + let witness = match TxWitness::new(&[txn.clone()]) { + Ok(witnesses) => witnesses, + Err(e) => { + validation_report.push(format!("Failed to create TxWitness: {e}")); + return None; + }, + }; + + if let Some(output) = outputs.get(index) { + match output { + pallas::ledger::primitives::conway::PseudoTransactionOutput::Legacy(o) => { + return validate_payment_output_key_helper( + &o.address.to_vec(), + validation_report, + &witness, + txn_idx, + ); + }, + pallas::ledger::primitives::conway::PseudoTransactionOutput::PostAlonzo( + o, + ) => { + return validate_payment_output_key_helper( + &o.address.to_vec(), + validation_report, + &witness, + txn_idx, + ); + }, + }; + } + validation_report.push( + "Role payment key reference index is not found in transaction outputs" + .to_string(), + ); + return None; + } + // Positive indicates reference to tx input + let inputs = &tx.transaction_body.inputs; + let index = match decremented_index(payment_key) { + Ok(value) => value, + Err(e) => { + validation_report.push(format!("Failed to get index of payment key: {e}")); + return None; + }, + }; + // Check whether the index exists in transaction inputs + if inputs.get(index).is_none() { + validation_report.push( + "Role payment key reference index is not found in transaction inputs" + .to_string(), + ); + return None; + } + Some(true) + } else { + validation_report + .push("Unsupported transaction era for stake payment key validation".to_string()); + None + } + } else { + Some(false) + } +} + /// Helper function for validating payment output key. fn validate_payment_output_key_helper( output_address: &[u8], validation_report: &mut Vec, witness: &TxWitness, txn_idx: usize, @@ -609,7 +618,7 @@ fn validate_payment_output_key_helper( // Compare the key hash and return the result return Some(compare_key_hash(&[key], witness, idx).is_ok()); } - validation_report.push(format!("Failed to extract payment key hash from address")); + validation_report.push("Failed to extract payment key hash from address".to_string()); None } @@ -716,12 +725,12 @@ mod tests { .get(3) .expect("Failed to get transaction index"); - let aux_data = cip_509_aux_data(tx); + // let aux_data = cip_509_aux_data(tx); - let mut decoder = Decoder::new(aux_data.as_slice()); - let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); - cip509.validate_aux(tx, &mut validation_report); - assert!(cip509.validate_aux(tx, &mut validation_report).unwrap()); + // let mut decoder = Decoder::new(aux_data.as_slice()); + // let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); + validate_aux(tx, &mut validation_report); + assert!(validate_aux(tx, &mut validation_report).unwrap()); } #[test] @@ -767,9 +776,7 @@ mod tests { if let Some(role_set) = &cip509.x509_chunks.0.role_set { for role in role_set { if role.role_number == 0 { - assert!(cip509 - .validate_payment_key(tx, 0, role, &mut validation_report,) - .unwrap()); + assert!(validate_payment_key(tx, 0, role, &mut validation_report,).unwrap()); } } } @@ -798,7 +805,7 @@ mod tests { if role.role_number == 0 { println!( "{:?}", - cip509.validate_payment_key(tx, 0, role, &mut validation_report,) + validate_payment_key(tx, 0, role, &mut validation_report,) ); } } diff --git a/rust/rbac-registration/src/utils/hashing.rs b/rust/rbac-registration/src/utils/hashing.rs index 2425f40ad0b..2ccf2e636d3 100644 --- a/rust/rbac-registration/src/utils/hashing.rs +++ b/rust/rbac-registration/src/utils/hashing.rs @@ -3,7 +3,6 @@ use blake2b_simd::{self, Params}; /// Convert the given value to `blake2b_244` array. -#[allow(dead_code)] // Its OK if we don't use this general utility function. pub(crate) fn blake2b_244(value: &[u8]) -> anyhow::Result<[u8; 28]> { let h = Params::new().hash_length(28).hash(value); let b = h.as_bytes(); @@ -12,7 +11,6 @@ pub(crate) fn blake2b_244(value: &[u8]) -> anyhow::Result<[u8; 28]> { } /// Convert the given value to `blake2b_256` array. -#[allow(dead_code)] // Its OK if we don't use this general utility function. pub(crate) fn blake2b_256(value: &[u8]) -> anyhow::Result<[u8; 32]> { let h = Params::new().hash_length(32).hash(value); let b = h.as_bytes(); From b874c3e4761c5a5b95d4a7b53f55e05dd16f6ce3 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Mon, 25 Nov 2024 13:25:59 +0700 Subject: [PATCH 13/45] fix: smart pointer Signed-off-by: bkioshn --- .../src/cardano/cip509/rbac/certs.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs b/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs index 39023aa53d6..57eb5a81775 100644 --- a/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs +++ b/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs @@ -56,7 +56,7 @@ pub enum C509Cert { /// A c509 certificate in metadatum reference. C509CertInMetadatumReference(C509CertInMetadatumReference), /// A c509 certificate. - C509Certificate(C509), + C509Certificate(Box), } impl Decode<'_, ()> for C509Cert { @@ -85,7 +85,10 @@ impl Decode<'_, ()> for C509Cert { minicbor::data::Type::Bytes => { let c509 = decode_bytes(d, "C509Cert")?; let mut c509_d = Decoder::new(&c509); - Ok(Self::C509Certificate(C509::decode(&mut c509_d, ctx)?)) + Ok(Self::C509Certificate(Box::new(C509::decode( + &mut c509_d, + ctx, + )?))) }, minicbor::data::Type::Undefined => Ok(Self::Undefined), _ => Err(decode::Error::message("Invalid datatype for C509Cert")), @@ -117,13 +120,11 @@ impl Decode<'_, ()> for C509CertInMetadatumReference { arr.map(Some) }, minicbor::data::Type::Null => Ok(None), - _ => { - Ok(Some(vec![decode_helper( - d, - "C509CertInMetadatumReference", - ctx, - )?])) - }, + _ => Ok(Some(vec![decode_helper( + d, + "C509CertInMetadatumReference", + ctx, + )?])), }?; Ok(Self { txn_output_field, From 89f627aa2343da880c0834c6fe93d41d0bdd21b0 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Mon, 25 Nov 2024 13:28:30 +0700 Subject: [PATCH 14/45] chore: fix format Signed-off-by: bkioshn --- .../src/cardano/cip509/rbac/certs.rs | 12 +++++++----- rust/rbac-registration/src/lib.rs | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs b/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs index 57eb5a81775..08a3aa9b826 100644 --- a/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs +++ b/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs @@ -120,11 +120,13 @@ impl Decode<'_, ()> for C509CertInMetadatumReference { arr.map(Some) }, minicbor::data::Type::Null => Ok(None), - _ => Ok(Some(vec![decode_helper( - d, - "C509CertInMetadatumReference", - ctx, - )?])), + _ => { + Ok(Some(vec![decode_helper( + d, + "C509CertInMetadatumReference", + ctx, + )?])) + }, }?; Ok(Self { txn_output_field, diff --git a/rust/rbac-registration/src/lib.rs b/rust/rbac-registration/src/lib.rs index bc4ab0a3234..72fecd94b77 100644 --- a/rust/rbac-registration/src/lib.rs +++ b/rust/rbac-registration/src/lib.rs @@ -1,4 +1,4 @@ //! This crate provides functionalities for RBAC registration. pub mod cardano; -pub(crate) mod utils; \ No newline at end of file +pub(crate) mod utils; From c90e0ee2c4f09779b8f2a853ae321a9ddc61c9b9 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Mon, 25 Nov 2024 13:52:31 +0700 Subject: [PATCH 15/45] fix: cleanup cargo.toml Signed-off-by: bkioshn --- rust/rbac-registration/Cargo.toml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/rust/rbac-registration/Cargo.toml b/rust/rbac-registration/Cargo.toml index be690b80242..22d03698cb3 100644 --- a/rust/rbac-registration/Cargo.toml +++ b/rust/rbac-registration/Cargo.toml @@ -20,10 +20,8 @@ workspace = true [dependencies] hex = "0.4.3" anyhow = "1.0.89" -strum = "0.26.3" strum_macros = "0.26.4" regex = "1.11.0" -ed25519-dalek = { version = "2.1.1", features = ["pem"] } minicbor = { version = "0.25.1", features = ["alloc", "derive", "half"] } brotli = "7.0.0" zstd = "0.13.2" @@ -33,13 +31,6 @@ bech32 = "0.11.0" dashmap = "6.1.0" blake2b_simd = "1.0.2" tracing = "0.1.40" -serde = "1.0.210" -serde_json = "1.0.128" c509-certificate = { version = "0.0.3", git = "https://github.com/input-output-hk/catalyst-libs.git" , tag = "v0.0.3" } pallas = { version = "0.30.1", git = "https://github.com/input-output-hk/catalyst-pallas.git", rev = "9b5183c8b90b90fe2cc319d986e933e9518957b3" } -pallas-hardano = { version = "0.30.1", git = "https://github.com/input-output-hk/catalyst-pallas.git", rev = "9b5183c8b90b90fe2cc319d986e933e9518957b3" } -pallas-crypto = { version = "0.30.1", git = "https://github.com/input-output-hk/catalyst-pallas.git", rev = "9b5183c8b90b90fe2cc319d986e933e9518957b3" } - - -[dev-dependencies] From 2777246a836a5a55f98923544406530b2de4835a Mon Sep 17 00:00:00 2001 From: bkioshn Date: Mon, 25 Nov 2024 13:53:18 +0700 Subject: [PATCH 16/45] fix: add rbac-registration to earthly rust --- rust/Earthfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rust/Earthfile b/rust/Earthfile index 0f282bb5329..639e837f8b0 100644 --- a/rust/Earthfile +++ b/rust/Earthfile @@ -13,6 +13,7 @@ COPY_SRC: catalyst-voting vote-tx-v1 vote-tx-v2 \ cbork cbork-abnf-parser cbork-cddl-parser \ hermes-ipfs \ + rbac-registration \ immutable-ledger . # builder : Set up our target toolchains, and copy our files. @@ -54,7 +55,7 @@ build: --args1="--libs=c509-certificate --libs=cardano-chain-follower --libs=hermes-ipfs" \ --args2="--libs=cbork-cddl-parser --libs=cbork-abnf-parser" \ --args3="--libs=catalyst-voting --libs=vote-tx-v1 --libs=vote-tx-v2" \ - --args4="--bins=cbork/cbork" \ + --args4="--bins=cbork/cbork --libs=rbac-registration"\ --args5="--cov_report=$HOME/build/coverage-report.info" \ --output="release/[^\./]+" \ --junit="cat-libs.junit-report.xml" \ From 8fae2f02beed37f5d563c09f0264d38c59b4ce8f Mon Sep 17 00:00:00 2001 From: bkioshn Date: Mon, 25 Nov 2024 21:10:34 +0700 Subject: [PATCH 17/45] fix: move cip509 validation Signed-off-by: bkioshn --- .../src/cardano/cip509/mod.rs | 584 +--------------- .../src/cardano/cip509/validation.rs | 623 ++++++++++++++++++ 2 files changed, 633 insertions(+), 574 deletions(-) create mode 100644 rust/rbac-registration/src/cardano/cip509/validation.rs diff --git a/rust/rbac-registration/src/cardano/cip509/mod.rs b/rust/rbac-registration/src/cardano/cip509/mod.rs index 68fbf136991..377804684bf 100644 --- a/rust/rbac-registration/src/cardano/cip509/mod.rs +++ b/rust/rbac-registration/src/cardano/cip509/mod.rs @@ -6,25 +6,18 @@ pub mod rbac; pub(crate) mod utils; +pub(crate) mod validation; pub mod x509_chunks; -use c509_certificate::{general_names::general_name::GeneralNameValue, C509ExtensionType}; -use der_parser::der::parse_der_sequence; use minicbor::{ decode::{self}, Decode, Decoder, }; -use pallas::{ - codec::{ - minicbor::{Encode, Encoder}, - utils::Bytes, - }, - ledger::traverse::MultiEraTx, -}; -use rbac::{certs::C509Cert, role_data::RoleData}; +use pallas::ledger::traverse::MultiEraTx; use strum_macros::FromRepr; -use utils::cip19::{compare_key_hash, extract_cip19_hash, extract_key_hash}; -use x509_cert::{der::Decode as _, ext::pkix::ID_CE_SUBJECT_ALT_NAME}; +use validation::{ + validate_aux, validate_payment_key, validate_stake_public_key, validate_txn_inputs_hash, +}; use x509_chunks::X509Chunks; use super::transaction::witness::TxWitness; @@ -37,10 +30,6 @@ use crate::utils::{ /// CIP509 label. pub const LABEL: u64 = 509; -/// Context-specific primitive type with tag number 6 (`raw_tag` 134) for -/// uniform resource identifier (URI) in the subject alternative name extension. -pub(crate) const URI: u8 = 134; - /// CIP509. #[derive(Debug, PartialEq, Clone, Default)] pub struct Cip509 { @@ -224,9 +213,8 @@ impl Cip509 { pub fn validate( &self, txn: &MultiEraTx, txn_idx: usize, validation_report: &mut Vec, ) -> bool { - let tx_input_validate = self - .validate_txn_inputs_hash(txn, validation_report) - .unwrap_or(false); + let tx_input_validate = + validate_txn_inputs_hash(self, txn, validation_report).unwrap_or(false); let aux_validate = validate_aux(txn, validation_report).unwrap_or(false); let mut stake_key_validate = true; let mut payment_key_validate = true; @@ -235,9 +223,9 @@ impl Cip509 { // Validate only role 0 for role in role_set { if role.role_number == 0 { - stake_key_validate = self - .validate_stake_public_key(txn, txn_idx, validation_report) - .unwrap_or(false); + stake_key_validate = + validate_stake_public_key(self, txn, txn_idx, validation_report) + .unwrap_or(false); payment_key_validate = validate_payment_key(txn, txn_idx, role, validation_report) .unwrap_or(false); @@ -246,421 +234,12 @@ impl Cip509 { } tx_input_validate && aux_validate && stake_key_validate && payment_key_validate } - - /// Transaction inputs hash validation. - /// Must exist and match the hash of the transaction inputs. - fn validate_txn_inputs_hash( - &self, txn: &MultiEraTx, validation_report: &mut Vec, - ) -> Option { - let mut buffer = Vec::new(); - let mut e = Encoder::new(&mut buffer); - // CIP-0509 is expected to be in conway era - if let MultiEraTx::Conway(tx) = txn { - let inputs = tx.transaction_body.inputs.clone(); - if let Err(e) = e.array(inputs.len() as u64) { - validation_report.push(format!( - "Failed to encode array of transaction input in validate_txn_inputs_hash: {e}" - )); - return None; - } - for input in &inputs { - match input.encode(&mut e, &mut ()) { - Ok(()) => {}, - Err(e) => { - validation_report.push(format!( - "Failed to encode transaction input in validate_txn_inputs_hash {e}" - )); - return None; - }, - } - } - let inputs_hash = match blake2b_128(&buffer) { - Ok(hash) => hash, - Err(e) => { - validation_report.push(format!( - "Failed to hash transaction inputs in validate_txn_inputs_hash {e}" - )); - return None; - }, - }; - Some(TxInputHash(inputs_hash) == self.txn_inputs_hash) - } else { - validation_report.push( - "Unsupported transaction era for transaction inputs hash validation".to_string(), - ); - None - } - } - - /// Validate the stake public key in the certificate with witness set in transaction. - #[allow(clippy::too_many_lines)] - fn validate_stake_public_key( - &self, txn: &MultiEraTx, txn_idx: usize, validation_report: &mut Vec, - ) -> Option { - let mut pk_addrs = Vec::new(); - - // CIP-0509 is expected to be in conway era - if let MultiEraTx::Conway(_) = txn { - // X509 certificate - if let Some(x509_certs) = &self.x509_chunks.0.x509_certs { - for x509_cert in x509_certs { - match x509_cert { - rbac::certs::X509DerCert::X509Cert(cert) => { - // Attempt to decode the DER certificate - let der_cert = match x509_cert::Certificate::from_der(cert) { - Ok(cert) => cert, - Err(e) => { - validation_report.push(format!( - "Failed to decode x509 certificate DER: {e}" - )); - return None; - }, - }; - - // Find the Subject Alternative Name extension - let san_ext = - der_cert - .tbs_certificate - .extensions - .as_ref() - .and_then(|exts| { - exts.iter() - .find(|ext| ext.extn_id == ID_CE_SUBJECT_ALT_NAME) - }); - - // Subject Alternative Name extension if it exists - if let Some(san_ext) = san_ext { - match parse_der_sequence(san_ext.extn_value.as_bytes()) { - Ok((_, parsed_seq)) => { - for data in parsed_seq.ref_iter() { - // Check for context-specific primitive type with tag - // number - // 6 (raw_tag 134) - if data.header.raw_tag() == Some(&[URI]) { - match data.content.as_slice() { - Ok(content) => { - // Decode the UTF-8 string - let addr: String = match decode_utf8( - content, - ) { - Ok(addr) => addr, - Err(e) => { - validation_report.push(format!( - "Failed to decode UTF-8 string for context-specific primitive type with raw tag 134: {e}", - ), - ); - return None; - }, - }; - - // Extract the CIP19 hash and push into - // array - if let Some(h) = - extract_cip19_hash(&addr, Some("stake")) - { - pk_addrs.push(h); - } - }, - Err(e) => { - validation_report.push( - format!("Failed to process content for context-specific primitive type with raw tag 134: {e}")); - return None; - }, - } - } - } - }, - Err(e) => { - validation_report.push( - format!( - "Failed to parse DER sequence for Subject Alternative Name extension: {e}" - ) - ); - return None; - }, - } - } - }, - _ => continue, - } - } - } - // C509 Certificate - if let Some(c509_certs) = &self.x509_chunks.0.c509_certs { - for c509_cert in c509_certs { - match c509_cert { - C509Cert::C509CertInMetadatumReference(_) => { - validation_report.push( - "C509 metadatum reference is currently not supported".to_string(), - ); - }, - C509Cert::C509Certificate(c509) => { - for exts in c509.tbs_cert().extensions().extensions() { - if *exts.registered_oid().c509_oid().oid() - == C509ExtensionType::SubjectAlternativeName.oid() - { - match exts.value() { - c509_certificate::extensions::extension::ExtensionValue::AlternativeName(alt_name) => { - match alt_name.general_name() { - c509_certificate::extensions::alt_name::GeneralNamesOrText::GeneralNames(gn) => { - for name in gn.general_names() { - if name.gn_type() == &c509_certificate::general_names::general_name::GeneralNameTypeRegistry::UniformResourceIdentifier { - match name.gn_value() { - GeneralNameValue::Text(s) => { - if let Some(h) = extract_cip19_hash(s, Some("stake")) { - pk_addrs.push(h); - } - }, - _ => { - validation_report.push( - "Failed to get the value of subject alternative name".to_string(), - ); - } - } - } - } - }, - c509_certificate::extensions::alt_name::GeneralNamesOrText::Text(_) => { - validation_report.push( - "Failed to find C509 general names in subject alternative name".to_string(), - ); - } - } - }, - _ => { - validation_report.push( - "Failed to get C509 subject alternative name".to_string() - ); - } - } - } - } - }, - _ => continue, - } - } - } - } else { - validation_report - .push("Unsupported transaction era for stake public key validation".to_string()); - return None; - } - - // Create TxWitness - let witnesses = match TxWitness::new(&[txn.clone()]) { - Ok(witnesses) => witnesses, - Err(e) => { - validation_report.push(format!("Failed to create TxWitness: {e}")); - return None; - }, - }; - - let index = match u16::try_from(txn_idx) { - Ok(value) => value, - Err(e) => { - validation_report - .push(format!("Failed to convert transaction index to usize: {e}")); - return None; - }, - }; - Some( - compare_key_hash(&pk_addrs, &witnesses, index) - .map_err(|e| { - validation_report - .push(format!("Failed to compare public keys with witnesses {e}")); - }) - .is_ok(), - ) - } -} - -/// Validate the auxiliary data with the auxiliary data hash in the transaction. -fn validate_aux(txn: &MultiEraTx, validation_report: &mut Vec) -> Option { - // CIP-0509 is expected to be in conway era - if let MultiEraTx::Conway(tx) = txn { - if let pallas::codec::utils::Nullable::Some(a) = &tx.auxiliary_data { - let original_aux = a.raw_cbor(); - let aux_data_hash = tx - .transaction_body - .auxiliary_data_hash - .as_ref() - .or_else(|| { - validation_report - .push("Auxiliary data hash not found in transaction".to_string()); - None - })?; - validate_aux_helper(original_aux, aux_data_hash, validation_report) - } else { - validation_report.push("Auxiliary data not found in transaction".to_string()); - None - } - } else { - validation_report - .push("Unsupported transaction era for auxiliary data validation".to_string()); - None - } -} - -/// Helper function for auxiliary data validation. -fn validate_aux_helper( - original_aux: &[u8], aux_data_hash: &Bytes, validation_report: &mut Vec, -) -> Option { - // Compare the hash - match blake2b_256(original_aux) { - Ok(original_hash) => { - return Some(aux_data_hash.as_ref() == original_hash); - }, - Err(e) => { - validation_report.push(format!("Cannot hash auxiliary data {e}")); - None - }, - } -} - -/// Validate the payment key -fn validate_payment_key( - txn: &MultiEraTx, txn_idx: usize, role_data: &RoleData, validation_report: &mut Vec, -) -> Option { - if let Some(payment_key) = role_data.payment_key { - if payment_key == 0 { - validation_report.push("Invalid payment reference key, 0 is not allowed".to_string()); - return None; - } - // CIP-0509 is expected to be in conway era - if let MultiEraTx::Conway(tx) = txn { - // Negative indicates reference to tx output - if payment_key < 0 { - let index = match decremented_index(payment_key.abs()) { - Ok(value) => value, - Err(e) => { - validation_report.push(format!("Failed to get index of payment key: {e}")); - return None; - }, - }; - let outputs = tx.transaction_body.outputs.clone(); - let witness = match TxWitness::new(&[txn.clone()]) { - Ok(witnesses) => witnesses, - Err(e) => { - validation_report.push(format!("Failed to create TxWitness: {e}")); - return None; - }, - }; - - if let Some(output) = outputs.get(index) { - match output { - pallas::ledger::primitives::conway::PseudoTransactionOutput::Legacy(o) => { - return validate_payment_output_key_helper( - &o.address.to_vec(), - validation_report, - &witness, - txn_idx, - ); - }, - pallas::ledger::primitives::conway::PseudoTransactionOutput::PostAlonzo( - o, - ) => { - return validate_payment_output_key_helper( - &o.address.to_vec(), - validation_report, - &witness, - txn_idx, - ); - }, - }; - } - validation_report.push( - "Role payment key reference index is not found in transaction outputs" - .to_string(), - ); - return None; - } - // Positive indicates reference to tx input - let inputs = &tx.transaction_body.inputs; - let index = match decremented_index(payment_key) { - Ok(value) => value, - Err(e) => { - validation_report.push(format!("Failed to get index of payment key: {e}")); - return None; - }, - }; - // Check whether the index exists in transaction inputs - if inputs.get(index).is_none() { - validation_report.push( - "Role payment key reference index is not found in transaction inputs" - .to_string(), - ); - return None; - } - Some(true) - } else { - validation_report - .push("Unsupported transaction era for stake payment key validation".to_string()); - None - } - } else { - Some(false) - } -} - -/// Helper function for validating payment output key. -fn validate_payment_output_key_helper( - output_address: &[u8], validation_report: &mut Vec, witness: &TxWitness, txn_idx: usize, -) -> Option { - let idx = match u16::try_from(txn_idx) { - Ok(value) => value, - Err(e) => { - validation_report.push(format!("Transaction index conversion failed: {e}")); - return None; - }, - }; - // Extract the key hash from the output address - if let Some(key) = extract_key_hash(output_address) { - // Compare the key hash and return the result - return Some(compare_key_hash(&[key], witness, idx).is_ok()); - } - validation_report.push("Failed to extract payment key hash from address".to_string()); - None } #[cfg(test)] mod tests { use super::*; - use crate::cardano::transaction::raw_aux_data::RawAuxData; - fn conway_1() -> Vec { - hex::decode(include_str!("../../test_data/cardano/conway_1.block")) - .expect("Failed to decode hex block.") - } - - fn conway_2() -> Vec { - hex::decode(include_str!("../../test_data/cardano/conway_2.block")) - .expect("Failed to decode hex block.") - } - - fn conway_3() -> Vec { - hex::decode(include_str!("../../test_data/cardano/conway_3.block")) - .expect("Failed to decode hex block.") - } - - fn cip_509_aux_data(tx: &MultiEraTx<'_>) -> Vec { - let raw_auxiliary_data = tx - .as_conway() - .unwrap() - .clone() - .auxiliary_data - .map(|aux| aux.raw_cbor()); - - let raw_cbor_data = match raw_auxiliary_data { - pallas::codec::utils::Nullable::Some(data) => Ok(data), - _ => Err("Auxiliary data not found"), - }; - - let auxiliary_data = RawAuxData::new(raw_cbor_data.expect("Failed to get raw cbor data")); - auxiliary_data - .get_metadata(LABEL) - .expect("Failed to get metadata") - .to_vec() - } #[test] fn test_decode_cip509() { @@ -690,147 +269,4 @@ mod tests { assert_eq!(decoded_cip509.prv_tx_id, Some(TxHash(prv_tx_id))); assert_eq!(decoded_cip509.validation_signature, validation_signature); } - - #[test] - fn test_validate_txn_inputs_hash() { - let mut validation_report = Vec::new(); - let conway_block_data = conway_1(); - let multi_era_block = pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data) - .expect("Failed to decode MultiEraBlock"); - - let transactions = multi_era_block.txs(); - // Forth transaction of this test data contains the CIP509 auxiliary data - let tx = transactions - .get(3) - .expect("Failed to get transaction index"); - let aux_data = cip_509_aux_data(tx); - - let mut decoder = Decoder::new(aux_data.as_slice()); - let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); - assert!(cip509 - .validate_txn_inputs_hash(tx, &mut validation_report) - .unwrap()); - } - - #[test] - fn test_validate_aux() { - let mut validation_report = Vec::new(); - let conway_block_data = conway_1(); - let multi_era_block = pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data) - .expect("Failed to decode MultiEraBlock"); - - let transactions = multi_era_block.txs(); - // Forth transaction of this test data contains the CIP509 auxiliary data - let tx = transactions - .get(3) - .expect("Failed to get transaction index"); - - // let aux_data = cip_509_aux_data(tx); - - // let mut decoder = Decoder::new(aux_data.as_slice()); - // let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); - validate_aux(tx, &mut validation_report); - assert!(validate_aux(tx, &mut validation_report).unwrap()); - } - - #[test] - fn test_validate_public_key_success() { - let mut validation_report = Vec::new(); - let conway_block_data = conway_1(); - let multi_era_block = pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data) - .expect("Failed to decode MultiEraBlock"); - - let transactions = multi_era_block.txs(); - // Forth transaction of this test data contains the CIP509 auxiliary data - let tx = transactions - .get(3) - .expect("Failed to get transaction index"); - - let aux_data = cip_509_aux_data(tx); - - let mut decoder = Decoder::new(aux_data.as_slice()); - let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); - assert!(cip509 - .validate_stake_public_key(tx, 0, &mut validation_report) - .unwrap()); - } - - #[test] - fn test_validate_payment_key_success_positive_ref() { - let mut validation_report = Vec::new(); - let conway_block_data = conway_1(); - let multi_era_block = pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data) - .expect("Failed to decode MultiEraBlock"); - - let transactions = multi_era_block.txs(); - // Forth transaction of this test data contains the CIP509 auxiliary data - let tx = transactions - .get(3) - .expect("Failed to get transaction index"); - - let aux_data = cip_509_aux_data(tx); - - let mut decoder = Decoder::new(aux_data.as_slice()); - let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); - - if let Some(role_set) = &cip509.x509_chunks.0.role_set { - for role in role_set { - if role.role_number == 0 { - assert!(validate_payment_key(tx, 0, role, &mut validation_report,).unwrap()); - } - } - } - } - - #[test] - fn test_validate_payment_key_success_negative_ref() { - let mut validation_report = Vec::new(); - let conway_block_data = conway_3(); - let multi_era_block = pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data) - .expect("Failed to decode MultiEraBlock"); - - let transactions = multi_era_block.txs(); - // First transaction of this test data contains the CIP509 auxiliary data - let tx = transactions - .first() - .expect("Failed to get transaction index"); - - let aux_data = cip_509_aux_data(tx); - - let mut decoder = Decoder::new(aux_data.as_slice()); - let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); - - if let Some(role_set) = &cip509.x509_chunks.0.role_set { - for role in role_set { - if role.role_number == 0 { - println!( - "{:?}", - validate_payment_key(tx, 0, role, &mut validation_report,) - ); - } - } - } - } - - #[test] - fn test_validate_public_key_fail() { - let mut validation_report = Vec::new(); - let conway_block_data = conway_2(); - let multi_era_block = pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data) - .expect("Failed to decode MultiEraBlock"); - - let transactions = multi_era_block.txs(); - // Forth transaction of this test data contains the CIP509 auxiliary data - let tx = transactions - .get(3) - .expect("Failed to get transaction index"); - - let aux_data = cip_509_aux_data(tx); - - let mut decoder = Decoder::new(aux_data.as_slice()); - let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); - assert!(!cip509 - .validate_stake_public_key(tx, 0, &mut validation_report) - .unwrap()); - } } diff --git a/rust/rbac-registration/src/cardano/cip509/validation.rs b/rust/rbac-registration/src/cardano/cip509/validation.rs new file mode 100644 index 00000000000..bc2ea556f85 --- /dev/null +++ b/rust/rbac-registration/src/cardano/cip509/validation.rs @@ -0,0 +1,623 @@ +//! Basic validation for CIP-0509 +//! The validation include the following: +//! * Hashing the transaction inputs within the transaction should match the +//! txn-inputs-hash in CIP-0509 data. +//! * Auxiliary data hash within the transaction should match the hash of the auxiliary +//! data itself. +//! * Public key validation for role 0 where public key extracted from x509 and c509 +//! subject alternative name should match one of the witness in witness set within the +//! transaction. +//! * Payment key reference validation for role 0 where the reference should be either +//! 1. Negative index reference - reference to transaction output in transaction: +//! should match some of the key within witness set. +//! 2. Positive index reference - reference to the transaction input in transaction: +//! only check whether the index exist within the transaction inputs. +//! +//! See: +//! * +//! * +//! +//! Note: This CIP509 is still under development and is subject to change. + +use c509_certificate::{general_names::general_name::GeneralNameValue, C509ExtensionType}; +use der_parser::der::parse_der_sequence; +use pallas::{ + codec::{ + minicbor::{Encode, Encoder}, + utils::Bytes, + }, + ledger::traverse::MultiEraTx, +}; +use x509_cert::der::{oid::db::rfc5912::ID_CE_SUBJECT_ALT_NAME, Decode}; + +use super::{ + blake2b_128, blake2b_256, decode_utf8, decremented_index, + rbac::{ + certs::{C509Cert, X509DerCert}, + role_data::RoleData, + }, + utils::cip19::{compare_key_hash, extract_cip19_hash, extract_key_hash}, + Cip509, TxInputHash, TxWitness, +}; + +/// Context-specific primitive type with tag number 6 (`raw_tag` 134) for +/// uniform resource identifier (URI) in the subject alternative name extension. +pub(crate) const URI: u8 = 134; + +// ------------------------ Validate Txn Inputs Hash ------------------------ + +/// Transaction inputs hash validation. +/// CIP509 `txn_inputs_hash` must match the hash of the transaction inputs within the +/// body. +pub(crate) fn validate_txn_inputs_hash( + cip509: &Cip509, txn: &MultiEraTx, validation_report: &mut Vec, +) -> Option { + let function_name = "Validate Transaction Inputs Hash"; + let mut buffer = Vec::new(); + let mut e = Encoder::new(&mut buffer); + // CIP-0509 should only be in conway era + if let MultiEraTx::Conway(tx) = txn { + let inputs = tx.transaction_body.inputs.clone(); + if let Err(e) = e.array(inputs.len() as u64) { + validation_report.push(format!( + "{function_name}, Failed to encode array of transaction input: {e}" + )); + return None; + } + for input in &inputs { + match input.encode(&mut e, &mut ()) { + Ok(()) => {}, + Err(e) => { + validation_report.push(format!( + "{function_name}, Failed to encode transaction input {e}" + )); + return None; + }, + } + } + // Hash the transaction inputs + let inputs_hash = match blake2b_128(&buffer) { + Ok(hash) => hash, + Err(e) => { + validation_report.push(format!( + "{function_name}, Failed to hash transaction inputs {e}" + )); + return None; + }, + }; + Some(TxInputHash(inputs_hash) == cip509.txn_inputs_hash) + } else { + validation_report.push(format!("{function_name}, Unsupported transaction era for")); + None + } +} + +// ------------------------ Validate Stake Public Key ------------------------ + +/// Validate the stake public key in the certificate with witness set in transaction. +#[allow(clippy::too_many_lines)] +pub(crate) fn validate_stake_public_key( + cip509: &Cip509, txn: &MultiEraTx, txn_idx: usize, validation_report: &mut Vec, +) -> Option { + let function_name = "Validate Stake Public Key"; + let mut pk_addrs = Vec::new(); + + // CIP-0509 should only be in conway era + if let MultiEraTx::Conway(_) = txn { + // X509 certificate + if let Some(x509_certs) = &cip509.x509_chunks.0.x509_certs { + for x509_cert in x509_certs { + match x509_cert { + X509DerCert::X509Cert(cert) => { + // Attempt to decode the DER certificate + let der_cert = match x509_cert::Certificate::from_der(cert) { + Ok(cert) => cert, + Err(e) => { + validation_report.push(format!( + "{function_name}, Failed to decode x509 certificate DER: {e}" + )); + return None; + }, + }; + + // Find the Subject Alternative Name extension + let san_ext = + der_cert + .tbs_certificate + .extensions + .as_ref() + .and_then(|exts| { + exts.iter() + .find(|ext| ext.extn_id == ID_CE_SUBJECT_ALT_NAME) + }); + + // Subject Alternative Name extension if it exists + if let Some(san_ext) = san_ext { + match parse_der_sequence(san_ext.extn_value.as_bytes()) { + Ok((_, parsed_seq)) => { + for data in parsed_seq.ref_iter() { + // Check for context-specific primitive type with tag + // number + // 6 (raw_tag 134) + if data.header.raw_tag() == Some(&[URI]) { + match data.content.as_slice() { + Ok(content) => { + // Decode the UTF-8 string + let addr: String = match decode_utf8(content) { + Ok(addr) => addr, + Err(e) => { + validation_report.push(format!( + "{function_name}, Failed to decode UTF-8 string for context-specific primitive type with raw tag 134: {e}", + ), + ); + return None; + }, + }; + + // Extract the CIP19 hash and push into + // array + if let Some(h) = + extract_cip19_hash(&addr, Some("stake")) + { + pk_addrs.push(h); + } + }, + Err(e) => { + validation_report.push( + format!("{function_name}, Failed to process content for context-specific primitive type with raw tag 134: {e}")); + return None; + }, + } + } + } + }, + Err(e) => { + validation_report.push( + format!( + "{function_name}, Failed to parse DER sequence for Subject Alternative Name extension: {e}" + ) + ); + return None; + }, + } + } + }, + _ => continue, + } + } + } + // C509 Certificate + if let Some(c509_certs) = &cip509.x509_chunks.0.c509_certs { + for c509_cert in c509_certs { + match c509_cert { + C509Cert::C509CertInMetadatumReference(_) => { + validation_report.push(format!( + "{function_name}, C509 metadatum reference is currently not supported" + )); + }, + C509Cert::C509Certificate(c509) => { + for exts in c509.tbs_cert().extensions().extensions() { + if *exts.registered_oid().c509_oid().oid() + == C509ExtensionType::SubjectAlternativeName.oid() + { + match exts.value() { + c509_certificate::extensions::extension::ExtensionValue::AlternativeName(alt_name) => { + match alt_name.general_name() { + c509_certificate::extensions::alt_name::GeneralNamesOrText::GeneralNames(gn) => { + for name in gn.general_names() { + if name.gn_type() == &c509_certificate::general_names::general_name::GeneralNameTypeRegistry::UniformResourceIdentifier { + match name.gn_value() { + GeneralNameValue::Text(s) => { + if let Some(h) = extract_cip19_hash(s, Some("stake")) { + pk_addrs.push(h); + } + }, + _ => { + validation_report.push( + format!("{function_name}, Failed to get the value of subject alternative name"), + ); + } + } + } + } + }, + c509_certificate::extensions::alt_name::GeneralNamesOrText::Text(_) => { + validation_report.push( + format!("{function_name}, Failed to find C509 general names in subject alternative name"), + ); + } + } + }, + _ => { + validation_report.push( + format!("{function_name}, Failed to get C509 subject alternative name") + ); + } + } + } + } + }, + _ => continue, + } + } + } + } else { + validation_report.push(format!("{function_name}, Unsupported transaction era")); + return None; + } + + // Create TxWitness + let witnesses = match TxWitness::new(&[txn.clone()]) { + Ok(witnesses) => witnesses, + Err(e) => { + validation_report.push(format!("{function_name}, Failed to create TxWitness: {e}")); + return None; + }, + }; + + let index = match u16::try_from(txn_idx) { + Ok(value) => value, + Err(e) => { + validation_report.push(format!( + "{function_name}, Failed to convert transaction index to usize: {e}" + )); + return None; + }, + }; + Some( + compare_key_hash(&pk_addrs, &witnesses, index) + .map_err(|e| { + validation_report.push(format!( + "{function_name}, Failed to compare public keys with witnesses: {e}" + )); + }) + .is_ok(), + ) +} + +// ------------------------ Validate Aux ------------------------ + +/// Validate the auxiliary data with the auxiliary data hash in the transaction body. +pub(crate) fn validate_aux(txn: &MultiEraTx, validation_report: &mut Vec) -> Option { + let function_name = "Validate Aux"; + + // CIP-0509 should only be in conway era + if let MultiEraTx::Conway(tx) = txn { + if let pallas::codec::utils::Nullable::Some(a) = &tx.auxiliary_data { + let original_aux = a.raw_cbor(); + let aux_data_hash = tx + .transaction_body + .auxiliary_data_hash + .as_ref() + .or_else(|| { + validation_report.push(format!( + "{function_name}, Auxiliary data hash not found in transaction" + )); + None + })?; + validate_aux_helper(original_aux, aux_data_hash, validation_report) + } else { + validation_report.push(format!( + "{function_name}, Auxiliary data not found in transaction" + )); + None + } + } else { + validation_report.push(format!("{function_name}, Unsupported transaction era")); + None + } +} + +/// Helper function for auxiliary data validation. +fn validate_aux_helper( + original_aux: &[u8], aux_data_hash: &Bytes, validation_report: &mut Vec, +) -> Option { + // Compare the hash + match blake2b_256(original_aux) { + Ok(original_hash) => { + return Some(aux_data_hash.as_ref() == original_hash); + }, + Err(e) => { + validation_report.push(format!("Cannot hash auxiliary data {e}")); + None + }, + } +} + +// ------------------------ Validate Payment Key ------------------------ + +/// Validate the payment key reference. +/// Negative ref is for transaction output. +/// Positive ref is for transaction input. +pub(crate) fn validate_payment_key( + txn: &MultiEraTx, txn_idx: usize, role_data: &RoleData, validation_report: &mut Vec, +) -> Option { + let function_name = "Validate Payment Key"; + + if let Some(payment_key) = role_data.payment_key { + if payment_key == 0 { + validation_report.push(format!( + "{function_name}, Invalid payment reference key, 0 is not allowed" + )); + return None; + } + // CIP-0509 should only be in conway era + if let MultiEraTx::Conway(tx) = txn { + // Negative indicates reference to tx output + if payment_key < 0 { + let index = match decremented_index(payment_key.abs()) { + Ok(value) => value, + Err(e) => { + validation_report.push(format!( + "{function_name}, Failed to get index of payment key: {e}" + )); + return None; + }, + }; + let outputs = tx.transaction_body.outputs.clone(); + let witness = match TxWitness::new(&[txn.clone()]) { + Ok(witnesses) => witnesses, + Err(e) => { + validation_report + .push(format!("{function_name}, Failed to create TxWitness: {e}")); + return None; + }, + }; + + if let Some(output) = outputs.get(index) { + match output { + pallas::ledger::primitives::conway::PseudoTransactionOutput::Legacy(o) => { + return validate_payment_output_key_helper( + &o.address.to_vec(), + validation_report, + &witness, + txn_idx, + ); + }, + pallas::ledger::primitives::conway::PseudoTransactionOutput::PostAlonzo( + o, + ) => { + return validate_payment_output_key_helper( + &o.address.to_vec(), + validation_report, + &witness, + txn_idx, + ); + }, + }; + } + validation_report.push( + format!("{function_name}, Role payment key reference index is not found in transaction outputs") + ); + return None; + } + // Positive indicates reference to tx input + let inputs = &tx.transaction_body.inputs; + let index = match decremented_index(payment_key) { + Ok(value) => value, + Err(e) => { + validation_report.push(format!( + "{function_name}, Failed to get index of payment key: {e}" + )); + return None; + }, + }; + // Check whether the index exists in transaction inputs + if inputs.get(index).is_none() { + validation_report.push( + format!("{function_name}, Role payment key reference index is not found in transaction inputs") + ); + return None; + } + Some(true) + } else { + validation_report.push(format!( + "{function_name}, Unsupported transaction era for stake payment key validation" + )); + None + } + } else { + Some(false) + } +} + +/// Helper function for validating payment output key. +fn validate_payment_output_key_helper( + output_address: &[u8], validation_report: &mut Vec, witness: &TxWitness, txn_idx: usize, +) -> Option { + let idx = match u16::try_from(txn_idx) { + Ok(value) => value, + Err(e) => { + validation_report.push(format!("Transaction index conversion failed: {e}")); + return None; + }, + }; + // Extract the key hash from the output address + if let Some(key) = extract_key_hash(output_address) { + // Compare the key hash and return the result + return Some(compare_key_hash(&[key], witness, idx).is_ok()); + } + validation_report.push("Failed to extract payment key hash from address".to_string()); + None +} + +#[cfg(test)] +mod tests { + + use minicbor::{Decode, Decoder}; + + use super::*; + use crate::cardano::transaction::raw_aux_data::RawAuxData; + + fn cip_509_aux_data(tx: &MultiEraTx<'_>) -> Vec { + let raw_auxiliary_data = tx + .as_conway() + .unwrap() + .clone() + .auxiliary_data + .map(|aux| aux.raw_cbor()); + + let raw_cbor_data = match raw_auxiliary_data { + pallas::codec::utils::Nullable::Some(data) => Ok(data), + _ => Err("Auxiliary data not found"), + }; + + let auxiliary_data = RawAuxData::new(raw_cbor_data.expect("Failed to get raw cbor data")); + auxiliary_data + .get_metadata(509) + .expect("Failed to get metadata") + .to_vec() + } + + fn conway_1() -> Vec { + hex::decode(include_str!("../../test_data/cardano/conway_1.block")) + .expect("Failed to decode hex block.") + } + + fn conway_2() -> Vec { + hex::decode(include_str!("../../test_data/cardano/conway_2.block")) + .expect("Failed to decode hex block.") + } + + fn conway_3() -> Vec { + hex::decode(include_str!("../../test_data/cardano/conway_3.block")) + .expect("Failed to decode hex block.") + } + + #[test] + fn test_validate_txn_inputs_hash() { + let mut validation_report = Vec::new(); + let conway_block_data = conway_1(); + let multi_era_block = pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data) + .expect("Failed to decode MultiEraBlock"); + + let transactions = multi_era_block.txs(); + // Forth transaction of this test data contains the CIP509 auxiliary data + let tx = transactions + .get(3) + .expect("Failed to get transaction index"); + let aux_data = cip_509_aux_data(tx); + + let mut decoder = Decoder::new(aux_data.as_slice()); + let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); + assert!(validate_txn_inputs_hash(&cip509, tx, &mut validation_report).unwrap()); + } + + #[test] + fn test_validate_aux() { + let mut validation_report = Vec::new(); + let conway_block_data = conway_1(); + let multi_era_block = pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data) + .expect("Failed to decode MultiEraBlock"); + + let transactions = multi_era_block.txs(); + // Forth transaction of this test data contains the CIP509 auxiliary data + let tx = transactions + .get(3) + .expect("Failed to get transaction index"); + + // let aux_data = cip_509_aux_data(tx); + + // let mut decoder = Decoder::new(aux_data.as_slice()); + // let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); + validate_aux(tx, &mut validation_report); + assert!(validate_aux(tx, &mut validation_report).unwrap()); + } + + #[test] + fn test_validate_public_key_success() { + let mut validation_report = Vec::new(); + let conway_block_data = conway_1(); + let multi_era_block = pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data) + .expect("Failed to decode MultiEraBlock"); + + let transactions = multi_era_block.txs(); + // Forth transaction of this test data contains the CIP509 auxiliary data + let tx = transactions + .get(3) + .expect("Failed to get transaction index"); + + let aux_data = cip_509_aux_data(tx); + + let mut decoder = Decoder::new(aux_data.as_slice()); + let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); + assert!(validate_stake_public_key(&cip509, tx, 0, &mut validation_report).unwrap()); + } + + #[test] + fn test_validate_payment_key_success_positive_ref() { + let mut validation_report = Vec::new(); + let conway_block_data = conway_1(); + let multi_era_block = pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data) + .expect("Failed to decode MultiEraBlock"); + + let transactions = multi_era_block.txs(); + // Forth transaction of this test data contains the CIP509 auxiliary data + let tx = transactions + .get(3) + .expect("Failed to get transaction index"); + + let aux_data = cip_509_aux_data(tx); + + let mut decoder = Decoder::new(aux_data.as_slice()); + let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); + + if let Some(role_set) = &cip509.x509_chunks.0.role_set { + for role in role_set { + if role.role_number == 0 { + assert!(validate_payment_key(tx, 0, role, &mut validation_report,).unwrap()); + } + } + } + } + + #[test] + fn test_validate_payment_key_success_negative_ref() { + let mut validation_report = Vec::new(); + let conway_block_data = conway_3(); + let multi_era_block = pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data) + .expect("Failed to decode MultiEraBlock"); + + let transactions = multi_era_block.txs(); + // First transaction of this test data contains the CIP509 auxiliary data + let tx = transactions + .first() + .expect("Failed to get transaction index"); + + let aux_data = cip_509_aux_data(tx); + + let mut decoder = Decoder::new(aux_data.as_slice()); + let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); + + if let Some(role_set) = &cip509.x509_chunks.0.role_set { + for role in role_set { + if role.role_number == 0 { + println!( + "{:?}", + validate_payment_key(tx, 0, role, &mut validation_report,) + ); + } + } + } + } + + #[test] + fn test_validate_public_key_fail() { + let mut validation_report = Vec::new(); + let conway_block_data = conway_2(); + let multi_era_block = pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data) + .expect("Failed to decode MultiEraBlock"); + + let transactions = multi_era_block.txs(); + // Forth transaction of this test data contains the CIP509 auxiliary data + let tx = transactions + .get(3) + .expect("Failed to get transaction index"); + + let aux_data = cip_509_aux_data(tx); + + let mut decoder = Decoder::new(aux_data.as_slice()); + let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); + assert!(!validate_stake_public_key(&cip509, tx, 0, &mut validation_report).unwrap()); + } +} From 8aba254c9fcdcb0451551fbfaad52bc05d39b091 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Tue, 26 Nov 2024 12:53:27 +0700 Subject: [PATCH 18/45] fix: rust ci Signed-off-by: bkioshn --- rust/Earthfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/Earthfile b/rust/Earthfile index 639e837f8b0..dc1f64f6ddc 100644 --- a/rust/Earthfile +++ b/rust/Earthfile @@ -55,7 +55,7 @@ build: --args1="--libs=c509-certificate --libs=cardano-chain-follower --libs=hermes-ipfs" \ --args2="--libs=cbork-cddl-parser --libs=cbork-abnf-parser" \ --args3="--libs=catalyst-voting --libs=vote-tx-v1 --libs=vote-tx-v2" \ - --args4="--bins=cbork/cbork --libs=rbac-registration"\ + --args4="--bins=cbork/cbork --libs=rbac-registration" \ --args5="--cov_report=$HOME/build/coverage-report.info" \ --output="release/[^\./]+" \ --junit="cat-libs.junit-report.xml" \ From d84aee0a46e23f172380384f2c7cf0ea9671b37d Mon Sep 17 00:00:00 2001 From: bkioshn Date: Tue, 26 Nov 2024 14:44:22 +0700 Subject: [PATCH 19/45] feat: add role signing key validation Signed-off-by: bkioshn --- .../src/cardano/cip509/mod.rs | 10 ++++-- .../src/cardano/cip509/validation.rs | 32 +++++++++++++++++-- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/mod.rs b/rust/rbac-registration/src/cardano/cip509/mod.rs index 377804684bf..a4617dc7fb7 100644 --- a/rust/rbac-registration/src/cardano/cip509/mod.rs +++ b/rust/rbac-registration/src/cardano/cip509/mod.rs @@ -16,7 +16,8 @@ use minicbor::{ use pallas::ledger::traverse::MultiEraTx; use strum_macros::FromRepr; use validation::{ - validate_aux, validate_payment_key, validate_stake_public_key, validate_txn_inputs_hash, + validate_aux, validate_payment_key, validate_role_singing_key, validate_stake_public_key, + validate_txn_inputs_hash, }; use x509_chunks::X509Chunks; @@ -199,7 +200,8 @@ impl Cip509 { /// 2. Positive index reference - reference to the transaction input in /// transaction: only check whether the index exist within the transaction /// inputs. - /// + /// * Role signing key validation for role 0 where the signing keys should only be the certificates + /// /// See: /// * /// * @@ -218,6 +220,7 @@ impl Cip509 { let aux_validate = validate_aux(txn, validation_report).unwrap_or(false); let mut stake_key_validate = true; let mut payment_key_validate = true; + let mut signing_key = true; // Validate the role 0 if let Some(role_set) = &self.x509_chunks.0.role_set { // Validate only role 0 @@ -229,10 +232,11 @@ impl Cip509 { payment_key_validate = validate_payment_key(txn, txn_idx, role, validation_report) .unwrap_or(false); + signing_key = validate_role_singing_key(role, validation_report); } } } - tx_input_validate && aux_validate && stake_key_validate && payment_key_validate + tx_input_validate && aux_validate && stake_key_validate && payment_key_validate && signing_key } } diff --git a/rust/rbac-registration/src/cardano/cip509/validation.rs b/rust/rbac-registration/src/cardano/cip509/validation.rs index bc2ea556f85..e75d46b97b7 100644 --- a/rust/rbac-registration/src/cardano/cip509/validation.rs +++ b/rust/rbac-registration/src/cardano/cip509/validation.rs @@ -12,7 +12,8 @@ //! should match some of the key within witness set. //! 2. Positive index reference - reference to the transaction input in transaction: //! only check whether the index exist within the transaction inputs. -//! +//! * Role signing key validation for role 0 where the signing keys should only be the certificates +//! //! See: //! * //! * @@ -34,7 +35,7 @@ use super::{ blake2b_128, blake2b_256, decode_utf8, decremented_index, rbac::{ certs::{C509Cert, X509DerCert}, - role_data::RoleData, + role_data::{LocalRefInt, RoleData}, }, utils::cip19::{compare_key_hash, extract_cip19_hash, extract_key_hash}, Cip509, TxInputHash, TxWitness, @@ -441,6 +442,33 @@ fn validate_payment_output_key_helper( None } +// ------------------------ Validate role signing key ------------------------ + +/// Validate role singing key for role 0. +/// Must reference certificate not the public key +pub(crate) fn validate_role_singing_key( + role_data: &RoleData, validation_report: &mut Vec, +) -> bool { + const FUNCTION_NAME: &str = "Validate Role Signing Key"; + + // If signing key exist, it should not contain public key + if let Some(local_ref) = &role_data.role_signing_key { + if local_ref + .iter() + .any(|k| k.local_ref == LocalRefInt::PubKeys) + { + validation_report.push(format!( + "{FUNCTION_NAME}, Role signing key should reference certificate, not public key", + )); + return false; + } + } + + true +} + +// ------------------------ Tests ------------------------ + #[cfg(test)] mod tests { From 86c163fde9dc2a2a1bcf05485a390b9904b0befe Mon Sep 17 00:00:00 2001 From: bkioshn Date: Tue, 26 Nov 2024 15:13:27 +0700 Subject: [PATCH 20/45] fix: add Bytes conversion for Pubkey Signed-off-by: bkioshn --- .../src/cardano/cip509/rbac/pub_key.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs b/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs index 59aaf97d4f9..5d45b86124f 100644 --- a/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs +++ b/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs @@ -1,6 +1,7 @@ //! Public key type for RBAC metadata use minicbor::{decode, Decode, Decoder}; +use pallas::codec::utils::Bytes; use super::tag::KeyTag; use crate::utils::decode_helper::{decode_bytes, decode_tag}; @@ -18,7 +19,7 @@ pub enum SimplePublicKeyType { } /// 32 bytes Ed25519 public key. -#[derive(Debug, PartialEq, Clone, Default)] +#[derive(Debug, PartialEq, Clone, Default, Eq, Hash)] pub struct Ed25519PublicKey([u8; 32]); impl From<[u8; 32]> for Ed25519PublicKey { @@ -27,6 +28,13 @@ impl From<[u8; 32]> for Ed25519PublicKey { } } +impl Into for Ed25519PublicKey { + fn into(self) -> Bytes { + let vec: Vec = self.0.to_vec(); + Bytes::from(vec) + } +} + impl Decode<'_, ()> for SimplePublicKeyType { fn decode(d: &mut Decoder, _ctx: &mut ()) -> Result { match d.datatype()? { @@ -48,11 +56,9 @@ impl Decode<'_, ()> for SimplePublicKeyType { } }, minicbor::data::Type::Undefined => Ok(Self::Undefined), - _ => { - Err(decode::Error::message( - "Invalid datatype for SimplePublicKeyType", - )) - }, + _ => Err(decode::Error::message( + "Invalid datatype for SimplePublicKeyType", + )), } } } From afcaa0927c06c0dc01a6b4a4f12f62d8adf1ecd5 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Tue, 26 Nov 2024 15:18:52 +0700 Subject: [PATCH 21/45] fix: error return --- rust/rbac-registration/src/cardano/cip509/utils/cip19.rs | 7 ++++--- rust/rbac-registration/src/cardano/transaction/witness.rs | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/utils/cip19.rs b/rust/rbac-registration/src/cardano/cip509/utils/cip19.rs index 8c378705bf3..20f6fd6fd5d 100644 --- a/rust/rbac-registration/src/cardano/cip509/utils/cip19.rs +++ b/rust/rbac-registration/src/cardano/cip509/utils/cip19.rs @@ -1,5 +1,6 @@ //! Utility functions for CIP-19 address. +use anyhow::bail; use regex::Regex; use crate::cardano::transaction::witness::TxWitness; @@ -44,7 +45,7 @@ pub(crate) fn compare_key_hash( pk_addrs: &[Vec], witness: &TxWitness, txn_idx: u16, ) -> anyhow::Result<()> { if pk_addrs.is_empty() { - return Err(anyhow::anyhow!("No public key addresses provided")); + bail!("No public key addresses provided"); } pk_addrs.iter().try_for_each(|pk_addr| { @@ -57,10 +58,10 @@ pub(crate) fn compare_key_hash( // Key hash not found in the transaction witness set if !witness.check_witness_in_tx(&pk_addr, txn_idx) { - return Err(anyhow::anyhow!( + bail!( "Public key hash not found in transaction witness set given {:?}", pk_addr - )); + ); } Ok(()) diff --git a/rust/rbac-registration/src/cardano/transaction/witness.rs b/rust/rbac-registration/src/cardano/transaction/witness.rs index 2ba2377583e..05545bb91b8 100644 --- a/rust/rbac-registration/src/cardano/transaction/witness.rs +++ b/rust/rbac-registration/src/cardano/transaction/witness.rs @@ -1,6 +1,7 @@ //! Transaction Witness use std::fmt::{Display, Formatter}; +use anyhow::bail; use dashmap::DashMap; use pallas::{codec::utils::Bytes, ledger::traverse::MultiEraTx}; @@ -58,7 +59,7 @@ impl TxWitness { } }, _ => { - return Err(anyhow::anyhow!("Unsupported transaction type")); + bail!("Unsupported transaction type"); }, }; } From 76b20ca66c83ac6be0f0486ff7bf76504f15fe91 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Tue, 26 Nov 2024 15:38:06 +0700 Subject: [PATCH 22/45] fix: use Hash from pallas for tx hash Signed-off-by: bkioshn --- .../src/cardano/cip509/mod.rs | 49 ++++++------------- 1 file changed, 15 insertions(+), 34 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/mod.rs b/rust/rbac-registration/src/cardano/cip509/mod.rs index a4617dc7fb7..47768a08c3e 100644 --- a/rust/rbac-registration/src/cardano/cip509/mod.rs +++ b/rust/rbac-registration/src/cardano/cip509/mod.rs @@ -13,7 +13,7 @@ use minicbor::{ decode::{self}, Decode, Decoder, }; -use pallas::ledger::traverse::MultiEraTx; +use pallas::{crypto::hash::Hash, ledger::traverse::MultiEraTx}; use strum_macros::FromRepr; use validation::{ validate_aux, validate_payment_key, validate_role_singing_key, validate_stake_public_key, @@ -39,7 +39,7 @@ pub struct Cip509 { /// Transaction inputs hash. pub txn_inputs_hash: TxInputHash, // bytes .size 16 /// Optional previous transaction ID. - pub prv_tx_id: Option, // bytes .size 32 + pub prv_tx_id: Option>, // bytes .size 32 /// x509 chunks. pub x509_chunks: X509Chunks, // chunk_type => [ + x509_chunk ] /// Validation signature. @@ -70,30 +70,6 @@ impl TryFrom> for UuidV4 { } } -/// Transaction hash representing in 32 bytes. -#[derive(Debug, PartialEq, Clone, Default)] -pub struct TxHash([u8; 32]); - -impl From<[u8; 32]> for TxHash { - fn from(bytes: [u8; 32]) -> Self { - TxHash(bytes) - } -} - -impl TryFrom> for TxHash { - type Error = &'static str; - - fn try_from(vec: Vec) -> Result { - if vec.len() == 32 { - let mut array = [0u8; 32]; - array.copy_from_slice(&vec); - Ok(TxHash(array)) - } else { - Err("Input Vec must be exactly 32 bytes") - } - } -} - /// Transaction input hash representing in 16 bytes. #[derive(Debug, PartialEq, Clone, Default)] pub struct TxInputHash([u8; 16]); @@ -158,11 +134,12 @@ impl Decode<'_, ()> for Cip509 { })?; }, Cip509IntIdentifier::PreviousTxId => { - cip509_metadatum.prv_tx_id = Some( - TxHash::try_from(decode_bytes(d, "CIP509 previous tx ID")?).map_err( - |_| decode::Error::message("Invalid data size of PreviousTxId"), - )?, - ); + let prv_tx_hash: [u8; 32] = decode_bytes(d, "CIP509 previous tx ID")? + .try_into() + .map_err(|_| { + decode::Error::message("Invalid data size of PreviousTxId") + })?; + cip509_metadatum.prv_tx_id = Some(Hash::from(prv_tx_hash)); }, Cip509IntIdentifier::ValidationSignature => { let validation_signature = decode_bytes(d, "CIP509 validation signature")?; @@ -201,7 +178,7 @@ impl Cip509 { /// transaction: only check whether the index exist within the transaction /// inputs. /// * Role signing key validation for role 0 where the signing keys should only be the certificates - /// + /// /// See: /// * /// * @@ -236,7 +213,11 @@ impl Cip509 { } } } - tx_input_validate && aux_validate && stake_key_validate && payment_key_validate && signing_key + tx_input_validate + && aux_validate + && stake_key_validate + && payment_key_validate + && signing_key } } @@ -270,7 +251,7 @@ mod tests { assert_eq!(decoded_cip509.purpose, UuidV4(purpose)); assert_eq!(decoded_cip509.txn_inputs_hash, TxInputHash(txn_inputs_hash)); - assert_eq!(decoded_cip509.prv_tx_id, Some(TxHash(prv_tx_id))); + assert_eq!(decoded_cip509.prv_tx_id, Some(prv_tx_id.into())); assert_eq!(decoded_cip509.validation_signature, validation_signature); } } From 99d8a86f9e2312472083c8fcd82e579be1e46ac1 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Tue, 26 Nov 2024 18:14:43 +0700 Subject: [PATCH 23/45] fix: format Signed-off-by: bkioshn --- rust/rbac-registration/src/cardano/cip509/mod.rs | 3 ++- rust/rbac-registration/src/cardano/cip509/validation.rs | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/mod.rs b/rust/rbac-registration/src/cardano/cip509/mod.rs index 47768a08c3e..33b97492c95 100644 --- a/rust/rbac-registration/src/cardano/cip509/mod.rs +++ b/rust/rbac-registration/src/cardano/cip509/mod.rs @@ -177,7 +177,8 @@ impl Cip509 { /// 2. Positive index reference - reference to the transaction input in /// transaction: only check whether the index exist within the transaction /// inputs. - /// * Role signing key validation for role 0 where the signing keys should only be the certificates + /// * Role signing key validation for role 0 where the signing keys should only be the + /// certificates /// /// See: /// * diff --git a/rust/rbac-registration/src/cardano/cip509/validation.rs b/rust/rbac-registration/src/cardano/cip509/validation.rs index e75d46b97b7..1e579538e13 100644 --- a/rust/rbac-registration/src/cardano/cip509/validation.rs +++ b/rust/rbac-registration/src/cardano/cip509/validation.rs @@ -12,8 +12,9 @@ //! should match some of the key within witness set. //! 2. Positive index reference - reference to the transaction input in transaction: //! only check whether the index exist within the transaction inputs. -//! * Role signing key validation for role 0 where the signing keys should only be the certificates -//! +//! * Role signing key validation for role 0 where the signing keys should only be the +//! certificates +//! //! See: //! * //! * @@ -449,7 +450,7 @@ fn validate_payment_output_key_helper( pub(crate) fn validate_role_singing_key( role_data: &RoleData, validation_report: &mut Vec, ) -> bool { - const FUNCTION_NAME: &str = "Validate Role Signing Key"; + let function_name = "Validate Role Signing Key"; // If signing key exist, it should not contain public key if let Some(local_ref) = &role_data.role_signing_key { @@ -458,7 +459,7 @@ pub(crate) fn validate_role_singing_key( .any(|k| k.local_ref == LocalRefInt::PubKeys) { validation_report.push(format!( - "{FUNCTION_NAME}, Role signing key should reference certificate, not public key", + "{function_name}, Role signing key should reference certificate, not public key", )); return false; } From 72a1d3a0d14be3ecb21eb708536dda787f53e52f Mon Sep 17 00:00:00 2001 From: bkioshn Date: Tue, 26 Nov 2024 18:16:14 +0700 Subject: [PATCH 24/45] fix: bytes to pubkey conversion --- .../src/cardano/cip509/rbac/pub_key.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs b/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs index 5d45b86124f..6094c7d7b8c 100644 --- a/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs +++ b/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs @@ -28,9 +28,9 @@ impl From<[u8; 32]> for Ed25519PublicKey { } } -impl Into for Ed25519PublicKey { - fn into(self) -> Bytes { - let vec: Vec = self.0.to_vec(); +impl From for Bytes { + fn from(val: Ed25519PublicKey) -> Self { + let vec: Vec = val.0.to_vec(); Bytes::from(vec) } } @@ -56,9 +56,11 @@ impl Decode<'_, ()> for SimplePublicKeyType { } }, minicbor::data::Type::Undefined => Ok(Self::Undefined), - _ => Err(decode::Error::message( - "Invalid datatype for SimplePublicKeyType", - )), + _ => { + Err(decode::Error::message( + "Invalid datatype for SimplePublicKeyType", + )) + }, } } } From bb43028314ae1a7a77889dfb8d871aea6dbb8e1a Mon Sep 17 00:00:00 2001 From: bkioshn Date: Tue, 26 Nov 2024 20:12:52 +0700 Subject: [PATCH 25/45] fix: add Bytes conversion for Pubkey Signed-off-by: bkioshn --- .../src/cardano/cip509/rbac/pub_key.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs b/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs index 6094c7d7b8c..837c1327d54 100644 --- a/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs +++ b/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs @@ -28,6 +28,24 @@ impl From<[u8; 32]> for Ed25519PublicKey { } } +impl TryFrom for Ed25519PublicKey { + type Error = &'static str; + + fn try_from(bytes: Bytes) -> Result { + let byte_vec: Vec = bytes.into(); + + if byte_vec.len() != 32 { + return Err("Invalid length for Ed25519 public key: expected 32 bytes."); + } + + let byte_array: [u8; 32] = byte_vec + .try_into() + .map_err(|_| "Failed to convert Vec to [u8; 32]")?; + + Ok(Ed25519PublicKey::from(byte_array)) + } +} + impl From for Bytes { fn from(val: Ed25519PublicKey) -> Self { let vec: Vec = val.0.to_vec(); From 7583aeceb445dea32338092d3ecd5b04d82f94d7 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Wed, 27 Nov 2024 12:26:43 +0700 Subject: [PATCH 26/45] fix: add rbac-reg to semantic pull request --- .github/workflows/semantic_pull_request.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/semantic_pull_request.yml b/.github/workflows/semantic_pull_request.yml index 022bb4036ea..a9e4a49307a 100644 --- a/.github/workflows/semantic_pull_request.yml +++ b/.github/workflows/semantic_pull_request.yml @@ -24,6 +24,7 @@ jobs: rust/vote-tx-v2 rust/cbork rust/hermes-ipfs + rust/rbac-registration dart docs general From 5571d1aa354b4197af0703494682257785a8ee9d Mon Sep 17 00:00:00 2001 From: bkioshn Date: Fri, 29 Nov 2024 11:45:28 +0700 Subject: [PATCH 27/45] fix: test data Signed-off-by: bkioshn --- .../src/cardano/cip509/validation.rs | 47 ++++++++----------- .../src/test_data/cardano/conway_1.block | 2 +- .../src/test_data/cardano/conway_2.block | 2 +- .../src/test_data/cardano/conway_3.block | 2 +- 4 files changed, 23 insertions(+), 30 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/validation.rs b/rust/rbac-registration/src/cardano/cip509/validation.rs index 1e579538e13..84fb9d22324 100644 --- a/rust/rbac-registration/src/cardano/cip509/validation.rs +++ b/rust/rbac-registration/src/cardano/cip509/validation.rs @@ -149,7 +149,7 @@ pub(crate) fn validate_stake_public_key( Ok(addr) => addr, Err(e) => { validation_report.push(format!( - "{function_name}, Failed to decode UTF-8 string for context-specific primitive type with raw tag 134: {e}", + "{function_name}, Failed to decode UTF-8 string for context-specific primitive type with raw tag 134: {e}", ), ); return None; @@ -454,10 +454,7 @@ pub(crate) fn validate_role_singing_key( // If signing key exist, it should not contain public key if let Some(local_ref) = &role_data.role_signing_key { - if local_ref - .iter() - .any(|k| k.local_ref == LocalRefInt::PubKeys) - { + if local_ref.local_ref == LocalRefInt::PubKeys { validation_report.push(format!( "{function_name}, Role signing key should reference certificate, not public key", )); @@ -521,12 +518,11 @@ mod tests { .expect("Failed to decode MultiEraBlock"); let transactions = multi_era_block.txs(); - // Forth transaction of this test data contains the CIP509 auxiliary data + // Second transaction of this test data contains the CIP509 auxiliary data let tx = transactions - .get(3) + .get(1) .expect("Failed to get transaction index"); let aux_data = cip_509_aux_data(tx); - let mut decoder = Decoder::new(aux_data.as_slice()); let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); assert!(validate_txn_inputs_hash(&cip509, tx, &mut validation_report).unwrap()); @@ -540,15 +536,11 @@ mod tests { .expect("Failed to decode MultiEraBlock"); let transactions = multi_era_block.txs(); - // Forth transaction of this test data contains the CIP509 auxiliary data + // Second transaction of this test data contains the CIP509 auxiliary data let tx = transactions - .get(3) + .get(1) .expect("Failed to get transaction index"); - // let aux_data = cip_509_aux_data(tx); - - // let mut decoder = Decoder::new(aux_data.as_slice()); - // let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); validate_aux(tx, &mut validation_report); assert!(validate_aux(tx, &mut validation_report).unwrap()); } @@ -561,29 +553,30 @@ mod tests { .expect("Failed to decode MultiEraBlock"); let transactions = multi_era_block.txs(); - // Forth transaction of this test data contains the CIP509 auxiliary data + // Second transaction of this test data contains the CIP509 auxiliary data let tx = transactions - .get(3) + .get(1) .expect("Failed to get transaction index"); let aux_data = cip_509_aux_data(tx); let mut decoder = Decoder::new(aux_data.as_slice()); let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); + validate_stake_public_key(&cip509, tx, 0, &mut validation_report); assert!(validate_stake_public_key(&cip509, tx, 0, &mut validation_report).unwrap()); } #[test] - fn test_validate_payment_key_success_positive_ref() { + fn test_validate_payment_key_success_negative_ref() { let mut validation_report = Vec::new(); let conway_block_data = conway_1(); let multi_era_block = pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data) .expect("Failed to decode MultiEraBlock"); let transactions = multi_era_block.txs(); - // Forth transaction of this test data contains the CIP509 auxiliary data + // Second transaction of this test data contains the CIP509 auxiliary data let tx = transactions - .get(3) + .get(1) .expect("Failed to get transaction index"); let aux_data = cip_509_aux_data(tx); @@ -594,14 +587,17 @@ mod tests { if let Some(role_set) = &cip509.x509_chunks.0.role_set { for role in role_set { if role.role_number == 0 { - assert!(validate_payment_key(tx, 0, role, &mut validation_report,).unwrap()); + println!( + "{:?}", + validate_payment_key(tx, 0, role, &mut validation_report,) + ); } } } } #[test] - fn test_validate_payment_key_success_negative_ref() { + fn test_validate_payment_key_success_positive_ref() { let mut validation_report = Vec::new(); let conway_block_data = conway_3(); let multi_era_block = pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data) @@ -621,10 +617,7 @@ mod tests { if let Some(role_set) = &cip509.x509_chunks.0.role_set { for role in role_set { if role.role_number == 0 { - println!( - "{:?}", - validate_payment_key(tx, 0, role, &mut validation_report,) - ); + assert!(validate_payment_key(tx, 0, role, &mut validation_report,).unwrap()); } } } @@ -638,9 +631,9 @@ mod tests { .expect("Failed to decode MultiEraBlock"); let transactions = multi_era_block.txs(); - // Forth transaction of this test data contains the CIP509 auxiliary data + // First transaction of this test data contains the CIP509 auxiliary data let tx = transactions - .get(3) + .first() .expect("Failed to get transaction index"); let aux_data = cip_509_aux_data(tx); diff --git a/rust/rbac-registration/src/test_data/cardano/conway_1.block b/rust/rbac-registration/src/test_data/cardano/conway_1.block index ee2341dbf79..137c6e3be81 100644 --- a/rust/rbac-registration/src/test_data/cardano/conway_1.block +++ b/rust/rbac-registration/src/test_data/cardano/conway_1.block @@ -1 +1 @@ -820785828a1a002a250b1a0453dd635820de837235735da44fef4d3dd7c810e36df43a56741f36488d43c7724844bdb93a5820087932a594533b96a2abd339715dd222835d36856ac2c0b8fcd171e3ec944b8e5820b5b14e8eb25a9366e0c1a43afcd8d0859249202f3a8dc03b13f416c70d4de7d78258408f9c0c36c36241dc497cf218bbaa28569b20da43d0dde22c42a06c7dbf9086f67a2724d3743749893edb73027bffb9382b3534e48e0210c50755b7e1c53faf885850a981acb25e4fe8018eb69799e8625a7b415d3acf8c7935a32b010018f29415ad7a0f48738dda9bb34fbaff868f777670e7b7b512b3fbe5ba3878ff16adda7f5a12c7f45f4f9a8c20cf29867534c6830619156858202de40ebb5bb7974675746077e043354f77856687144dcbeba15b5b039bc757438458208cc744c58c7a05709e02beb11b63c311687a1c1308b11e483d1c728f4f6fbf7d041901fd5840ba3364505403cd1f409595472bbf1e9e020fa331b39cf9c8a9652841ad6fe609de243d2368861351e83c685eff105f8b7bb7389d11c898cb94876202803115008209015901c080e7950464897eace2060d54656948ba519f9ce3e78eb93b29798f77940c49d17b310966cc580cd96162aa138d71c119cd8e76118daa8c3d32bf714fa6d8a409b5a92156fd374d4d912307b8652f38df7faf5608febf3599bca862c578ef7b68e1dfffb9be2a836067727899337e2be78b79913b35f7097ffb20a0972c0c5aee723debf8f922e55b0f93a194c9e8cdb21a1bcffa1ab6341e1d8cbff21c1f4d60d59a5bfc24176fc987241cfddd27e0882c3f8dc958a2a181829fcce9415554a96b5f62d71578479dd496ab0feddd385e3bfaa86229bacd1977799b7d4039f918659c7a8d73c6168d9708a4a6c10060dd58907423f0da8e79bc1024c7942fa9a20144540af7e596892aeb3057cab0ad6d2fc676d90bb4eca2bc42c84dadb719ddad18f036f0563f29eb67d7fee36b700117a243dae2732ecaafc1629cdcfb0a80c94d4f89321969289f0852d693c0b899f8f698465d7cff645879cb54c50eaa24d44fc48a79066c73c54d9438145136d161f1efdaefa9a544d3e3c35244ed941f968e917cc68a627df194e19d08bfc18482638deb4f2da42f7096d2cbc7e8b4f472f1027f53cca668252e30946f6e9ca4c3e224cd40f764ea59fe85d420b0bbe785a90082825820a8ee092381312ff15f5af09c8de5d9f75e661982873a92bf14b80c7a64d10c6400825820a8ee092381312ff15f5af09c8de5d9f75e661982873a92bf14b80c7a64d10c64010d81825820a8ee092381312ff15f5af09c8de5d9f75e661982873a92bf14b80c7a64d10c64011281825820db62a0577b70a84aadf81883f9314e5ed3f01a79429010c81875d719dcb3eb05010182a300581d7055f73a4556031cccd599cac389b4a98457c44cef77ef8647639ad5fe01821a001b9f18a1581c0c64e02d195a9a4a86a3e080ccc643cd79223b7fc45676d07cf3a802a14d446a65644f7261636c654e465401028201d81858a6d8799f5840d4d88111d26121908711b6c375d659e0f9bb1bc320912715324644f802fb01f0b9ef630c309aa38e26c779eadb66abe906d7755645ebbc73d23ed23d03b59407d8799fd8799f1a0007a1201a0002c5a3ffd8799fd8799fd87a9f1b0000019266006960ffd87a80ffd8799fd87a9f1b00000192660e2500ffd87a80ffff43555344ff581c0c64e02d195a9a4a86a3e080ccc643cd79223b7fc45676d07cf3a802ff82583900f8e2356e21af3563556c167c31bcc00f9fc061a8a852a16a42ffcc7927ce10b87147e3e19a5dc622b6a535ab9ce01cb2d586a3a690a74c0e1a8acb1ea0021a0003fa8a031a0453e0a0081a0453dd1c0e81581cf8e2356e21af3563556c167c31bcc00f9fc061a8a852a16a42ffcc790b5820d342f73527d192f78a35b8b6517964bdab319c68ad9b15ae024d323656675848a90082825820551cc8d750767bb04fec1cefed55b2ba230d769d3d42907de8dd4e2fdca901ec00825820551cc8d750767bb04fec1cefed55b2ba230d769d3d42907de8dd4e2fdca901ec010d81825820551cc8d750767bb04fec1cefed55b2ba230d769d3d42907de8dd4e2fdca901ec0112818258205de304d9c8884dd62ad8535d529e3d8fd5f212cc3c0c39410e4f3bccfca6e46b010182a300581d7039a4c3afe97b4c2d3385fefd5206d1865c74786b7ce955ebb6532e7a01821a001b9f18a1581cdab1406f1c769fbdb00514c494eed47a54c7ffc5d7aafc524cca069aa14d446a65644f7261636c654e465401028201d81858a6d8799f5840908c7e221021da9411c2951209f58ec0e5b00e2c6dbb5e64476ab9e185afd51b9c929893d36b0a8f727103fb31f981505483fbc4d3a9e95bc080362862fc5001d8799fd8799f1a000f42401a00058b45ffd8799fd8799fd87a9f1b0000019266006960ffd87a80ffd8799fd87a9f1b00000192660e2500ffd87a80ffff43555344ff581cdab1406f1c769fbdb00514c494eed47a54c7ffc5d7aafc524cca069aff82583900f0a26fc170ad82b64a3d43dede08e78ea6e2028b5101058d0263a80a4c0ec23eba3aa27a4b8d61107f59f3e0d24b7bb6d7ae1a4bbd689c8d1ab75f0348021a0003fa8a031a0453e0a0081a0453dd1c0e81581cf0a26fc170ad82b64a3d43dede08e78ea6e2028b5101058d0263a80a0b5820d342f73527d192f78a35b8b6517964bdab319c68ad9b15ae024d323656675848ab0084825820157dcc88aa4a0278d4eebd6764fdd3a7f7d53716d298d5366cb7fff5b124553901825820d8c2aa33753209ba095bb811a8cfe92a32539bdfae5bec771eca3ecd996d269100825820d8c2aa33753209ba095bb811a8cfe92a32539bdfae5bec771eca3ecd996d269101825820d8c2aa33753209ba095bb811a8cfe92a32539bdfae5bec771eca3ecd996d2691020184a300581d703f89c436844cd0acd2714201817836b2cd16b082e03e3a21bb69e5ad01821a002dc6c0a2581c2b556df9f37c04ef31b8f7f581c4e48174adcf5041e8e52497d81556a148416767537461746501581cc6f192a236596e2bbaac5900d67e9700dec7c77d9da626c98e0ab2aca14c5061796d656e74546f6b656e1a3890bbd1028201d81858eed87b9fd8799fd8799f9f581c59184749e2d67a7ea2ca31ef48fc0befb3c3fad5a88af7264531ae07581c1be17c0feca072beb5a395a286ca4bf269f74f33db80509337ff3dbd581c564b2400e0901e336538bea2eb48f0bac8aa77b0c3a948f07cd15ff0581cbd05c7354d160113edb39a32a2ce6045f48f51d5728ffafdae2decef581cbdebe65210d74725fffa6635f28ddeae6cbcd1a8aa6898ab75cfd5e3ff1917701a001b77401a001b774018c81927101a000493e0d8799f186418c81864ff021905dcd8799f9f581c1a550d5f572584e1add125b5712f709ac3b9828ad86581a4759022baff01ffffffffa300581d703f89c436844cd0acd2714201817836b2cd16b082e03e3a21bb69e5ad01821a001e8480a1581c2b556df9f37c04ef31b8f7f581c4e48174adcf5041e8e52497d81556a14a4f7261636c654665656401028201d8185823d8799fd87b9fa3001a0005c7a4011b000001926600b8ef021b00000192661c302fffffa300581d703f89c436844cd0acd2714201817836b2cd16b082e03e3a21bb69e5ad01821a002dc6c0a2581c2b556df9f37c04ef31b8f7f581c4e48174adcf5041e8e52497d81556a14652657761726401581cc6f192a236596e2bbaac5900d67e9700dec7c77d9da626c98e0ab2aca14c5061796d656e74546f6b656e1a02a74bc3028201d81858d2d87c9fd8799f9fd8799f581c59184749e2d67a7ea2ca31ef48fc0befb3c3fad5a88af7264531ae071a00838e05ffd8799f581c1be17c0feca072beb5a395a286ca4bf269f74f33db80509337ff3dbd1a00822d24ffd8799f581c564b2400e0901e336538bea2eb48f0bac8aa77b0c3a948f07cd15ff01a0081fd52ffd8799f581cbd05c7354d160113edb39a32a2ce6045f48f51d5728ffafdae2decef1a00a66f7cffd8799f581cbdebe65210d74725fffa6635f28ddeae6cbcd1a8aa6898ab75cfd5e31a0014151cffff1a00650eb0ffff82581d6059184749e2d67a7ea2ca31ef48fc0befb3c3fad5a88af7264531ae071a006e2d88021a001b26b8031a0453dd94081a0453dd1c0b5820d04e736b347a342ad321aab49d0a731d1fe97b8cadc5cfad8d0954ffd826a22f0d81825820157dcc88aa4a0278d4eebd6764fdd3a7f7d53716d298d5366cb7fff5b1245539010e81581c59184749e2d67a7ea2ca31ef48fc0befb3c3fad5a88af7264531ae071082581d6059184749e2d67a7ea2ca31ef48fc0befb3c3fad5a88af7264531ae071a00524801111a00370c3f128782582056859125fa6486f85015233788e398546004743bd6af30692e51ce7d464cc9b100825820c9a9d5b5b100c69c3eafa6cf5b9d48c8b49251bd5cf7f78496f4b90b852fbd2d008258204d3550e32ba844e8e11df2bdca827cf11eaad9f35aaee2b925365de452ceda4900825820157dcc88aa4a0278d4eebd6764fdd3a7f7d53716d298d5366cb7fff5b1245539008258201a28375b01a8928f21a87598e24165ab5806e7c3e77a5eb4d78c56e3cf07894c00825820a9baf90b688b11a30dfb0c29b8cdfda8447476a5a87dff05b451aa0d6c40be6600825820991b8a8b8f3d2423d5c7bba99b7698a7a4d68fc62681b4d8a52ff7ca2822edf801a600818258206fcd3774965658d24f131cea65bca5230b8014f0022652a978fbc4b1fffcd831000181a200583900eb21979c03eed7207020b2a0b47565b5aafb1a2c3849b59a1fa8e6c5e075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d6473903011b000000024fd8978e021a0003997d0758209d9ab1f617ed24eda3f6acc4b6cb4dfc0792ef85da7cda5123d9809e3d16101b0e81581ce075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d64739030f00ab00838258205a3450aa523f773eb21e3a047c61aea074d74396ca65f216bd358840d7babb11018258205c03aab3f3c954d6ffcdda26f2a84cca50170484773b15ba0c6b26f1f34dcfeb00825820862cf9c5c614b27d95f2893c77f8cba673b273d3be714401cbf1eb3fc0079bd7020183a300581d70a74ce540e819f246e6f65f3b4597daaaf9d37c0443fbcb13618a10c901821a001e8480a1581c1116903479e7320b8e4592207aaebf627898267fcd80e2d9646cbf07a1484e6f64654665656401028201d818583cd87a9fd8799f581cc91e84382cbd1f0b5e407232743d80894e7aae5bec7c835334841e72d8799fd8799f1a00058ac01b0000019266010bc2ffffffff82581d60c91e84382cbd1f0b5e407232743d80894e7aae5bec7c835334841e721a0089544082581d60c91e84382cbd1f0b5e407232743d80894e7aae5bec7c835334841e721a00a6ab3d021a0009ce9c031a0453dd94081a0453dd1c0b582091b0d6dba1d88661433fb0ed0097faa20ddc563980b6b66039fdb211cc8a30ee0d818258205c03aab3f3c954d6ffcdda26f2a84cca50170484773b15ba0c6b26f1f34dcfeb020e81581cc91e84382cbd1f0b5e407232743d80894e7aae5bec7c835334841e721082581d60c91e84382cbd1f0b5e407232743d80894e7aae5bec7c835334841e721a0079705c111a00370c3f1281825820b31179a89f31d9b08bc0f075a4661c72a31c2270eb27ce42f9d4170180118def0085a20081825820c58b6e67ad72d3e4de04c0c143b15e0e60573ea9b740cf0a8fbfafb0475d50fd584076b53453d37a56ba653f93a741805181973fb19a62e6668481e82a82296cbaf539cdc2f15d2678499abe776cccb27bdec9a0937c63f6cf3846c611aa2287de040581840000d87980821a000669651a0a708998a200818258205401b7f67442e6cc870fbcffe921d24ab03e97e03bb655a24b4f424fd832c615584023858e80006b6c668029a3de035a1e993f887f1a525ece7c3fc27491cff587655271f3ed74f9fb240aa0643091cc2ac6caccc7cdf9ef66bbbd732213409e480f0581840000d87980821a000669651a0a708998a2008182582047075e86572a64490e20b0d020f6e7a4cfa74c591ac057ec59839d30c64471745840d0bede6b68fd1dbe68b61eeb6d6c13b6f0dd36cc56ef7becd3c6e9349fa10c4ff5d6c96595b99373cb067a744f297f5cb2b1bea456e32d3c37396e43360d0a0505a382000182d87c80821a009f8e921a7f561c3082000282d87c80821a00091aa91a08e642dc82000382d87c80821a000bfb2c1a0b89b757a100828258208469288efa6f9cb49040b43dcff93f40969d72433f751afde50235012c016020584038522b52e1abe30dd9d587abbee42ffe62aaa3a7f99cdbb07a9c3e9b73a365252ac2cf78aa26cb117802ac6d7fa34f716060d526f3eabb9979e58e3b4de1dd0782582076af5530fa318a370820270031d1838545a4ceed8696510627563d1114d4182b584007230be41958bfe1a41c9de16f804f1f82cb6ed1de7947b07eb4683bd94204c07f2b02ff9c8b74a5420a664c56764d4983154ec93b946778707882b839aca805a200818258206b7e1b2d051ed3f4651688ffb184e6d0b415b0b3025ef63c57d231404a73564458403e7e6eb19d2377c2603e1df5f5ca96bf9443203547d60ab19494ece629785fac0dc7c4786fc1f7b8248246a356e8a867cb23fbce48d1984c0187335ba1fb7e0a0581840001d87980821a0012c8941a106a99c6a103a11901fda50050ca7a1457ef9f4c7f9c747f8c4a4cfa6c0150226d126819472b7afad7d0b8c7b89aa20258204d3f576f26db29139981a69443c2325daa812cc353a31b5a4db794a5bcbb06c20b9458401b03060066006fd5b67002167882eac0b5f2b11da40788a39bfa0324c494f7003a6b4c1c4bac378e322cb280230a4002f5b2754e863806f7e524afc99996aa28584032f02b600cbf04c6a09e05100880a09ee59b6627dc78d68175469b8c5b1fac141a6da5c6c2ea446597b6f0b6efea00a04ac0c1756455589908a5e089ba604a1258405917d6ee2b2535959d806c00eb2958929ababb40d681b5245751538e915d3d90f561ddcaa9aaa9cd78a30882a22a99c742c4f7610b43750a0d6651e8640a8d4c58402167427cfa933d6430c026640888210cd0c4e93e7015100300dcaef47b9c155ea4ccb27773c27f5d6a44fbf98065a14e5f0eca530e57082a971cbf22fa9065585840ae72e2a061eb558d3fd7727e87a8f07b5faf0d3cedf8d99ab6e0c845f5dd3ce78d31d7365c523b5a4dfe5d35bfafaefb2f60dd7473cbe8d6aa6bf557b1fbdf775840bf96bcd3ffdbfc7d20b65be7f5c7dba1cf635e3b449bb644bdfc73e0e49a5db73edddc7ed331220ba732f62f3aee8503f5c6f9bd5f7fedb37dc6580196052e50584027fdd7e8bfe9146561ad1ebc79ecef0ee1df7081cf9cd1fd929569ef3d55972d5b7ff882ce2213f789fc08787164f14aa86d55e98e332b220a07fa464aaa7c335840ce4bcfb268ed577f72e87fdea4442107bf2da93fe05121d5befa7ae5aecc5f3f9c732e82108003166380198c0146b0e214114a31d7c62b0ec18afd5834034c2b58402b2c515b350d8980a16932071b6d8d125ea1eb53dc28a8aee1787a1670b9e8c4c8cb00c726f3515a39ca1689f870295752820a64721e05e1a234710583416316584031d80291ac9a2b66a04cba844b85f9928a4a04a9928b2805124a25b3aaa4422e45e5d422a9b88a028ba4a5123ac244b8b472164b86085ac21357c3aae7696be25840f1104878009b03813d9e6c53255722402090206058a009d2b808aff772fb712d75f1dea09507fd83838e045dd9ce3eb59e4554f5ed02b8aeb60700f4b39dd9fe584064e1d5a137de0fa4c6cccfd71f831bee372756d72990b357a44e2f9eaf3854db65379db466cfcb55517ba71550acade564f4b7efd1fd95fa57228cee6fa9ae3458405ce1ae79b77f7cd5bdecfcb800fbdb7eaf720eae5995176d94a07c326c71aaf5e6f8439e577edb2d1ed64959324b5a7476e9159bf37bdf226edb747787b79b9e5840bc6ab5b84714eefa4a8c2df4aba37a36757d8b39dd79ec41b4a2f3ee96eabdc0e1f65b37264bdbfdf79eebbc820a7deab4e39f7e1cbf6610402fd8fb55fbef3d584038226e4d37c42970c830184b2e1c5026eadb9677ae8f6d300975ca6ceec5c8920382e827c1f636f7dd9f8d492737f4520a944bfeebba5ca2d5efa80ad453a43f584004c357ecccfc4dab75ce560b0300db9092ced52625d0c8df6fc89da9a45b6dc9c2461f21e6ee7b7afd877fbd8c1a1fa7ff38fa506e14749ebb68e24571c6220c584004208c284d628c2148b252f91b8b50014b080b040554095b52ca862bb974218222d412112ae5d2584c54584ae157f22b183cb4ba9c5fc42ba6894ad074ffe0875840c69ee921211d0ce4cd0f89b7e708163b3ab9286fe26a8c68ed85930cabc5dbfed7f9681c535dbdbfeb56f7a2b32d1f43de1dbcc934676edefacb3df7c1210067584064a1b8d94448b7f22a77dc736edb12f7c2c52b2eb8d4a80b78147d89f9a3a0659c03e10bbb336e391b3961f1afbfa08af3de2a817fceddea0cb57f438b0f8947581e9782ee92e890df65636d835d2d465cc5521c0ec05470e002800015eecf5818635840e0427f23196c17cf13f030595335343030c11d914bc7a84b56af7040930af4110fd4ca29b0bc0e83789adb8668ea2ef28c1dd10dc1fd35ea6ae8c06ee769540d80 \ No newline at end of file +820785828a1a002ccb261a0499865c58209bf00c1465594b9cbc4afcf4ab9abb70c1c96732407d5bc07b30b4b30e5bc0a15820de38e66a4d84e9197670fc33e571603b5c54f6b20067be727c9f6027e11b56b7582040f01e8e7c3dca1cab6bdf3be3b95b1966b6f8ba74bf1f2b20f647d77b21e272825840db68ec72a5b6b89e587ef774610b8c2a336abf35e2563e14638648333389b535248b0722657adf594dab1d8ad191f317d94adf8c06d93097952f30032fc5e5975850db002cc64f44772f221b37fb1cf74ebb451e329259a6744f2476f88bea536b39023234901abdde450a012cf47f26b6d385ea4f0e93ea64ac06bbbc056452fcdfee4a8ecc4a674542daae2921d8ded2031906e75820415d178ac638757394f411aaff696d5448926c4fd87dc8394d80b929c802a9e184582055f458677d1f7280698d9caf1df339d5af2972ec3757be29372747bbf8ce2c5f0519022b584024cfb714a8d74800f267785ee123ae49ee678d12b4f4b54799e176c089fa4a9ffc2af663346d2ad5e6e5a12044a155608066dac87058b74e90b2042abc6d170d820a005901c0fc80e58656078f05dc94a7d1c880c50c972e22462fc85e188e6e6f318e7af888abc4e31e7bf8888ce11e147d93452cf129886d201ab6a73b01d6eca26dd506027b6212ebc2e64fb3494980642daaaf374307e9d52a38a905c09d463c337e37c3b27667ec1fe958d860d31edeb70682a03a40dc40ed541ab68e887e01590bb693b1539bafebcaed000308e78f0ee7b41124b5cc5b5bdee0d93f967a9f4fc0b561222d22bb9ddce32e6fe4540607dc2bc5007e70ee4565af5c4eac81ad8e398eab665c595eb954c89a3ae27214beafc71ba043a2209fd2689e5748d03f9e4fa9c5b0ffa2f7f83833801ce5e93e744200546017894abb44ba15936ebca8675fbfee2df23b3d1bdddea6f01a2ba1d1dd578da5bbd5a13060dcb23bdb28654b6ea734d9d810d3a48c18eb7779da2f939dfa61b0b29c934fc04942769dcb397dd90f841c82b63a76028fb575d6eb48101a784546381822df66aa2b001045f2493001b794a5a308b0cb79054f593f99d5224ad2ed87e9822537b5699aed45656448be056a7a29acce6560bf8aa2b74c4834fcbca6399785f49f25efbf05698838dd57128aec11382f4b74d6c1fda4bae6392e74ceb99a72c591c2c21d501a3c7aca864482ab0084825820aab6bd3323da74be3ff8a5dd4032fb6f8ce7452a2d2abc0008e816db96ebff0202825820f28d02bd171a11fe59dad6744f742d8d96e69f4dfa59c9c3936fae3062144bab00825820f28d02bd171a11fe59dad6744f742d8d96e69f4dfa59c9c3936fae3062144bab02825820f62e2faea6659472b4b98415367fc143f64d935f7acf5186129737e20a50c063020183a300581d70b07d22a4dc75abdba1b8c80033a15b85305b76521a0114b17f291a8701821a001e8480a1581c362e3f869c98ce971ead0e2705c56df467ddd2aecb44f6f216c3e1d5a1484e6f64654665656401028201d818583ad87a9fd8799f581cbd95d582888acda57a20256bb03e4c4abb6bdf09a47d788605412c53d8799fd8799f1906d41b00000193761d6992ffffffff82581d60bd95d582888acda57a20256bb03e4c4abb6bdf09a47d788605412c531a0089544082581d60bd95d582888acda57a20256bb03e4c4abb6bdf09a47d788605412c531a00bc0b8c021a0007ac07031a049986c7081a0499864f0b582068f0db238ceffb3af04581959c68b310531097487ac7a9b4f8e1d850d01c92830d81825820f28d02bd171a11fe59dad6744f742d8d96e69f4dfa59c9c3936fae3062144bab010e81581cbd95d582888acda57a20256bb03e4c4abb6bdf09a47d788605412c531082581d60bd95d582888acda57a20256bb03e4c4abb6bdf09a47d788605412c531a00524801111a00370c3f1281825820180fb7cfedba19697b4d164867c49ad8bf2493f6dd51d860aa03cd3e46301cb600a6008182582081a561fbb27af9f739c1893e83ed232c582bc70ee7817703743af9fa0e7a06c6000181a200583900eb21979c03eed7207020b2a0b47565b5aafb1a2c3849b59a1fa8e6c5e075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d6473903011b000000024fa63d6a021a000316b107582065328b47a7b512ded7757865e3755c9ba7cd6161ff5142390fba09b489c63fd60e81581ce075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d64739030f0082a20081825820df06fec0a22f6839e6637f11dabb6c98995ffb30136983fe240e76d828a8017f58408870329282aabe756fae43d511bb70eb47bf8162afd108457078cc09cf12e0ef3a3ef484d93432f613fdf94014dddccc2d9d5da12188c649c6ac5cab3080030f05a182000182d87980821a00132df31a112de183a100828258208469288efa6f9cb49040b43dcff93f40969d72433f751afde50235012c016020584052f1396834e485d805a5bfbc5255b89535cfb1aa36344f69fba03304b3fc28182aeba41e304b5e6008fb43ebcb3aa192bba42fdbe55801347930ddccdd067c0582582076af5530fa318a370820270031d1838545a4ceed8696510627563d1114d4182b5840a06561722f5a03d015cf33f7d44984f8bb21fa96dc157dd7659464e212b36b2baeb3b68039977f388f012a64a62256db3ae379021c8541de211ad175f51d1d07a101a11901fda50050ca7a1457ef9f4c7f9c747f8c4a4cfa6c0150215f9394884fa3614b6b309dcbafaa410258204d3f576f26db29139981a69443c2325daa812cc353a31b5a4db794a5bcbb06c20b8858401bed020064f03bdf8a48de44553c0c4e47f98bc0d14d7dc0477ce5311b13e8bc89638915615816489e3e606e393783ae05ebb25162f675b28a87bad0b682126d58406373d47d399d46a5d1142d473d1af3a3f10b5be75d4bde3c343115d1cb8fa980c9a2fba3053014f2019745efc060c9691dda413990e68623e09d005005140b7b5840b6404813a00c48a2e009b82d065d394025900fcc1708167e9181e879761053aaf43d30a98565e5212cab02ca832ab0725788acf06786814fa857e956ca704e205840e69bd2a2ac78731e6b44c5335f7a44b0fd55a87d79a1cdda7270ddafe22af411a077002dcd63a39481e60234abb4635a92bc88351af09f2003acb86409b6c6b658401b10edebb7e1659f5cbe6a38dc2712c1c2b8656c2ffb3b8324865726008ef72937a0ca804415908954200a58a15c9cc40646ca57ae841189b262a698ae18c5725840a962bf68ac3c19c7009f6357cc60d069cc47c0d8532743c439c7cfa232996c3a75fab412247ac850388df73bca2a76f8b3a1fcb4dfd54d332f543bd1d5abcf675840eb50d6677fcdf8f26ddcfb758710d1aae18c33e25ddc931f326555175cfb76e596af36110e6ead5277e07f5d615deab4aa8a6d633b6cf499a79dae8e46036e9e583d2e3edf2cd098d0ed91ccce965cdd5fcf27accb1d927f742ff939daf4eff5c0cfaca38fe77e7a7af16aa34370e52b4d04ada650a8251e856ee145fe000218635840aee26347b9702d53b82f157a57f3f4960101e8e7e104215d587da715748196ef09aa8e79da3f4f7f7e2ffc5a32be6f46d5f4598c17309a34bb12c5bcd6e3f50380 \ No newline at end of file diff --git a/rust/rbac-registration/src/test_data/cardano/conway_2.block b/rust/rbac-registration/src/test_data/cardano/conway_2.block index d38c51b885e..51e86d94ce9 100644 --- a/rust/rbac-registration/src/test_data/cardano/conway_2.block +++ b/rust/rbac-registration/src/test_data/cardano/conway_2.block @@ -1 +1 @@ -820785828a1a002a259a1a0453ede35820b2acff53e862876989980fe9cac80548c2c3ed417951324c54d69b4d86daf5da58202f5ad4772c00767b6d75c8c1fae31e3479d91921abf41b1989346d2896b61de258201003b81af09c4acb552e0fbb33ffe12415360f2842c0e020b11b09cb82f7c5e48258406db3ca895414ace8710f345f8e93536ef2f24d7d6d2fb29e50a8844c83fe2eb5f64aa72968d70d681f9b01f4fe7332ce675a6a809448bd6b95c1f5cb2c86011c58503bb668495b649f05239672b7b8eecf33bec1cb060d1bdf5a1676fdd4b03d4218e6e6e41c87ff6f22759ccf4da65caffff14c1099ea7d4e2b56f76de67f33d80e72212e7a709db122a7ca33ea53690c021914ae58206fc671bd90b0d6955b8f48e9f8195291ffaedadd191d77884adc695edadb4853845820873bdd3584228477e9d9ee6b60a9cd6000627acf154126162178860ba752e9cd0619020d5840e461305c25b0690c1001e96f882d51c3a12ca32bae4cd25f9f46a98a0e9a7fe9b43ed108f230393c474444ce5ea88cc1bd299b94e7a60795d883e0a16004c0068209015901c06bb09e2dc5ca2e8e8b4124323489a64dcfacb9f80db18c6ef99f74a700d033b44fa89ebd120443c94312a92b9cf6e59c49541649c9e2e1d51d3ea16679d9f4078efa53d0f18fde0a05b87670b6f58d8d1e749f02f193d4d6ce790c21ae001f1550d19b5a842060a4d5d0120e311886a172b74585f7dac0ed59d1ec45f66ed0c760458b7616e67b0df1b5539d0eaea60f0230b5c9e50097c918559a85d57111b13b230b6f222b03cf501ad53911235154a7f0dd6970e7a3a16036d351b4aaba08cf2dff5f669ab4795ae5484935ba59e01116e16dc84b1eed1a4613ebc17d3f8ddda8113b492fcfbc85edeee2191051abff6a6f91d4295964764e8696623ac78ee14e248a3bdda80e04c28e0024f3922993bd4642d92de6c907ed6f0f9371d8d7a7c4c45091f33b1389b0aeedf05f33c9355196326f06b6264c3504d60c2fc790044f7d3ecfbd9dbe013ea5dbd3c2b7d572dab25512dad0dae1cb96a49a0d3b1b6f5c6f71d4118b6b032db7096e4044d6fb78861cfd0187475ae7720782a881555e93cb843f7db6a80ae06161a8d373325fd19a0efcf6bc4c76635513353213c1d9d07e1fa563adabd17558a2ff1fece92c75a0957b3592ac2816f4fd9ad8773d87a500d9010282825820020363f4cfc9f3a1b7594e064e74f5d9de740e25b246de6b01d4f18c931c99b318198258204567c47047668e8f0bd8f536bbca83b1de2fefa9148fb25c45d08859e29122a00001828258390069241e2a942d7b68cb729426261c1b0826982443a6b0702a7665e98979e7d3f2262b8b84c28cd004f11c89d98bd515ee0af810b9c369034e1a00596d488258390019c0438fe64e704bb98508e75b156fddc62d6e579c77d86ab67ca5af79e7d3f2262b8b84c28cd004f11c89d98bd515ee0af810b9c369034e1a007974e7021a0002bd51031a0454098f075820e84a468c26df3ac2f928642aeb3b85bf2609ff7f48888cb37b5a7d7edfd0ad8ca500d901028182582098069e04919bc65917e47a4e9027e0f65cd56ecc9779eccb0a9cc29fe17e171f182201828258390069241e2a942d7b68cb729426261c1b0826982443a6b0702a7665e98979e7d3f2262b8b84c28cd004f11c89d98bd515ee0af810b9c369034e1a00596d4882583900b1d55f9f64c24cfcc88aa5b49db76ea43386b35b7f2e80966bf37ace79e7d3f2262b8b84c28cd004f11c89d98bd515ee0af810b9c369034e1a002d4133021a0002a5c5031a04540993075820fc22a7f998d4a138fbca3bc375f6f2ec040152b965369c8997b90d3439b6c9b6a900d9010283825820b3458ce9b7e9f45d5a6c34ed20105fa899fcd5b8fe19d82a7468c88cdc64fc1504825820ca98a9e90895b7e8af44e48ea7b0c3ed837c5ed0c0802bc3f5bb4c35543351a001825820ca98a9e90895b7e8af44e48ea7b0c3ed837c5ed0c0802bc3f5bb4c35543351a00501828358391044376a5f63342097a4f20401088c62da272639e60644a9ec1d70f444358a4e4105c08f59e4779070699c7a72566893332f9857db4e742beb821a005d1420a3581c53827a77e4ed3d5c211706708c0aa9b9a3be19db901b1cbf7fa515b8a15820c76aa2e728cac2a3b9f61f88f0fdc6c603c367696596fc1bb6746983bb9f135701581cc6e65ba7878b2f8ea0ad39287d3e2fd256dc5c4160fc19bdf4c4d87ea1457447454e531a02a8ec66581cf03eca9ebdd30db76c5d5c0b450c97bbb4faccbc67cbf683e483197ca145745553444d1a03db7e7258204e86ed215442ac3225e8b68e43d4942be391c1c165e224ea1cdcaaaac3d070c78258390078886065c99d810efddc589ffb760821e57314fd3760263038a12096f3213e19402ff9bd59d0c506a727c34ae4b12ad5eb9c9706def94db8821a35a33db7a2581cc6e65ba7878b2f8ea0ad39287d3e2fd256dc5c4160fc19bdf4c4d87ea1457447454e531a00d74746581cf03eca9ebdd30db76c5d5c0b450c97bbb4faccbc67cbf683e483197ca145745553444d1a1cbd7691021a000615140758209f7a0691b5cd8c61203de22560c3290776c33d6f4de92717d6ef9a813d9228620b582057dd2ba2828bbe242b69bdd76b11474b997107d19691d9f518b1be9954fe7d210dd9010281825820f9e68746ebb05beba703c12fe31c97a9605196a5bcbf470b4d66210bb4122aac04108258390078886065c99d810efddc589ffb760821e57314fd3760263038a12096f3213e19402ff9bd59d0c506a727c34ae4b12ad5eb9c9706def94db81a00432ba2111a00091f9e12d901028282582016647d6365020555d905d6e0edcf08b90a567886f875b40b3d7cec1c704826240082582016647d6365020555d905d6e0edcf08b90a567886f875b40b3d7cec1c7048262402a60081825820e33e183249d63e516c7f5ec8b41c89d4fc9d24b2626c441407a6e78d0784e95d000181a200583900eb21979c03eed7207020b2a0b47565b5aafb1a2c3849b59a1fa8e6c5e075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d6473903011b000000024fcdca3b021a000399d5075820377701c2f4f80a44c251a3bc8e5e12244cbeffa5c26d9a959a93642916d2bf890e81581ce075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d64739030f00a300d9010281825820beb5f158de66dda9a234e887c27eb6aa0961afb9d142445bb30a05f0a0604bec183d01818258393040fc34adeda87d8eb4c7f3bf1b811b95fa835d356aa129a0089822a4d448965f62c0afafd791b02435b2dca9f6215df84d3c30a5ae35da261b00000002540be400021a00030d40a500d9010281825820fe63bacd187869d23260b844e7c6ffab508b411786156c0f0265ecc99593ff81184201828258390069241e2a942d7b68cb729426261c1b0826982443a6b0702a7665e98979e7d3f2262b8b84c28cd004f11c89d98bd515ee0af810b9c369034e1a00596d4882583900dbf48c2c653ad449fcb447670692bad9d9e28689f59d2f5aff908f4d79e7d3f2262b8b84c28cd004f11c89d98bd515ee0af810b9c369034e1a0091e8d7021a0002a5c5031a045409ff07582065e215890ab4d0ed1e1c1386d3d4b56fa1db85cabed224fcf4da3fe90b8d5f9da300d9010281825820beb5f158de66dda9a234e887c27eb6aa0961afb9d142445bb30a05f0a0604bec183e0181825839007eb505be585918dea709480a7b3ff664c72b77f28e8337e7c92d71e16de8be2814a24b0f2bd8918faf3d07685078b94e14303d5333ff4cb11b00000002540be400021a00030d4087a100d9010282825820081397fd90c2cd717f732e85b9df6a60f258b468e633583b6e847f27c6238fcc584067d605eed13606c8298ea57176e0396af8a601e4321866a59c6b970c16ab777faf192a20471278d7e118704a0d13d7d1449b5b9082bdad0b8c6a343a6cbdfc0082582079d1ab518df51b5584ab6710e9b06f72d7d292008252aaf20b1f00821ba392655840f3e981bec4eee7d8d9afa2c9d662b4071f46db5a8c9384a63a766a8acb355cc60faae6b50b2ab64772ef65cc756c40b16174368fde166ec1fe08ac240bc21d0ba100d901028182582079d1ab518df51b5584ab6710e9b06f72d7d292008252aaf20b1f00821ba392655840fbe829ffb25dd94bbed898eb5c6f7baec81731641073c864c516348ea9bc98e1b60e1d33859df31291c11bd1c0bfff5d79988681657117c6f9651fa97c72600da300d901028282582025333b67631e94b6c5cf329fd664eb8952d9521901863b07930892b5b1b793c65840d216f8ff58a379a5671bd741b0791682a5729426d41dd1feec26ea3dd1e739fa9e9c53ef7542fdd74c2da7e1e4fda98180b82e4b6a4909bb09ba70b7c243a307825820e8ad851eecf7d7af03934a076dde98241f081b7dd2a482309018fcbb24404ec5584093cd8d4fffe095596f008e5a5aa301a59768ad3a799cba3b2e2724e8bbc3ec9617b4c90e393555792a7ea85616840f5a3f38d3148cb025ec9a343e7afd1c6f0c04d9010282d8799f581c25195af85c41b9d97da7f4f215d3e74c9cef7f04739d6ba473ba72a2d8799fd8799f581c25195af85c41b9d97da7f4f215d3e74c9cef7f04739d6ba473ba72a2ffd8799fd8799fd8799f581c358a4e4105c08f59e4779070699c7a72566893332f9857db4e742bebffffffffd8799f581cc6e65ba7878b2f8ea0ad39287d3e2fd256dc5c4160fc19bdf4c4d87e457447454e53ff1a077359401a02a3338ed8799f581cf03eca9ebdd30db76c5d5c0b450c97bbb4faccbc67cbf683e483197c45745553444dffd8799f0405ff5820c76aa2e728cac2a3b9f61f88f0fdc6c603c367696596fc1bb6746983bb9f1357d87a80d87a80041a000f42401a000f4240d8799f1a003d09001a0005b8d81a0001c6b0ff1a03d9b7c2ffd8799f581c25195af85c41b9d97da7f4f215d3e74c9cef7f04739d6ba473ba72a2d8799fd8799f581c25195af85c41b9d97da7f4f215d3e74c9cef7f04739d6ba473ba72a2ffd8799fd8799fd8799f581c358a4e4105c08f59e4779070699c7a72566893332f9857db4e742bebffffffffd8799f581cc6e65ba7878b2f8ea0ad39287d3e2fd256dc5c4160fc19bdf4c4d87e457447454e53ff1a077359401a0361efaed8799f581cf03eca9ebdd30db76c5d5c0b450c97bbb4faccbc67cbf683e483197c45745553444dffd8799f0405ff5820c76aa2e728cac2a3b9f61f88f0fdc6c603c367696596fc1bb6746983bb9f1357d87a80d87a80031a000f42401a000f4240d8799f1a002dc6c01a0005b8d81a00015180ff1a03412142ff05a182000182d87a9f1a00bebc20ff821a000f49ff1a13bd0568a100828258208469288efa6f9cb49040b43dcff93f40969d72433f751afde50235012c01602058404a54c5c6b967a7c10fb561b63b898759faad058302fdc8b964d68b550cc5ece049b379ade714d07170e19c030c93c809bfd8568b24a9183f1e6939ef368e1f0182582076af5530fa318a370820270031d1838545a4ceed8696510627563d1114d4182b584057bf74e35d877b23ddfa939d6efcf157c09e3df9a4d5960f30fc64b6931e15e7d9caaa02451c9895644bafd3f36b5caf4a64a80668d21b97099943ad11eaec0aa100d901028182582045a35a111726f809cf2c33980ca06e45d29db1b06153c54e6eaafb6e4abfb2e958405909ae97282b146c27d506eca06e4a986703e4a5171c2a11c4896649768df5fb08a2f5b91db0766f2fca122ad981b271281e074f6fc4a8c36c804702e4fa4009a100d9010281825820d8229d65d9939ea5113366904b94715a816b9f21b128c4a95b6d15fc29032d265840455baffb3b01684ce9b461456addb02a3b7c35a182b92b7557178d161585236d19c682696c2839df4f513ee2906c5fea516f8c0d460a6c5e5e6cd170b0195d03a100d901028182582045a35a111726f809cf2c33980ca06e45d29db1b06153c54e6eaafb6e4abfb2e9584077feddb21c3d1ccbb7a23489febea4456c541f63fb7f32ba2ae0d79765c9f540edc229677fb2c067a08f2396fef7fc04c670e58267848efea4344fa977258d04a500d90103a100a11a000bb3f4a16376696478403938333831333164336462346535316531626237616232656334613661383037386334393266663637383062393932366435383030646466306464346633613601d90103a100a11a000bb3f4a16376696478406136336433636264333238643834326465373031633132363339393964613066633831393534336564326363636437363363643530633032383764373466623802d90103a100a11902a2a1636d736781781947656e6975735969656c643a204f726465722066696c6c656403a11901fda50050ca7a1457ef9f4c7f9c747f8c4a4cfa6c0150616f4c5a8adc62d5634102eeb7faea6f0258204d3f576f26db29139981a69443c2325daa812cc353a31b5a4db794a5bcbb06c20b9458401b0306006678baf9873001af26aafd1674e5b11bf549d4bea0a39bfa0324c494f7003a6b4c1c4bac378e322cb280230a4002f5b2754e863806f7e524afc9995658406829cb08ab014e580e707468318ac0086262ffa6d3039a557178a3c556cbee076b44163b02b2812e49d3ab27b5e7bb2e0c6b1321d042d1069a24b4884032468a58400a28c6c0b0af3474b476028a193051b0641d5d778162040c24d7d2d1cc925f3d504c8171e5b4757466bc5104a11147eeb110f17d6067ae4b0d04a1d4200a3590584042a59443fca0277bc8e0b053c8101ca338ac0fe93e702d048761686fe9bdf0d511e472c6bb85a27f033a24d745712b933dbe1c51141d6309d9cb4fa9c41f2acc5840995aa78f9877abf5fe4c55fd4ed2120cbfde107fcefdb363e0e2e35774f7357a6ec99ada7c55e70497f29bb3e860cbe05fd8b1e3e6f6193b3853ad6afcfef77d5840afa5ef4cff767e1fcb1e347afdf176f2f3acd76e99d65d192fff1c3eb37eefad4f3777dd744fab80b466fecbcc79e11670e3d281afbf0e3a3f0b5e8d30e217eb584092ffeb8ddef4c8b073d44ddc36f3ef6ed1df5461d4428bfbf43525effdb557aedc74ecb2d1522bdfd9b5696059fab43568cfdaa6dde757a7d58f8d4ca773a2cb58402ec92e8406f6aa7f8607fd795394c13ff26d79eb613f96cc50fd7acddef25f3f3cabae20280cc1cb70d806e0b005509e25a145b68604e551a03c844733c00db25840c212b5b5d3900818fa2673b0d1d32e6dacf77e8a1b755c5eb8bf7f1d8d864d8c9a0c706c8a000b64626162b10034d1a4744c28573452f9342c50c20a0e0e0c695840080e138ae55c9a24180b0992d2028040ca62f3f91c85985a27a5b243387496428629021a64120e1588654dd2207e089f16c80c0d95f3852d69947081f679dafa584004028a101f005b207025906daae14d828944320a0f0b31417a17114c5eee3a65a02b3990b0e6486c62c2f104d5aeb0b39b5844b5df2e0c0df43c80d19fbd21bd58404f96a827de443e9059867cbefe18d97cbf339fdba2e45f50c93c5e3c5f7db479c0655ec1fafd2fd7661c98a744b39dede7647efb9e39ca3655a559dcdfce659b5840aff6d5bf3b3a6bedbdcada9c38d7db31afaa8eac5f5b936991a779266559b1e3b6dca3feaadf2ecb268d8a962f202fbdb042e574e9c05bbecf4ee37da9bdd6675840d40ecb86c4a9deaf24f892989600ff8e816f59e7fa4f3ec854f6dfddce3d001febbd75634e536fec818bfd2a4c14d56298fbf4e3ac29b3b8a0ba035f0dffde0358406939eb07d1f03ab732b39985f5639a24536766ffa6ec36ab648ff2ce619c223d2ccc7c92e8ea74dff7d94cd99ccdc3d43a4dfa7f63afab81adbb39f126cad2875840482ae68dc83e43f47e9d1418764ea2f7afb3a2a34b177f43ee4cabd4617b053ee311dfd677ff70ece4af9719b36605fd38f15061917089b674f68b69654b71045840467192ac80318630c94ae56f2c163436b11a58805dc71484d2316aa8284c2ce38a25f40601d6100c42802c8015868582c05045a83c882e14622209b734969068584057bc65eca05168123b2db2fcc886fd6bafcddeebf1c2573515e1aaff3981776ce9a7339f12e6382207df6f3c7e3df750de8f58d753bf33ba2877a4ab8e5e5e435840b6b053deec20963a4430ddf75fdcb9dcb77ed9eacb5e3c5d6123d5546afb286165f8e1d4258365b73c2d3cf0dcd75f30e5a548adf83f176ff527bbbe09cc1dda5820334badf960b27d7aa97150b5db2ac882a65c014130ae05210b0075d0a2fd8c05186358404d9ecc0d2e40dba79d29e8c1f8b264b93ccbbb60a49ef419e12827196991aedfddfe44ca0388415b708579c060fd8047d96e6d68a0d7b8f2e6a3b9448fd53e0905d90103a100a11a000bb3f4a16376696478403638393139396437303165383961333138336536336230383336653230373062343131313538333139396562326237393934633038663762663332636365383380 \ No newline at end of file +820785828a1a002ccb5a1a04998bb05820c2401f491a275eace00ee76cbc4265323377eeda7f18b26a0b6f3e7b237ecb3b5820087932a594533b96a2abd339715dd222835d36856ac2c0b8fcd171e3ec944b8e5820b5b14e8eb25a9366e0c1a43afcd8d0859249202f3a8dc03b13f416c70d4de7d7825840f40b889e2dff194f6a6b21943b190b3aa2549698b13af0856dd490a74be701c9a45e700335fd7ae03a3fb732ec4982c529909075ccb31c6d4741ed921c0e44c75850502e08f267c7ba9abd821d099ba1afec773280bc89867ebf3045c646ba497e18c8ca2024b22a78f5ff055a674f8cff840c43b2e7ef7976b0e7d791d17587b410912adb655b8b92fcd1b556a99239e207190466582026900aefbc5e59ffeeb1cd80acad7a15479095b2a3068e42f74aa211669afd60845820928c2533874afbcbd5f11379eefe8fd038039b6390d5a67f197eff71ffe8242d0519023358404373b24cde0054d3bee81cbf5cde4d40732edac587be46c79d8e95c1340bd68684b05eb6010e132ad700775e1ea7dcaaf75607248e9abdb48abedc0eec476f03820a005901c03de125d80cb791bfcfcc1259437499ad25864bb618805cdbb002722c14e80285afa2db5c72517002850ffec2ee0f276472cf1d18d6928ad9f0014ac51ebf3b0cef881a64db9de443f84ede8f05e031fecb6eb1d9f711b521c0333a78bb6dfb9af101eb457dba359a60ebb49591abe105bbff8364b3e84ae3f80dd14498531c0aad6e959f55ad61c1c0c3f5ac91d964d59e283b64589300e49210e4dab33b2333f975a2074e4e4ac051167f51cdb96c477acc61cc68faee34a74a12c84858d65de751d8b800812caa02d5714922e9102a6d1361568227a4a23dd4a14655e7f7032c230b620cb8c3104f05d1adb8c8bff209bc75de808100fc5e879998463d2646a6a1e5089249de795a18d3ac4663543c0366552503f5d41830f3448601c48e955c6e791e3a1c4ed7e65745565580294651bcc5aa92b57f6e2a2ff6b52e0434f0b2141ced79aeb9a85f42f3dd50714630e0f98bd10a0abb7832b8d6f07c6729731e97e0eb13f43ce54533762c9b53977a3644101d47f37865a3c2208644ea56413dadcabe8c880f2ed0e7bd8a6cdf00eaeeb18cab609ebefbab9c2c9b58f87357cc4206fd1dbe0806d1bcef3112389926a755c00abedc20706d32b81cb772431e81a60081825820196fd754d9c6ec7815715632c43579b183982eafdbae9ec5ef4e1c4868f170a2000181a200583900eb21979c03eed7207020b2a0b47565b5aafb1a2c3849b59a1fa8e6c5e075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d6473903011b000000024f9cf03b021a000320a9075820c18d15d64b54b1f14056916ff9cf6d4d027c4ed97c67290704ec07a3e99eeaee0e81581ce075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d64739030f0081a100828258208469288efa6f9cb49040b43dcff93f40969d72433f751afde50235012c016020584018430fd6585dda6e1dc40b7446bfd9356dae24e6d8697fa70d44640688d1b100bf6189494cefa13f31289fdb9f4aa205c6575680b32324b2eb54a0c778df870782582076af5530fa318a370820270031d1838545a4ceed8696510627563d1114d4182b58403a668351115e280dc49d463163649cc98919db909f656af493a2a66883b5d840ea516e49a619bf54207bd93a530822e2a7a55acb732cf0e0aef65e709c7e450da100a11901fda50050ca7a1457ef9f4c7f9c747f8c4a4cfa6c01501011f74e337c2f1747fa47c5c55844b80258204d3f576f26db29139981a69443c2325daa812cc353a31b5a4db794a5bcbb06c20b8958401bed02003c0e772c72637668c289a39b361dd1161a123da11e1118d08c7ab73ed1455e25aab3105e92334ba1fe128febedfb3e4912243755f42aca92094de82658404a4149a26917374c7cfd021376195439e4ea64844b46cfe1f87e9b6bf4d43c9dace1920ceeb2cc82bb60018b5b2de9571c5ea9c81dddd4077cc4571eb33181ce58409b38965811866581e4e903e6967333e85ab02e1b25665f272d24db06fb0183d3dd1cd937e2e260e3e0d045c976e057dde418766ea47dd551a68c20c015f508e25840118815ae0511d0e258b4440bd4b921222339016000ba853d2119394d8006a8a220d1c4755d3920f4403b302919d9c22f32106e1e10c3942a7d0f1c8ce42283205840528a20238dc8802d44cafe99612022d4abb8dc58894462642a21bb150449720b1f0e4e2a62bb9210b334d2f13ba4057d05f409d0fa3c666a3cb41cd012cc8e29584086ea45acd180f40932c052962cc156bad9f4a8a80d2f5d2e488c7ba8a496b1b1bf332482c7f8b9f981bfcb862878854a29842b460c8c782fb7905037399087685840693fc55005905188558891f50a0b0b0d8f0c04528e5d4a3c3c5c1cfc3360ef293f8938271225c0c6c727c4c59e3e4e09a2788847c7cfc79646ce62974cc11f1558405fbca2dc36eaf3594d18624bde0c7adac3324a828c2b833b3b3fbcd0c657c337f05bd53ece84f0adf329567b7234fe45897252656f11cf8ae6e56073452a93e85835133271a0fbc9f8d641adafb33a0267685f05fd95caf1ff3efa9d60febcfced727553ff21cd774cee682b161636860470b149c61f40186358400462942f9403413877b1ec95608d07a5d2f5c668a650d673044a6ecff9cdf3caf2d26482a1df522acb99bc89885df8b4418a8add23b01de5276bc4371cd8f60680 \ No newline at end of file diff --git a/rust/rbac-registration/src/test_data/cardano/conway_3.block b/rust/rbac-registration/src/test_data/cardano/conway_3.block index 09dd13e194f..c79dee4fe2c 100644 --- a/rust/rbac-registration/src/test_data/cardano/conway_3.block +++ b/rust/rbac-registration/src/test_data/cardano/conway_3.block @@ -1 +1 @@ -820785828a1a002a25421a0453e3515820ab3904be7f5116a7ae4ffb8749b06ed1a6a09f2abdc0828b2b9f6b9621e45bc75820ac16ce463e6697d7b49bd22add462f092d9f6ceb808f74a4ecdbd8ed4ae1bbd758202596aa11710a0e31e9b3a8602005df3560a1d78e2f57e0dac408d205ead1835c825840765bb637312728cc374a634017e65c5fa8f6a477106d5c5ffb4620c847d417d0be21f88b8effdf14aec3cc632bd8016f504b6cd1a28595fd079bb64237d294cf5850e2be622ef7ed6d351b064f6f7eac04b2487a3320d3b1a7fc10481463abcf642e3b0b3e3e6aa88dc2adef6ab24ece460921bc5fc087d398a17c3bd89c93a825a68ec451ec589b199cb0019d20fd765801190726582036eca374937cb8fe6e5fa8c30a1af65ab42e2c18be80fdc793e9b224ae3efa788458206f897ea416c6817010ce15d184b1692da860fa1efce2dc87edf9ff517447c1b80919021c5840803cf49927cd22d4a68a6f4c91177d0478de2c33974870e20168ca5ebd6e7e49de2a615a3aeb3f38d9906ee20e6d86ef70f82a98247fa2d6998bcf252884260d8209015901c055a847c305997bad4458d8e08f53fa97fa99520ac46fc80834888485ed928476bc1c5b2a21d8db8fc85f258b8e09d1b45db0688edbba85ce836d7dd40345d40c5ecc67ae0dd6446c97071b3ba1ec6b71fa8f44de0d157c07180a83c043d1c813437ae50016f77e01a105d0e189020138f0b3427785c40a716e4310fae99bba5cd0d981a0ca5e9ca103216d71f4c03da7dde73b3d532a0c72d1986db62b4aeb037a8fba11119ec1154db04df51bc8c62a8ef1d035057970109c78378b47debdfe9ae2ecf9a35d15804db55ac2749c48e80b2e05312b734333484401eede425978e7ae6994c271c92f392a89ca2a6e419d4f9044aadc74baf32568067520db3f74543165ee22b69b57acd21ba4f3c762196a045d34c37274f33ce483666e42d149afc97e8495eb1ea5804c38d3fd728c5550513d1f20f26090dc67325315b71a0740a81c259dfd387e7dabb52d89baf37c5ed526a52b8171974221794d0db0213d224a7e68209452f3eed343dc3ac76107681efc39126d4133ae07f43232cd3fab7369a4238b74638c7ea0aa0c92468f2b9fb09275f8099fef584b15c62f700d91f857670868cbea6fd6bc409b37448efa78cc6b6b25a9ab4a8bdc6ed7d1d6bed481a60081825820a87b4642a59eba961124544a483d98568711284d8652e65105b051a6777e134e000181a200583900eb21979c03eed7207020b2a0b47565b5aafb1a2c3849b59a1fa8e6c5e075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d6473903011b000000024fd4fde5021a000399a907582079af25e1de0c8377fe74ffda579a5a121b58511f78c6cb80dd37dea97cd123d40e81581ce075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d64739030f0081a100828258208469288efa6f9cb49040b43dcff93f40969d72433f751afde50235012c0160205840fbfdebfa5d695157394d1306fb369abd4b8d541f7686d8f5ec0cdf00262cedce6379b9fdd18fdef149be17f5ad215b08ec0e96cd9f7ebda8a6ce77fdeb84e90a82582076af5530fa318a370820270031d1838545a4ceed8696510627563d1114d4182b5840ee532d8f465da42639109a93a21a4f693d65ba71e5326f8a34ec2f5d82c1b29fa23ae9213da417f3ba1507c592b7087819183722630fce4503c90f6953cb1d0ea100a11901fda50050ca7a1457ef9f4c7f9c747f8c4a4cfa6c015060ab06a8774b60aa1b1389f7a307a15f0258204d3f576f26db29139981a69443c2325daa812cc353a31b5a4db794a5bcbb06c20b9458401b03060066006fd5b67002167882eac0b5f2b11da40788a39bfa0324c494f7003a6b4c1c4bac378e322cb280230a4002f5b2754e863806f7e524afc99996aa28584032f02b600cbf04c6a09e05100880a09ee59b6627dc78d68175469b8c5b1fac141a6da5c6c2ea446597b6f0b6efea00a04ac0c1756455589908a5e089ba604a1258405917d6ee2b2535959d806c00eb2958929ababb40d681b5245751538e915d3d90f561ddcaa9aaa9cd78a30882a22a99c742c4f7610b43750a0d6651e8640a8d4c58402167427cfa933d6430c026640888210cd0c4e93e7015100300dcaef47b9c155ea4ccb27773c27f5d6a44fbf98065a14e5f0eca530e57082a971cbf22fa9065585840ae72e2a061eb558d3fd7727e87a8f07b5faf0d3cedf8d99ab6e0c845f5dd3ce78d31d7365c523b5a4dfe5d35bfafaefb2f60dd7473cbe8d6aa6bf557b1fbdf775840bf96bcd3ffdbfc7d20b65be7f5c7dba1cf635e3b449bb644bdfc73e0e49a5db73edddc7ed331220ba732f62f3aee8503f5c6f9bd5f7fedb37dc6580196052e50584027fdd7e8bfe9146561ad1ebc79ecef0ee1df7081cf9cd1fd929569ef3d55972d5b7ff882ce2213f789fc08787164f14aa86d55e98e332b220a07fa464aaa7c335840ce4bcfb268ed577f72e87fdea4442107bf2da93fe05121d5befa7ae5aecc5f3f9c732e82108003166380198c0146b0e214114a31d7c62b0ec18afd5834034c2b58402b2c515b350d8980a16932071b6d8d125ea1eb53dc28a8aee1787a1670b9e8c4c8cb00c726f3515a39ca1689f870295752820a64721e05e1a234710583416316584031d80291ac9a2b66a04cba844b85f9928a4a04a9928b2805124a25b3aaa4422e45e5d422a9b88a028ba4a5123ac244b8b472164b86085ac21357c3aae7696be25840f1104878009b03813d9e6c53255722402090206058a009d2b808aff772fb712d75f1dea09507fd83838e045dd9ce3eb59e4554f5ed02b8aeb60700f4b39dd9fe584064e1d5a137de0fa4c6cccfd71f831bee372756d72990b357a44e2f9eaf3854db65379db466cfcb55517ba71550acade564f4b7efd1fd95fa57228cee6fa9ae3458405ce1ae79b77f7cd5bdecfcb800fbdb7eaf720eae5995176d94a07c326c71aaf5e6f8439e577edb2d1ed64959324b5a7476e9159bf37bdf226edb747787b79b9e5840bc6ab5b84714eefa4a8c2df4aba37a36757d8b39dd79ec41b4a2f3ee96eabdc0e1f65b37264bdbfdf79eebbc820a7deab4e39f7e1cbf6610402fd8fb55fbef3d584038226e4d37c42970c830184b2e1c5026eadb9677ae8f6d300975ca6ceec5c8920382e827c1f636f7dd9f8d492737f4520a944bfeebba5ca2d5efa80ad453a43f584004c357ecccfc4dab75ce560b0300db9092ced52625d0c8df6fc89da9a45b6dc9c2461f21e6ee7b7afd877fbd8c1a1fa7ff38fa506e14749ebb68e24571c6220c584004208c284d628c2148b252f91b8b50014b080b040554095b52ca862bb974218222d412112ae5d2584c54584ae157f22b183cb4ba9c5fc42ba6894ad074ffe0875840c69ee921211d0ce4cd0f89b7e708163b3ab9286fe26a8c68ed85930cabc5dbfed7f9681c535dbdbfeb56f7a2b32d1f43de1dbcc934676edefacb3df7c1210067584064a1b8d94448b7f22a77dc736edb12f7c2c52b2eb8d4a80b78147d89f9a3a0659c03e10bbb336e391b3961f1afbfa08af3de2a817fceddea0cb57f438b0f8947581f9782ee92e890df65636d835d2d465cc5521c0ec05470e00280ac54b83f630118635840353ab6bd8dee46d19c9f0f19304e5088a9d866cdfbb13fb8c517d3129978b8a78d447201df75b604ad0b144a7e7f11cfe6ba70980c7bab8d4af158c90d39d40980820785828a1a00291dbb1a043840285820160ea19fb19f00088e2ef986fa354c958f0f40db89d29c32f188e5247a410736582054ca56226c90d5544bf6f558026b814027b634e60de0bc215237944110569c6c582037d6147adbf2907948d66f9e4751774bbe9d6c4ad562ce04ef66f0fc1e7ac301825840f4db3a5ec9aabd655c9f651071036626a368e855152897a01115661660f32b19bb406669ef359e30745c48dbe4872e1f5d110191de27d750230243f6150494f758505af67e52747541a74c5b13d1d24a4706fe7c84ce254b1eab283e1f38fe1398d055ce36a4ba4268d67ce1ef84580f093bc671dbbbf5887f96c2bf192ad1bb3d8b97765cdb927219f3e0988c93bbb0510c190f2b58202f98cae65753f272fa96ba2a7ab566f4aa81356e97c1800b57f6246fc193aeb5845820579c45732473db77b8acd430585aeb79ccf7e76b7b118f1e7302291a2cb3d958071901ff5840cad68c6ebff25b637335d3ea1647707cc0e91ecdbc00f1e4a8e3639f1879a5560d50bc948792d43dd2d7cbf54c3e188df16c7ad79f1f78ad1ec760b0725fbe0a820a005901c0f98f0569ea6ff5df6d3483aaa68e195ac1866edb28b03499a0adc8b2655a06855d69b15c39e94d13db695281a6249a65d175d37bcc029c422ba3cadf2afdde070f5658ef64058ab83e1435b5066a132195fba0981f76c4badaa93dd5cbe25a7028efed14eefadf9988557610d4e875a2d9375d25e5fe2939ebf23a5c44c0e4be7b107dfdc93a3b21c6a87ff9b46f69b65c53a204e5ead4fe9b1ae756e7a2755fd5a6dd9b4b67bc511479247eab6089e0eb17fea1eca7ac5fcd03dad404798a3067a2669d5eba8c3d762de40b42157486cd99746c0a021c213054b868a1f35f747933368ec3bd901b18ddddc09918d9d3562dfb425c9a3f442929c71ea09984f9a2d1f9a5f67016b07c406f319e9bda78158204f8641c215e9d6a726f1bba21ccf82a241e31ecf24624e95d5c82b42bea18435946706671969b91157f2071609c3cbb4b5e944e4829392e85b6c20920072ef246d1c9aec109331fc83c9f1d4b07d0d012547f82da0b83d514e2981186cb2d6b6e392c57465010414ae32bcbaf60f5e77e321a6a78b24e4ad9c44d5e6f1dfebb74d11d470cfd77d05848bab980308cdfd157edbc572c75a2f2c6462c9351607e1e7a944e9d0b41e1bfd1ef2dbdb387ab0083825820c3ad11a79c6b2b131fd37fec1f85be7dda6d5e8891f7523377008205ad6122b002825820ddebcb766e4902dc1b2537178875ff80da782402858254e994c9b782aa1dee3b01825820fe7258dd047bd4bb15641c1589ba9c9e5ebeccc689d2b01ae4210613f4d52d5b000183a300581d703f89c436844cd0acd2714201817836b2cd16b082e03e3a21bb69e5ad01821a001e8480a1581c2b556df9f37c04ef31b8f7f581c4e48174adcf5041e8e52497d81556a1484e6f64654665656401028201d818583cd87a9fd8799f581c564b2400e0901e336538bea2eb48f0bac8aa77b0c3a948f07cd15ff0d8799fd8799f1a0004fc6f1b00000191fa22918effffffff82581d60564b2400e0901e336538bea2eb48f0bac8aa77b0c3a948f07cd15ff01a0089544082581d60564b2400e0901e336538bea2eb48f0bac8aa77b0c3a948f07cd15ff01a006f2857021a0009d160031a0438406d081a04383ff50b5820d5adb9576273dc98ae443e7248d3ba5a35ba7befa01b37ead68c0810aa40882e0d81825820fe7258dd047bd4bb15641c1589ba9c9e5ebeccc689d2b01ae4210613f4d52d5b010e81581c564b2400e0901e336538bea2eb48f0bac8aa77b0c3a948f07cd15ff01082581d60564b2400e0901e336538bea2eb48f0bac8aa77b0c3a948f07cd15ff01a00524801111a00370c3f12818258201a28375b01a8928f21a87598e24165ab5806e7c3e77a5eb4d78c56e3cf07894c00a6008182582030133b1ce7dd59fedcf79adbd0012f7fab64bc54d08ea9621e0df25cf18ac656000181a200583900eb21979c03eed7207020b2a0b47565b5aafb1a2c3849b59a1fa8e6c5e075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d6473903011b000000024fdc310b021a0003372d075820aa05878c56074cd1241e91b49a63ef718988ff544c596e44521f1a8dc6b387650e81581ce075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d64739030f00a500818258202d9559bceafc74514c59ce55bbd9e2bec6dc31790f277d4aba4e042e85f4112a000181825839009fbdc6a75e10012d8c42017e375337cbc3760e96f911c87aacb6dfbe74b1d8380c8d4a9dfe1781ce3616c5f099b74854da5aa69532235ba91a04b04d51021a00029a6d031a04385c0e075820880443667460ae3b3016366d5bf66aca62c8149d67a53e94dde37120adffa624a500818258209e911e840789b2727a194f4d00959b4ee1f94db95542a16f2b0fde5cc14e03ff010182825839006f2c165bfb9e28a313da02d395eafe57ee1081bb14b8faac1856b0473907b2a702a6dcd0d937d4cbdd2ea8db46c335f9c6980ee93b0ee9b31a00a542f782583900fe33b43ae102d26c495a616187fd535a8ab31cf0cb0d7bc4f2ac35343f05a30548ea2f467860ff3ea675fcebf50bccf21eb99c9fc92d854a1b00000001d6236a82021a000295c9031a04385b0f081a005c5a2da40082825820139e2c770f1c836164cd9a04b756dc34622a672289f579efd42b48e929c3bf0a01825820bf1f24ac9ffcd9938f71a5dc2ce457f496d877440c57de1134334dc66378cc6d000182825839006d874798dee894262bb80ff32409e1ccb5c662f438d0832f7e83635b93cfb33930fd28cae34fd9a508f9a1c98bdd52561297e062983583dc821a001226b8a1581cf0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9aa150000de14068616e646c65746f73656e640182583900f3cb671c88d464460e064deca0dcdbdb68a5753a1f08a09deeed716a7fc7000fdaeb72ac4f46ead88304fb4b036d953184cd114522c60f2b821a034df563a1581c63f01fe6cd68ec6438c95a46cea4a6cd27efb791b5e8cc1fa92af329a1524c616365204e46542050726570726f64313501021a0002a98d031a04385c15a400d9010281825820c5fc4abb70cb78e2dd81ede78769187a5e62a2cbe61e89e3241cc87bf49d3c9901018282584f82d818584583581cb0c95c61063a9438de2cadd7835078da4fd3d967b325b0ef49a98be2a201581e581c0e3885905f8f51eb31f58ee644d9adbf925e914c7953bcd14859471e024101001ac50f88b51a0010602782584f82d818584583581cacdf62c4488e78c07e28c4c80539f4aa0bcf0f7d14b37c9eeef45173a201581e581c0e3885905f8f5185d980efe6b5befe429093a12e9751ccae2633a713024101001afd4bab7e1a17d16bed021a0002a6a1031a04385c2fa30082825820c0ded58d1f20a8b7e1c01087ebb1d1ebb94af490e8a93e3b9e5bb20a98ebf41e01825820ffb031c142583f453bb67564b3994f38edcaf175ef66edb778d447b3faffa558010183a300581d70c05492bb753dd4750beaf12356ce4e8252f3930cbf92d06854f6852d011a00b71b00028201d8185842d8799f43323435581cdf95da5f1f11e588bad36eef8c9ea171a706e4cdf529fd97156e74ebd8799f1a01312d00d879801a0007a12000d87980d8798001d87a80ffff82583900df95da5f1f11e588bad36eef8c9ea171a706e4cdf529fd97156e74eb758ab9d2ac7b679542638ee8024dfea41e77d462389977872454f2cc821a00129c92a1581c8a746b4319053b7855c68ac5ed9c943525702afa94c0b303684bc814a2463234305f6e6f1a012b1280473234305f7965731a012b128082583900df95da5f1f11e588bad36eef8c9ea171a706e4cdf529fd97156e74eb758ab9d2ac7b679542638ee8024dfea41e77d462389977872454f2cc1a9925a57b021a0002b3dd87a2008182582002205785797b8efa694d21d7dbc3a290d2ae09538957920c9409999c261ecb2f584084d1baa591107d76dc8d2c0ee9780ebe3840b0f249ab8a00539aa25002adabd96e36f081241eeafa071a02c4982c756ce2dd6e077c9cdd380c9dbd96feb076030581840002d87980821a0012eff61a1085253da100828258208469288efa6f9cb49040b43dcff93f40969d72433f751afde50235012c016020584047f575a45ba7a644eb8131f0c2a85475873fd3ea71d0a5104905dcd637013407b113b1cbf4b7af8cfc2922de2a416168a1276839946b293c0aa6dda61b87960d82582076af5530fa318a370820270031d1838545a4ceed8696510627563d1114d4182b584019db9a32be1507c0c13b2177086c17ddc39d0d0c7956258da66dfab937cc57d8c39f6384ef2a07ccffddbea56b1afbbfd0d2cf82446d099f4658bcd2989a0506a10081825820f398d21bc567394b5e2670bf2b8088a36c0355a60786f83033aaee90c51a0652584088064631469320af98bae7571a1f6ea50f733c46dd65c5cd1e79153096063a4b4b3f508dbfb715f80742bcf751d1d913a8e082b4a5be73b4dce608160c355000a1008182582046c7b1430923a9aa57971324819633d7f0dc984ae03ba37b2c58631ea9f18f1d584096465f0d4f1b715d26f027edf5be6624558aefe6d3d2dec6f3cd1b3f7b62269e2f578b1c8f1e539652f5d9f0758536a2fa860b199771e53e1705b139ad2b5f07a10081825820fa198ff264f1191a846af5f6067afa341b36b9c882988810b209613ef4686d585840ebb8757498a9209c4ff1b7e92ec69e44f89df18ea1093d978dbf0cc1a2ca6edcf4d1950887acc0c710f8828d2988854eb15e34b64325b6b1cfcec53d5155e400a102d9010281845820847953db74a03327eecedf38f424d510bd6dbfb0da2be989d5be80251b76319b58400789ea001bac725e24ee88673706771013c356f864f3a268544ff985f72d1dcc26a4a9992a367ad9e9ae9ba83325f73dcb37f76d6f262031837e94256431d90b582053ecfa2c8bc4ab302ec166256aa48a95d549c4a7c01f1e0a98153c654e94c16e5825a201581e581c0e3885905f8f51949c09b5e6d340fe92426bdf4a912d78ef9422b32a024101a10081825820975277c2fe1db3f260850e3f4946f15affa868a6df0388dbb9b4fcb40725e1c858402f68e3b0716c37dc105d6cee678ed5f734f1d906dcd37a72f7da4768629e42bd9b345f8d356733ee85a9231d45d7eed711acc9d11fa3c6f8a14928897441c002a201a11901fda50050ca7a1457ef9f4c7f9c747f8c4a4cfa6c0150cb6c2b83991a305eb48f60c933913acf0258204d3f576f26db29139981a69443c2325daa812cc353a31b5a4db794a5bcbb06c20b8b58401b09048064084f8b2b0dc2dd44a61d5c0c4ef620f87f37f5000ff1946fb706d0f7268e255684615920797acf734fc8224eb7197419d8ab868cb5be7696f3994258406c0360b135c350c9221c43314ccf2774cfee070b8dd79c709dd157d779a7e943d4cc0580a47057652be88502c0a3f05c825260b9d9500164bee00af9dd011a005840bde49a23144104280752cbf8425e6ad77503d4078a850542e1442bd2109e3675a1a42c9f030b4391970f08845e1082406f581492be6f46e7b990742b6e8df4735840dd542ee353bdda6ecea406fcce7c29a5d5bb02d12abec73a636fb8f257db52e62360de0166816f9bcb08660660a6b2dd6256fa1ae6194ff0fc58604bcad3446d5840447687b2ca55828795cd2d419e9e9534adf6d4c8c52e97be31a8536b02b440a3a9f4ee08eca80d048db4afb655ddea5da35377d23e01fe6a6dad575b635b839f584052dd5cdf56ad54f9e86ad44080af2921081c231f01f319db13048eb9ae144a921c1cdd2e5646e28b849ebdf9fd3cb95fee9fd53e137f87584cb9107cb8c8999b5840874722f9f71256ae0dec4a9c23183b07b9bdad2439b33e63ad6a7dbc38f3787ee16164d9cdf5a6e3f6dc06a9572ce04ddf7b5bf1cd2b53a79ec765b2b94b73125840ea47ede0e931058f9184839d037d07ab7326c7a3a86d6ccde8b526aa2727743fa20f1dbfc06a5b313bfd49abb9fbbef11153ffbc4e9e39d3f7d7f1c7dd46d1175840e925b35ea90a97b0188ab354276d7fb6b020a264c330a4c768e5a7a4691fc7e20ffef38e8df03dfeaae0cfdca04393824cdcc71e497c71ff95b0d6ebb945f8c6584095b2439d0d3d275295cddcd143765ce1d8159df9989105737b902cb00aae77dc7761c732f7aaa5ab2eb9b488344a2ffd0ef327d12b820e252c1e5b78c7d9c8895834cd009d9daf3f3b4d35d7d7db36e139f4389ae1b2cb2779eacbe08f974e9eb2d9b1ffef48239a598e2028cb47b0c3805bf1e9bf9118635840466ebf1f9df0f881cdaafa8a876b55ae6c38c958f9242261df29f48be13dfd9226f44fd82e60bc868353d5e790a920b006a42d36b63e3405d891ad297123500502a1191acea165706f6f6c7381a26269647838353136366636393434336666376433356136356637356330333565653765626637663964626531376334396234643861383262613439623566776569676874186480 \ No newline at end of file +820785828a1a002ccb3d1a049987cf582015b83a54c895c25a0b801321e37b8e739f8f48e6c8de774f8b902cf0d689ff855820bae7fccf57e9292efa30244aed16f64471ac32957c50e5261c33d31226df84ba5820d754ac94a56d8ca60309a56c42d38a6eb910250b771cfc039644464d7c2a384c825840f43e71e6a14fc82c42da848d83a15520c3ee88e747729038c31564e9bd34da76876a3f5e007d282dd200b7b5f053a8e4a3b7eba91fbf5f8e63629cecc966672e5850fac87b66d699befd76858c79efe609ff44e20f75af2cfb6afd8dca1a664b3adc11fe4d3ef812df82012f9538c1f0e57cd64793148b9c0c344ca1b0370446fb314dac42296fa8c47863dca2caf2932f0a19042c58209c55f69be15d6dc821b18b41bf56cf39e83ea0f3ee8db63ca2a014e0b10513d8845820ddf29f596c53637abfc18ff36f6ef1ecb7f9df738188b0156a0fe6983a51aea20119024a5840e602a673cb7146fc38abad1dec9330c0f8d57a147c3b1278112203d379e7d057e337ea37e8fc4bfdaf4680d83906e591660e0e5480efc5057b719c3f68aa6b05820a015901c0a1e0c65487c6bde1b1177a9ee9eca9e1bc5cc8db429a28a4193015eb6107b9a21de4f7fd1af59e0c99d02494911e6525aabec2f1b8b24e05d5c032ea6dc5bc078e2522aaf8c03b2c4b0c8211e1ef99c1a7a1a9b8670ba332f3e54f4d312a628f777cd93ef428e9a416ff433d449c90706d88c1b606c6d294e9c74c77402d47f42bbd0ed3d62a9b4bcd0d938afb6eea1c700f25eda01f07489301f9580469b57e2d67c132c019830f95b2bdcdcc022c30aab889b645baff27000336085d6f8f85e536802d4af42b49884bfc0f54fa031b4d121812eb3dbdc533bd8888fbf58af4dfca253a020248ec172a127960f6556208ed9663a5d2f5791584c81f7f7bffca0f056c0a7dd027b284911db62b56363bf34b6ae0f7bc7d2e7e809b96712212c656e53e347b5394e8a9ed4a6693d210b8f3c00a9b29b777a112dd3ab9703ab0de66cca4f2a078483eb6ff7b02287366260254404027783fac9f7403b21d2ad8d4966afc937f66563ca8ef591b64e67ac33862660ce796d2fb7b0ff03d5e64cdd96d828239537717b35a1257e89170e407ec14adb4bbec2fe6545ef9874a1a09785d04d05604f23843b30248033067699f2b99ec2fff6171ddea34f6fc3f8c3fe881a600818258202dc856d3b4a0c81ddde5099d44d50964f23ad28b63cb260ae13b35aeb9f6123d000181a200583900eb21979c03eed7207020b2a0b47565b5aafb1a2c3849b59a1fa8e6c5e075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d6473903011b000000024fa326b9021a000316b107582066a0c1b30e0b0f98665c6ebe08d0bcb4cf0f90248734adca108d01c36e83b8b90e81581ce075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d64739030f0081a100828258208469288efa6f9cb49040b43dcff93f40969d72433f751afde50235012c0160205840933228faf5c8c4985ca65109175978c117454cc3b1025214c4c178347d60462f30f637d8e1fa2f11667a59ed78aa80ba00c075417ea19570bffe9e2f86d7f90b82582076af5530fa318a370820270031d1838545a4ceed8696510627563d1114d4182b584098078a7af8ccf41b263a960426ab5821c0727c6973e862c6fc87b56ba14c5122ed6c92e251261ad37151eb87b5c69eb9da679f39f92fee90325390560b7cba04a100a11901fda50050ca7a1457ef9f4c7f9c747f8c4a4cfa6c01501b3dc524cf72f482bbbf60454f3a4a330258204d3f576f26db29139981a69443c2325daa812cc353a31b5a4db794a5bcbb06c20b8858401bed020064f03bdf8a48de44553c0c4e47f98bc0d14d7dc0477ce5311b13e8bc89638915615816489e3e606e393783ae05ebb25162f675b28a87bad0b682126d58406373d47d399d46a5d1142d473d1af3a3f10b5be75d4bde3c343115d1cb8fa980c9a2fba3053014f2019745efc060c9691dda413990e68623e09d005005140b7b5840b6404813a00c48a2e009b82d065d394025900fcc1708167e9181e879761053aaf43d30a98565e5212cab02ca832ab0725788acf06786814fa857e956ca704e205840e69bd2a2ac78731e6b44c5335f7a44b0fd55a87d79a1cdda7270ddafe22af411a077002dcd63a39481e60234abb4635a92bc88351af09f2003acb86409b6c6b658401b10edebb7e1659f5cbe6a38dc2712c1c2b8656c2ffb3b8324865726008ef72937a0ca804415908954200a58a15c9cc40646ca57ae841189b262a698ae18c5725840a962bf68ac3c19c7009f6357cc60d069cc47c0d8532743c439c7cfa232996c3a75fab412247ac850388df73bca2a76f8b3a1fcb4dfd54d332f543bd1d5abcf675840eb50d6677fcdf8f26ddcfb758710d1aae18c33e25ddc931f326555175cfb76e596af36110e6ead5277e07f5d615deab4aa8a6d633b6cf499a79dae8e46036e9e583d2e3edf2cd098d0ed91ccce965cdd5fcf27accb1d927f742ff939daf4eff5c0cfaca38fe77e7a7af16aa34370e52b4d04ada650a8251e854ee545fe0002186358405ba46fa3c1161a6069721779a976c74cf721b555a4a8b3350e6a531f89406e77b2646c2d16d4e52abbd658a3225f449becf5f87266e31ed1179eda1dfef4f50e80 \ No newline at end of file From 3a9f1709a59525d0b0d8d73f9a6ea45924072067 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Fri, 29 Nov 2024 11:45:44 +0700 Subject: [PATCH 28/45] fix: signing and encryption key Signed-off-by: bkioshn --- .../src/cardano/cip509/rbac/role_data.rs | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/rbac/role_data.rs b/rust/rbac-registration/src/cardano/cip509/rbac/role_data.rs index f07b1be9d94..486c2c8deda 100644 --- a/rust/rbac-registration/src/cardano/cip509/rbac/role_data.rs +++ b/rust/rbac-registration/src/cardano/cip509/rbac/role_data.rs @@ -14,9 +14,9 @@ pub struct RoleData { /// Role number. pub role_number: u8, /// Optional role signing key. - pub role_signing_key: Option>, + pub role_signing_key: Option, /// Optional role encryption key. - pub role_encryption_key: Option>, + pub role_encryption_key: Option, /// Optional payment key. pub payment_key: Option, /// Optional role extended data keys. @@ -56,20 +56,12 @@ impl Decode<'_, ()> for RoleData { role_data.role_number = decode_helper(d, "RoleNumber in RoleData", ctx)?; }, RoleDataInt::RoleSigningKey => { - let arr_len = decode_array_len(d, "RoleSigningKey")?; - let mut role_signing_key = Vec::new(); - for _ in 0..arr_len { - role_signing_key.push(KeyLocalRef::decode(d, ctx)?); - } - role_data.role_signing_key = Some(role_signing_key); + decode_array_len(d, "RoleSigningKey")?; + role_data.role_signing_key = Some(KeyLocalRef::decode(d, ctx)?); }, RoleDataInt::RoleEncryptionKey => { - let arr_len = decode_array_len(d, "RoleEncryptionKey")?; - let mut role_encryption_key = Vec::new(); - for _ in 0..arr_len { - role_encryption_key.push(KeyLocalRef::decode(d, ctx)?); - } - role_data.role_encryption_key = Some(role_encryption_key); + decode_array_len(d, "RoleEncryptionKey")?; + role_data.role_encryption_key = Some(KeyLocalRef::decode(d, ctx)?); }, RoleDataInt::PaymentKey => { role_data.payment_key = From 6cb3b8d42f01a61013b90344ac942b9904bbef83 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Fri, 29 Nov 2024 11:45:59 +0700 Subject: [PATCH 29/45] fix: format Signed-off-by: bkioshn --- .../src/cardano/cip509/rbac/pub_key.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs b/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs index 837c1327d54..b6e5b8a0f1d 100644 --- a/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs +++ b/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs @@ -67,18 +67,19 @@ impl Decode<'_, ()> for SimplePublicKeyType { ed25519.copy_from_slice(&bytes); Ok(Self::Ed25519(Ed25519PublicKey(ed25519))) } else { - Err(decode::Error::message("Invalid length for Ed25519 key")) + Err(decode::Error::message(format!( + "Invalid length for Ed25519 key, got {}", + bytes.len() + ))) } }, _ => Err(decode::Error::message("Unknown tag for Self")), } }, minicbor::data::Type::Undefined => Ok(Self::Undefined), - _ => { - Err(decode::Error::message( - "Invalid datatype for SimplePublicKeyType", - )) - }, + _ => Err(decode::Error::message( + "Invalid datatype for SimplePublicKeyType", + )), } } } From 7e8ec2d201dd02ea73c79a12d80405b0033d68e3 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Fri, 29 Nov 2024 12:42:02 +0700 Subject: [PATCH 30/45] fix: witness test data to be compatible with new test data Signed-off-by: bkioshn --- rust/rbac-registration/src/cardano/transaction/witness.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/rbac-registration/src/cardano/transaction/witness.rs b/rust/rbac-registration/src/cardano/transaction/witness.rs index 05545bb91b8..849ee4b6811 100644 --- a/rust/rbac-registration/src/cardano/transaction/witness.rs +++ b/rust/rbac-registration/src/cardano/transaction/witness.rs @@ -113,7 +113,7 @@ mod tests { let txs_conway = conway_block.txs(); let tx_witness_conway = TxWitness::new(&txs_conway).expect("Failed to create TxWitness"); let vkey1_hash: [u8; 28] = - hex::decode("f8e2356e21af3563556c167c31bcc00f9fc061a8a852a16a42ffcc79") + hex::decode("bd95d582888acda57a20256bb03e4c4abb6bdf09a47d788605412c53") .expect("Failed to decode vkey1_hash") .try_into() .expect("Invalid length of vkey1_hash"); From 4a07cae72eece03792adad4ab8d68d858e831b73 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Fri, 29 Nov 2024 12:43:25 +0700 Subject: [PATCH 31/45] fix: validate stake public key witness and add test for role signing key Signed-off-by: bkioshn --- .../src/cardano/cip509/mod.rs | 2 +- .../src/cardano/cip509/validation.rs | 52 ++++++++++++------- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/mod.rs b/rust/rbac-registration/src/cardano/cip509/mod.rs index 33b97492c95..0f02ddae4e9 100644 --- a/rust/rbac-registration/src/cardano/cip509/mod.rs +++ b/rust/rbac-registration/src/cardano/cip509/mod.rs @@ -205,7 +205,7 @@ impl Cip509 { for role in role_set { if role.role_number == 0 { stake_key_validate = - validate_stake_public_key(self, txn, txn_idx, validation_report) + validate_stake_public_key(self, txn, validation_report) .unwrap_or(false); payment_key_validate = validate_payment_key(txn, txn_idx, role, validation_report) diff --git a/rust/rbac-registration/src/cardano/cip509/validation.rs b/rust/rbac-registration/src/cardano/cip509/validation.rs index 84fb9d22324..9b5aac547f0 100644 --- a/rust/rbac-registration/src/cardano/cip509/validation.rs +++ b/rust/rbac-registration/src/cardano/cip509/validation.rs @@ -99,7 +99,7 @@ pub(crate) fn validate_txn_inputs_hash( /// Validate the stake public key in the certificate with witness set in transaction. #[allow(clippy::too_many_lines)] pub(crate) fn validate_stake_public_key( - cip509: &Cip509, txn: &MultiEraTx, txn_idx: usize, validation_report: &mut Vec, + cip509: &Cip509, txn: &MultiEraTx, validation_report: &mut Vec, ) -> Option { let function_name = "Validate Stake Public Key"; let mut pk_addrs = Vec::new(); @@ -158,6 +158,7 @@ pub(crate) fn validate_stake_public_key( // Extract the CIP19 hash and push into // array + println!("{:?}", &addr); if let Some(h) = extract_cip19_hash(&addr, Some("stake")) { @@ -249,6 +250,7 @@ pub(crate) fn validate_stake_public_key( } // Create TxWitness + // Note that TxWitness designs to work with multiple transactions let witnesses = match TxWitness::new(&[txn.clone()]) { Ok(witnesses) => witnesses, Err(e) => { @@ -257,17 +259,11 @@ pub(crate) fn validate_stake_public_key( }, }; - let index = match u16::try_from(txn_idx) { - Ok(value) => value, - Err(e) => { - validation_report.push(format!( - "{function_name}, Failed to convert transaction index to usize: {e}" - )); - return None; - }, - }; Some( - compare_key_hash(&pk_addrs, &witnesses, index) + // Set transaction index to 0 because the list of transaction is manually constructed + // for TxWitness -> &[txn.clone()], so we can assume that the witness contains only the witness + // within this transaction. + compare_key_hash(&pk_addrs, &witnesses, 0) .map_err(|e| { validation_report.push(format!( "{function_name}, Failed to compare public keys with witnesses: {e}" @@ -276,6 +272,7 @@ pub(crate) fn validate_stake_public_key( .is_ok(), ) } +// pk addrs [[224, 117, 190, 16, 236, 92, 87, 92, 175, 251, 104, 176, 140, 49, 71, 6, 102, 212, 254, 26, 238, 160, 124, 22, 214, 71, 57, 3]] // ------------------------ Validate Aux ------------------------ @@ -562,8 +559,7 @@ mod tests { let mut decoder = Decoder::new(aux_data.as_slice()); let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); - validate_stake_public_key(&cip509, tx, 0, &mut validation_report); - assert!(validate_stake_public_key(&cip509, tx, 0, &mut validation_report).unwrap()); + assert!(validate_stake_public_key(&cip509, tx, &mut validation_report).unwrap()); } #[test] @@ -587,15 +583,35 @@ mod tests { if let Some(role_set) = &cip509.x509_chunks.0.role_set { for role in role_set { if role.role_number == 0 { - println!( - "{:?}", - validate_payment_key(tx, 0, role, &mut validation_report,) - ); + assert!(validate_payment_key(tx, 0, role, &mut validation_report,).unwrap()); } } } } + #[test] + fn test_role_0_signing_key() { + let mut validation_report = Vec::new(); + let conway_block_data = conway_1(); + let multi_era_block = pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data) + .expect("Failed to decode MultiEraBlock"); + + let transactions = multi_era_block.txs(); + // Second transaction of this test data contains the CIP509 auxiliary data + let tx = transactions + .get(1) + .expect("Failed to get transaction index"); + + let aux_data = cip_509_aux_data(tx); + + let mut decoder = Decoder::new(aux_data.as_slice()); + let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); + assert!(validate_role_singing_key( + &cip509.x509_chunks.0.role_set.as_ref().unwrap()[0], + &mut validation_report + )); + } + #[test] fn test_validate_payment_key_success_positive_ref() { let mut validation_report = Vec::new(); @@ -640,6 +656,6 @@ mod tests { let mut decoder = Decoder::new(aux_data.as_slice()); let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); - assert!(!validate_stake_public_key(&cip509, tx, 0, &mut validation_report).unwrap()); + assert!(!validate_stake_public_key(&cip509, tx, &mut validation_report).unwrap()); } } From f10c23024020418c74929ec6b9400b6c6909dc89 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Fri, 29 Nov 2024 12:48:40 +0700 Subject: [PATCH 32/45] chore: doc about context specific Signed-off-by: bkioshn --- rust/rbac-registration/src/cardano/cip509/validation.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rust/rbac-registration/src/cardano/cip509/validation.rs b/rust/rbac-registration/src/cardano/cip509/validation.rs index 9b5aac547f0..8d2923440ce 100644 --- a/rust/rbac-registration/src/cardano/cip509/validation.rs +++ b/rust/rbac-registration/src/cardano/cip509/validation.rs @@ -44,6 +44,12 @@ use super::{ /// Context-specific primitive type with tag number 6 (`raw_tag` 134) for /// uniform resource identifier (URI) in the subject alternative name extension. +/// Following the ASN.1 , +/// the tag is derive from +/// | Class (2 bit) | P/C (1 bit) | Tag Number (5 bit) | +/// |`CONTEXT_SPECIFIC` | `PRIMITIVE` `| 6` | +/// |`10` | `0` `| 00110` | +/// Result in 0x86 or 134 in decimal. pub(crate) const URI: u8 = 134; // ------------------------ Validate Txn Inputs Hash ------------------------ From 00fa07d2694656980aa59c98dd8328a49345d541 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Fri, 29 Nov 2024 13:02:45 +0700 Subject: [PATCH 33/45] chore: fix format Signed-off-by: bkioshn --- rust/rbac-registration/src/cardano/cip509/mod.rs | 3 +-- .../rbac-registration/src/cardano/cip509/rbac/pub_key.rs | 8 +++++--- rust/rbac-registration/src/cardano/cip509/validation.rs | 9 ++++----- .../rbac-registration/src/cardano/transaction/witness.rs | 1 - rust/rbac-registration/src/utils/decode_helper.rs | 1 - 5 files changed, 10 insertions(+), 12 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/mod.rs b/rust/rbac-registration/src/cardano/cip509/mod.rs index 0f02ddae4e9..90b6134e1fb 100644 --- a/rust/rbac-registration/src/cardano/cip509/mod.rs +++ b/rust/rbac-registration/src/cardano/cip509/mod.rs @@ -205,8 +205,7 @@ impl Cip509 { for role in role_set { if role.role_number == 0 { stake_key_validate = - validate_stake_public_key(self, txn, validation_report) - .unwrap_or(false); + validate_stake_public_key(self, txn, validation_report).unwrap_or(false); payment_key_validate = validate_payment_key(txn, txn_idx, role, validation_report) .unwrap_or(false); diff --git a/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs b/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs index b6e5b8a0f1d..2f2f3047d24 100644 --- a/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs +++ b/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs @@ -77,9 +77,11 @@ impl Decode<'_, ()> for SimplePublicKeyType { } }, minicbor::data::Type::Undefined => Ok(Self::Undefined), - _ => Err(decode::Error::message( - "Invalid datatype for SimplePublicKeyType", - )), + _ => { + Err(decode::Error::message( + "Invalid datatype for SimplePublicKeyType", + )) + }, } } } diff --git a/rust/rbac-registration/src/cardano/cip509/validation.rs b/rust/rbac-registration/src/cardano/cip509/validation.rs index 8d2923440ce..c2666877510 100644 --- a/rust/rbac-registration/src/cardano/cip509/validation.rs +++ b/rust/rbac-registration/src/cardano/cip509/validation.rs @@ -44,7 +44,8 @@ use super::{ /// Context-specific primitive type with tag number 6 (`raw_tag` 134) for /// uniform resource identifier (URI) in the subject alternative name extension. -/// Following the ASN.1 , +/// Following the ASN.1 +/// /// the tag is derive from /// | Class (2 bit) | P/C (1 bit) | Tag Number (5 bit) | /// |`CONTEXT_SPECIFIC` | `PRIMITIVE` `| 6` | @@ -164,7 +165,6 @@ pub(crate) fn validate_stake_public_key( // Extract the CIP19 hash and push into // array - println!("{:?}", &addr); if let Some(h) = extract_cip19_hash(&addr, Some("stake")) { @@ -267,8 +267,8 @@ pub(crate) fn validate_stake_public_key( Some( // Set transaction index to 0 because the list of transaction is manually constructed - // for TxWitness -> &[txn.clone()], so we can assume that the witness contains only the witness - // within this transaction. + // for TxWitness -> &[txn.clone()], so we can assume that the witness contains only + // the witness within this transaction. compare_key_hash(&pk_addrs, &witnesses, 0) .map_err(|e| { validation_report.push(format!( @@ -278,7 +278,6 @@ pub(crate) fn validate_stake_public_key( .is_ok(), ) } -// pk addrs [[224, 117, 190, 16, 236, 92, 87, 92, 175, 251, 104, 176, 140, 49, 71, 6, 102, 212, 254, 26, 238, 160, 124, 22, 214, 71, 57, 3]] // ------------------------ Validate Aux ------------------------ diff --git a/rust/rbac-registration/src/cardano/transaction/witness.rs b/rust/rbac-registration/src/cardano/transaction/witness.rs index 849ee4b6811..3355538e90a 100644 --- a/rust/rbac-registration/src/cardano/transaction/witness.rs +++ b/rust/rbac-registration/src/cardano/transaction/witness.rs @@ -117,7 +117,6 @@ mod tests { .expect("Failed to decode vkey1_hash") .try_into() .expect("Invalid length of vkey1_hash"); - println!("{tx_witness_conway}"); assert!(tx_witness_conway.get_witness_pk_addr(&vkey1_hash).is_some()); assert!(tx_witness_conway.check_witness_in_tx(&vkey1_hash, 0)); } diff --git a/rust/rbac-registration/src/utils/decode_helper.rs b/rust/rbac-registration/src/utils/decode_helper.rs index 5c64cb12f75..b6f12c7a240 100644 --- a/rust/rbac-registration/src/utils/decode_helper.rs +++ b/rust/rbac-registration/src/utils/decode_helper.rs @@ -209,7 +209,6 @@ mod tests { 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 - println!("{result:?}"); assert!(result.is_err()); } } From eea1afabdd3752e78ea1637261f7edbdceb64113 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Fri, 29 Nov 2024 13:08:21 +0700 Subject: [PATCH 34/45] fix: remove test since cip509 decode are done in validation test --- .../src/cardano/cip509/mod.rs | 35 ------------------- 1 file changed, 35 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/mod.rs b/rust/rbac-registration/src/cardano/cip509/mod.rs index 90b6134e1fb..00649fb9450 100644 --- a/rust/rbac-registration/src/cardano/cip509/mod.rs +++ b/rust/rbac-registration/src/cardano/cip509/mod.rs @@ -220,38 +220,3 @@ impl Cip509 { && signing_key } } - -#[cfg(test)] -mod tests { - - use super::*; - - #[test] - fn test_decode_cip509() { - // This data is from conway_1.block - let cip_509 = "a50050ca7a1457ef9f4c7f9c747f8c4a4cfa6c0150226d126819472b7afad7d0b8c7b89aa20258204d3f576f26db29139981a69443c2325daa812cc353a31b5a4db794a5bcbb06c20b9458401b03060066006fd5b67002167882eac0b5f2b11da40788a39bfa0324c494f7003a6b4c1c4bac378e322cb280230a4002f5b2754e863806f7e524afc99996aa28584032f02b600cbf04c6a09e05100880a09ee59b6627dc78d68175469b8c5b1fac141a6da5c6c2ea446597b6f0b6efea00a04ac0c1756455589908a5e089ba604a1258405917d6ee2b2535959d806c00eb2958929ababb40d681b5245751538e915d3d90f561ddcaa9aaa9cd78a30882a22a99c742c4f7610b43750a0d6651e8640a8d4c58402167427cfa933d6430c026640888210cd0c4e93e7015100300dcaef47b9c155ea4ccb27773c27f5d6a44fbf98065a14e5f0eca530e57082a971cbf22fa9065585840ae72e2a061eb558d3fd7727e87a8f07b5faf0d3cedf8d99ab6e0c845f5dd3ce78d31d7365c523b5a4dfe5d35bfafaefb2f60dd7473cbe8d6aa6bf557b1fbdf775840bf96bcd3ffdbfc7d20b65be7f5c7dba1cf635e3b449bb644bdfc73e0e49a5db73edddc7ed331220ba732f62f3aee8503f5c6f9bd5f7fedb37dc6580196052e50584027fdd7e8bfe9146561ad1ebc79ecef0ee1df7081cf9cd1fd929569ef3d55972d5b7ff882ce2213f789fc08787164f14aa86d55e98e332b220a07fa464aaa7c335840ce4bcfb268ed577f72e87fdea4442107bf2da93fe05121d5befa7ae5aecc5f3f9c732e82108003166380198c0146b0e214114a31d7c62b0ec18afd5834034c2b58402b2c515b350d8980a16932071b6d8d125ea1eb53dc28a8aee1787a1670b9e8c4c8cb00c726f3515a39ca1689f870295752820a64721e05e1a234710583416316584031d80291ac9a2b66a04cba844b85f9928a4a04a9928b2805124a25b3aaa4422e45e5d422a9b88a028ba4a5123ac244b8b472164b86085ac21357c3aae7696be25840f1104878009b03813d9e6c53255722402090206058a009d2b808aff772fb712d75f1dea09507fd83838e045dd9ce3eb59e4554f5ed02b8aeb60700f4b39dd9fe584064e1d5a137de0fa4c6cccfd71f831bee372756d72990b357a44e2f9eaf3854db65379db466cfcb55517ba71550acade564f4b7efd1fd95fa57228cee6fa9ae3458405ce1ae79b77f7cd5bdecfcb800fbdb7eaf720eae5995176d94a07c326c71aaf5e6f8439e577edb2d1ed64959324b5a7476e9159bf37bdf226edb747787b79b9e5840bc6ab5b84714eefa4a8c2df4aba37a36757d8b39dd79ec41b4a2f3ee96eabdc0e1f65b37264bdbfdf79eebbc820a7deab4e39f7e1cbf6610402fd8fb55fbef3d584038226e4d37c42970c830184b2e1c5026eadb9677ae8f6d300975ca6ceec5c8920382e827c1f636f7dd9f8d492737f4520a944bfeebba5ca2d5efa80ad453a43f584004c357ecccfc4dab75ce560b0300db9092ced52625d0c8df6fc89da9a45b6dc9c2461f21e6ee7b7afd877fbd8c1a1fa7ff38fa506e14749ebb68e24571c6220c584004208c284d628c2148b252f91b8b50014b080b040554095b52ca862bb974218222d412112ae5d2584c54584ae157f22b183cb4ba9c5fc42ba6894ad074ffe0875840c69ee921211d0ce4cd0f89b7e708163b3ab9286fe26a8c68ed85930cabc5dbfed7f9681c535dbdbfeb56f7a2b32d1f43de1dbcc934676edefacb3df7c1210067584064a1b8d94448b7f22a77dc736edb12f7c2c52b2eb8d4a80b78147d89f9a3a0659c03e10bbb336e391b3961f1afbfa08af3de2a817fceddea0cb57f438b0f8947581e9782ee92e890df65636d835d2d465cc5521c0ec05470e002800015eecf5818635840e0427f23196c17cf13f030595335343030c11d914bc7a84b56af7040930af4110fd4ca29b0bc0e83789adb8668ea2ef28c1dd10dc1fd35ea6ae8c06ee769540d"; - let binding = hex::decode(cip_509).unwrap(); - let mut decoder = Decoder::new(binding.as_slice()); - let decoded_cip509 = Cip509::decode(&mut decoder, &mut ()).unwrap(); - - let purpose: [u8; 16] = hex::decode("ca7a1457ef9f4c7f9c747f8c4a4cfa6c") - .unwrap() - .try_into() - .unwrap(); - let txn_inputs_hash: [u8; 16] = hex::decode("226d126819472b7afad7d0b8c7b89aa2") - .unwrap() - .try_into() - .unwrap(); - let prv_tx_id: [u8; 32] = - hex::decode("4d3f576f26db29139981a69443c2325daa812cc353a31b5a4db794a5bcbb06c2") - .unwrap() - .try_into() - .unwrap(); - let validation_signature = hex::decode("e0427f23196c17cf13f030595335343030c11d914bc7a84b56af7040930af4110fd4ca29b0bc0e83789adb8668ea2ef28c1dd10dc1fd35ea6ae8c06ee769540d").unwrap(); - - assert_eq!(decoded_cip509.purpose, UuidV4(purpose)); - assert_eq!(decoded_cip509.txn_inputs_hash, TxInputHash(txn_inputs_hash)); - assert_eq!(decoded_cip509.prv_tx_id, Some(prv_tx_id.into())); - assert_eq!(decoded_cip509.validation_signature, validation_signature); - } -} From 6da8f96dd1f31350973d0d25c627fa9101a2b650 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Fri, 29 Nov 2024 13:14:49 +0700 Subject: [PATCH 35/45] fix: test --- .../src/cardano/cip509/validation.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/validation.rs b/rust/rbac-registration/src/cardano/cip509/validation.rs index c2666877510..eae214d1e69 100644 --- a/rust/rbac-registration/src/cardano/cip509/validation.rs +++ b/rust/rbac-registration/src/cardano/cip509/validation.rs @@ -611,10 +611,13 @@ mod tests { let mut decoder = Decoder::new(aux_data.as_slice()); let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); - assert!(validate_role_singing_key( - &cip509.x509_chunks.0.role_set.as_ref().unwrap()[0], - &mut validation_report - )); + if let Some(role_set) = &cip509.x509_chunks.0.role_set { + for role in role_set { + if role.role_number == 0 { + assert!(validate_role_singing_key(role, &mut validation_report,)); + } + } + } } #[test] From a1cea398903b6be67075730a22020b6f00948741 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Fri, 29 Nov 2024 14:22:26 +0700 Subject: [PATCH 36/45] fix: test compression data Signed-off-by: bkioshn --- rust/rbac-registration/src/cardano/cip509/x509_chunks.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/x509_chunks.rs b/rust/rbac-registration/src/cardano/cip509/x509_chunks.rs index 69797559fea..457faef6a20 100644 --- a/rust/rbac-registration/src/cardano/cip509/x509_chunks.rs +++ b/rust/rbac-registration/src/cardano/cip509/x509_chunks.rs @@ -87,11 +87,11 @@ mod tests { use super::*; // RAW data: 10 - const RAW: &str = "0a98195840a50a815904a8308204a43082038ca00302010202141fec832800975bc0ae18b21985e0a97418b5334e300d06092a864886f70d01010b0500307c310b300906035840550406130255533113301106035504080c0a43616c69666f726e69613116301406035504070c0d53616e204672616e636973636f31123010060355040a0c094d584079436f6d70616e7931153013060355040b0c0c4d794465706172746d656e743115301306035504030c0c6d79646f6d61696e2e636f6d301e170d32343038323558403132343132315a170d3235303832353132343132315a307c310b30090603550406130255533113301106035504080c0a43616c69666f726e696131163014060358405504070c0d53616e204672616e636973636f31123010060355040a0c094d79436f6d70616e7931153013060355040b0c0c4d794465706172746d656e7431153058401306035504030c0c6d79646f6d61696e2e636f6d30820122300d06092a864886f70d01010105000382010f003082010a0282010100ba58de3aa83b075a69ee9e584074fe8a0c06259f41a64628f4c17a55c36b736ca4c7d475f05b176a0ac8c11785d50efcd65dfb450a718debad42cb27f32134a0c4d10dbb6529b14dd6b0d20cc558406e31fb6d9fbe7c8bfd012180d9b495b56dd67dd582dff7bbeb77ee15fd83f7904e8b12ebf1db46e74deb264c1b844be9fcc0c9acbadaf2d9b7d927495b000a965840fe4c4fe82633d8cfbdf5fabe23e636a8026942a00d07ff0e8fd9284b1e210d44b396fdb874fd48733d9e18df67a957ef2f0ba6a6afc3d012a21a2d98604930a358404a66a90386aa68b8cca84962908e94676d3e59cf78cd383487d5f93a35fcec554b70c1f6a47dc02e6b7811d5eba9ba5afaf8295dd10203010001a382011c3082584001183081ca0603551d110481c23081bf820c6d79646f6d61696e2e636f6d82107777772e6d79646f6d61696e2e636f6d820b6578616d706c652e636f6d820f77584077772e6578616d706c652e636f6d867f7765622b63617264616e6f3a2f2f616464722f616464725f74657374317172346a723975757130686477677273797a6558403270647234766b363634376336397375796e6476367237357764333071776b6c70706d7a75326177326c376d676b7a78727a336378766d32307578687735703758407064346a383879707332307565766e300b0603551d0f0404030205e0301d0603551d250416301406082b0601050507030106082b06010505070302301d06035558401d0e04160414e9b7c7100d76bd43a9c1404443c443d4b739caaf300d06092a864886f70d01010b050003820101008a86e00103f9873787e4a1d592ec3ce07819584037f3d7e302b0df83526e7c8170cdd47828e8e7a8c27b8a249b53acbce9aa4bbd9b81034e231f994cf6f74c8f6c15d44918dfb46e6c17a82d0fdd8f97aade5c6058404f4125db3fea5dc1acaa5f4c185109c947a35621b350c22fd4fb24a3931255a49d07a2cda5d422cfbded702cb613bb48871bc9d520a38c75482bea7682a13f7c5840332f808af64dcb89c6e04c8189ddb46ebd01c387dad899688740bdce89d472743d7c1150e5f197d616413561bdf511fdde30494fac8b033a61265916965462905840090615236a89af4e7e1a46285a838d823177c0734ce4442522df2de6967899b08d32610967ff132ad2347db86d42148158e10248722b0278f30067890c470101584023456789ab1a6703a2a0f647010123456789ab0a5820b45b8295e2701d2dbc8d4093fae94b979735f8c5e17a1843cf64a298e86659a2820201038206785377655840622b63617264616e6f3a2f2f616464722f7374616b655f7465737431757165686b636b306c616a713867723238743975786e757667637172633630373078336b58403972383034387a3879356773737274766e584004441e56b190be1238456c493c5ac1adbcaad398ba28e82dd4d63b250ff3436fc3a2f2c9f243992102beefaec45840d750bf51f84025c7fb4b8431dc77a7c2d0a907181e81d9800558203b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da2918288250eb5840f47281cf3c0a42fcceda894625ec34508cb997d509e0937f945813355e26a700186481a5000001820a000250ebf47281cf3c0a42fcceda894625ec3403200a644454657374"; + const RAW: &str = "0a8c5840a30a815902ae308202aa3082025ca00302010202147735a70599e68b49554b1cb3a6cf5e34583b3c2f300506032b6570307c310b300906035504061302555331584013301106035504080c0a43616c69666f726e69613116301406035504070c0d53616e204672616e636973636f31123010060355040a0c094d79436f6d70616e79584031153013060355040b0c0c4d794465706172746d656e743115301306035504030c0c6d79646f6d61696e2e636f6d301e170d3234313132393034333134305a1758400d3235313132393034333134305a307c310b30090603550406130255533113301106035504080c0a43616c69666f726e69613116301406035504070c0d53616e5840204672616e636973636f31123010060355040a0c094d79436f6d70616e7931153013060355040b0c0c4d794465706172746d656e743115301306035504030c0c58406d79646f6d61696e2e636f6d302a300506032b65700321007e082c662a8d4d3271d797067f36caf25d6472b83901620a2eac193331a7f871a381ef3081ec308158409e0603551d11048196308193820c6d79646f6d61696e2e636f6d82107777772e6d79646f6d61696e2e636f6d820b6578616d706c652e636f6d820f7777772e65584078616d706c652e636f6d86537765622b63617264616e6f3a2f2f616464722f7374616b655f7465737431757165686b636b306c616a713867723238743975786e5840757667637172633630373078336b3972383034387a3879356773737274766e300b0603551d0f0404030205e0301d0603551d250416301406082b06010505070358400106082b06010505070302301d0603551d0e04160414251ddd56123655faa9348ff93c1e92ce3bc15a29300506032b6570034100b11c80d36fdcba650b950f06584087e448b3bcbeb2caa5249b24aff83d16ebbb71249e44bd0ecfab8b40fb772b6f977f98ac9122e13954439d0120980b347e3f9707181e81d9800558206e42f8e5582e89a76ebb13ef279df7841efce978f106bee196f0e3cfd347bb31a2e8186481a4000001820a0003010a6454657374"; // Brotli data: 11 - const BROTLI: &str = "0b9458401b0306006678baf9873001af26aafd1674e5b11bf549d4bea0a39bfa0324c494f7003a6b4c1c4bac378e322cb280230a4002f5b2754e863806f7e524afc9995658406829cb08ab014e580e707468318ac0086262ffa6d3039a557178a3c556cbee076b44163b02b2812e49d3ab27b5e7bb2e0c6b1321d042d1069a24b4884032468a58400a28c6c0b0af3474b476028a193051b0641d5d778162040c24d7d2d1cc925f3d504c8171e5b4757466bc5104a11147eeb110f17d6067ae4b0d04a1d4200a3590584042a59443fca0277bc8e0b053c8101ca338ac0fe93e702d048761686fe9bdf0d511e472c6bb85a27f033a24d745712b933dbe1c51141d6309d9cb4fa9c41f2acc5840995aa78f9877abf5fe4c55fd4ed2120cbfde107fcefdb363e0e2e35774f7357a6ec99ada7c55e70497f29bb3e860cbe05fd8b1e3e6f6193b3853ad6afcfef77d5840afa5ef4cff767e1fcb1e347afdf176f2f3acd76e99d65d192fff1c3eb37eefad4f3777dd744fab80b466fecbcc79e11670e3d281afbf0e3a3f0b5e8d30e217eb584092ffeb8ddef4c8b073d44ddc36f3ef6ed1df5461d4428bfbf43525effdb557aedc74ecb2d1522bdfd9b5696059fab43568cfdaa6dde757a7d58f8d4ca773a2cb58402ec92e8406f6aa7f8607fd795394c13ff26d79eb613f96cc50fd7acddef25f3f3cabae20280cc1cb70d806e0b005509e25a145b68604e551a03c844733c00db25840c212b5b5d3900818fa2673b0d1d32e6dacf77e8a1b755c5eb8bf7f1d8d864d8c9a0c706c8a000b64626162b10034d1a4744c28573452f9342c50c20a0e0e0c695840080e138ae55c9a24180b0992d2028040ca62f3f91c85985a27a5b243387496428629021a64120e1588654dd2207e089f16c80c0d95f3852d69947081f679dafa584004028a101f005b207025906daae14d828944320a0f0b31417a17114c5eee3a65a02b3990b0e6486c62c2f104d5aeb0b39b5844b5df2e0c0df43c80d19fbd21bd58404f96a827de443e9059867cbefe18d97cbf339fdba2e45f50c93c5e3c5f7db479c0655ec1fafd2fd7661c98a744b39dede7647efb9e39ca3655a559dcdfce659b5840aff6d5bf3b3a6bedbdcada9c38d7db31afaa8eac5f5b936991a779266559b1e3b6dca3feaadf2ecb268d8a962f202fbdb042e574e9c05bbecf4ee37da9bdd6675840d40ecb86c4a9deaf24f892989600ff8e816f59e7fa4f3ec854f6dfddce3d001febbd75634e536fec818bfd2a4c14d56298fbf4e3ac29b3b8a0ba035f0dffde0358406939eb07d1f03ab732b39985f5639a24536766ffa6ec36ab648ff2ce619c223d2ccc7c92e8ea74dff7d94cd99ccdc3d43a4dfa7f63afab81adbb39f126cad2875840482ae68dc83e43f47e9d1418764ea2f7afb3a2a34b177f43ee4cabd4617b053ee311dfd677ff70ece4af9719b36605fd38f15061917089b674f68b69654b71045840467192ac80318630c94ae56f2c163436b11a58805dc71484d2316aa8284c2ce38a25f40601d6100c42802c8015868582c05045a83c882e14622209b734969068584057bc65eca05168123b2db2fcc886fd6bafcddeebf1c2573515e1aaff3981776ce9a7339f12e6382207df6f3c7e3df750de8f58d753bf33ba2877a4ab8e5e5e435840b6b053deec20963a4430ddf75fdcb9dcb77ed9eacb5e3c5d6123d5546afb286165f8e1d4258365b73c2d3cf0dcd75f30e5a548adf83f176ff527bbbe09cc1dda5820334badf960b27d7aa97150b5db2ac882a65c014130ae05210b0075d0a2fd8c05"; + const BROTLI: &str = "0b8958401bed02003c0e772c72637668c289a39b361dd1161a123da11e1118d08c7ab73ed1455e25aab3105e92334ba1fe128febedfb3e4912243755f42aca92094de82658404a4149a26917374c7cfd021376195439e4ea64844b46cfe1f87e9b6bf4d43c9dace1920ceeb2cc82bb60018b5b2de9571c5ea9c81dddd4077cc4571eb33181ce58409b38965811866581e4e903e6967333e85ab02e1b25665f272d24db06fb0183d3dd1cd937e2e260e3e0d045c976e057dde418766ea47dd551a68c20c015f508e25840118815ae0511d0e258b4440bd4b921222339016000ba853d2119394d8006a8a220d1c4755d3920f4403b302919d9c22f32106e1e10c3942a7d0f1c8ce42283205840528a20238dc8802d44cafe99612022d4abb8dc58894462642a21bb150449720b1f0e4e2a62bb9210b334d2f13ba4057d05f409d0fa3c666a3cb41cd012cc8e29584086ea45acd180f40932c052962cc156bad9f4a8a80d2f5d2e488c7ba8a496b1b1bf332482c7f8b9f981bfcb862878854a29842b460c8c782fb7905037399087685840693fc55005905188558891f50a0b0b0d8f0c04528e5d4a3c3c5c1cfc3360ef293f8938271225c0c6c727c4c59e3e4e09a2788847c7cfc79646ce62974cc11f1558405fbca2dc36eaf3594d18624bde0c7adac3324a828c2b833b3b3fbcd0c657c337f05bd53ece84f0adf329567b7234fe45897252656f11cf8ae6e56073452a93e85835133271a0fbc9f8d641adafb33a0267685f05fd95caf1ff3efa9d60febcfced727553ff21cd774cee682b161636860470b149c61f40"; // Zstd data: 12 - const ZSTD: &str= "0c95584028b52ffd601605ad2800b44ca50a815904a8308204a43082038ca00302010202141fec832800975bc0ae18b21985e0a97418b5334e300d06092a864886f70d015840010b0500307c310b300906035504061302555331133011080c0a43616c69666f726e696131163014070c0d53616e204672616e636973636f311230100a0c094d584079436f6d70616e79311530130b0c0c4d794465706172746d656e74030c0c6d79646f6d61696e2e636f6d301e170d3234303832353132343132315a170d323582584001220105000382010f003082010a0282010100ba58de3aa83b075a69ee9e74fe8a0c06259f41a64628f4c17a55c36b736ca4c7d475f05b176a0ac8c11785d50e5840fcd65dfb450a718debad42cb27f32134a0c4d10dbb6529b14dd6b0d20cc56e31fb6d9fbe7c8bfd012180d9b495b56dd67dd582dff7bbeb77ee15fd83f7904e8b584012ebf1db46e74deb264c1b844be9fcc0c9acbadaf2d9b7d927495b000a96fe4c4fe82633d8cfbdf5fabe23e636a8026942a00d07ff0e8fd9284b1e210d44b3965840fdb874fd48733d9e18df67a957ef2f0ba6a6afc3d012a21a2d98604930a34a66a90386aa68b8cca84962908e94676d3e59cf78cd383487d5f93a35fcec554b705840c1f6a47dc02e6b7811d5eba9ba5afaf8295dd10203010001a382011c308201183081ca0603551d110481c23081bf8282107777772e0b6578616d706c65820f8658407f7765622b63617264616e6f3a2f2f616464725f74657374317172346a723975757130686477677273797a653270647234766b363634376336397375796e64765840367237357764333071776b6c70706d7a75326177326c376d676b7a78727a336378766d3230757868773570377064346a383879707332307565766e300b06035558401d0f0404030205e0301d2504082b06010505070301020e04160414e9b7c7100d76bd43a9c1404443c443d4b739caaf0b01008a86e00103f9873787e4a1d592ec58403ce0781937f3d7e302b0df83526e7c8170cdd47828e8e7a8c27b8a249b53acbce9aa4bbd9b81034e231f994cf6f74c8f6c15d44918dfb46e6c17a82d0fdd8f975840aade5c604f4125db3fea5dc1acaa5f4c185109c947a35621b350c22fd4fb24a3931255a49d07a2cda5d422cfbded702cb613bb48871bc9d520a38c75482bea76584082a13f7c332f808af64dcb89c6e04c8189ddb46ebd01c387dad899688740bdce89d472743d7c1150e5f197d616413561bdf511fdde30494fac8b033a61265916584096546290090615236a89af4e7e1a46285a838d823177c0734ce4442522df2de6967899b08d32610967ff132ad2347db86d42148158e10248722b0278f300678958400c47010123456789ab1a6703a2a0f60a5820b45b8295e2701d2dbc8d4093fae94b979735f8c5e17a1843cf64a298e86659a282020103820678537374616b657558407165686b636b306c616a713867723238743975786e757667637172633630373078336b3972383034387a3879356773737274766e584004441e56b190be12384558406c493c5ac1adbcaad398ba28e82dd4d63b250ff3436fc3a2f2c9f243992102beefaec4d750bf51f84025c7fb4b8431dc77a7c2d0a907181e81d9800558203b6a584027bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da2918288250ebf47281cf3c0a42fcceda894625ec34508cb997d509e0937f945813355e584026a700186481a5000001820a000203200a6454657374001900b8f93ca19a5286c0f46288547ec4a40117ab88e058a8215aea28148137cc3850eed1b425543405581f22883507dcb24ec794216e78e8254099f1d1f9d5cd0576ee8d73c102eb8c02"; + const ZSTD: &str= "0c89584028b52ffd6000029d1100b41fa30a815902ae308202aa3082025ca00302010202147735a70599e68b49554b1cb3a6cf5e34583b3c2f300506032b6570307c310b5840300906035504061302555331133011080c0a43616c69666f726e696131163014070c0d53616e204672616e636973636f311230100a0c094d79436f6d70616e79584031153013060355040b0c0c4d794465706172746d656e74030c0c6d79646f6d61696e2e636f6d301e170d3234313132393034333134305a170d32352a0321007e5840082c662a8d4d3271d797067f36caf25d6472b83901620a2eac193331a7f871a381ef3081ec30819e0603551d110481963081938282107777772e820b6578616d5840706c65820f86537765622b63617264616e6f3a2f2f616464722f7374616b655f7465737431757165686b636b306c616a713867723238743975786e7576676371584072633630373078336b3972383034387a3879356773737274766e300b0f0404030205e0301d2504082b06010505070301020e04160414251ddd56123655faa93458408ff93c1e92ce3bc15a294100b11c80d36fdcba650b950f0687e448b3bcbeb2caa5249b24aff83d16ebbb71249e44bd0ecfab8b40fb772b6f977f98ac9122e139584054439d0120980b347e3f9707181e81d9800558206e42f8e589a76ebb13ef279df7841efce978f106bee196f0e3cfd347bb31a2e8186481a4000001820a000301583d0a64546573740013003d3e631feb0da1b068d5115f8161e2aed10b46d3acd0c00e1b9c80e50abeed00ca66cc432659ca8c6f3affd9b92ccedd01d66906"; #[test] fn test_decode_x509_chunks_raw() { From 39ff437f7725c93669b6f4a8bc1ba8a4d643cb87 Mon Sep 17 00:00:00 2001 From: bkioshn <35752733+bkioshn@users.noreply.github.com> Date: Fri, 29 Nov 2024 21:35:12 +0700 Subject: [PATCH 37/45] feat(rust/rbac-registration): add cardano registration chain (#90) * feat: add cardano registration chain Signed-off-by: bkioshn * feat: add wrapper to registration chain inner Signed-off-by: bkioshn * fix: format Signed-off-by: bkioshn * fix: comment Signed-off-by: bkioshn * fix: signing and encryption key Signed-off-by: bkioshn * chore: fix format Signed-off-by: bkioshn * fix: move struct to its own file and private the inner regis Signed-off-by: bkioshn --------- Signed-off-by: bkioshn --- rust/rbac-registration/src/lib.rs | 1 + .../src/registration/cardano/mod.rs | 589 ++++++++++++++++++ .../registration/cardano/payment_history.rs | 56 ++ .../src/registration/cardano/point_tx_idx.rs | 26 + .../src/registration/cardano/role_data.rs | 57 ++ .../rbac-registration/src/registration/mod.rs | 3 + 6 files changed, 732 insertions(+) create mode 100644 rust/rbac-registration/src/registration/cardano/mod.rs create mode 100644 rust/rbac-registration/src/registration/cardano/payment_history.rs create mode 100644 rust/rbac-registration/src/registration/cardano/point_tx_idx.rs create mode 100644 rust/rbac-registration/src/registration/cardano/role_data.rs create mode 100644 rust/rbac-registration/src/registration/mod.rs diff --git a/rust/rbac-registration/src/lib.rs b/rust/rbac-registration/src/lib.rs index 72fecd94b77..ab9cfd1e493 100644 --- a/rust/rbac-registration/src/lib.rs +++ b/rust/rbac-registration/src/lib.rs @@ -1,4 +1,5 @@ //! This crate provides functionalities for RBAC registration. pub mod cardano; +pub mod registration; pub(crate) mod utils; diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs new file mode 100644 index 00000000000..318772903a2 --- /dev/null +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -0,0 +1,589 @@ +//! Chain of Cardano registration data + +pub mod payment_history; +pub mod point_tx_idx; +pub mod role_data; + +use std::{collections::HashMap, sync::Arc}; + +use anyhow::bail; +use c509_certificate::c509::C509; +use pallas::{ + codec::utils::Bytes, crypto::hash::Hash, ledger::traverse::MultiEraTx, + network::miniprotocols::Point, +}; +use payment_history::PaymentHistory; +use point_tx_idx::PointTxIdx; +use role_data::RoleData; +use tracing::error; + +use crate::{ + cardano::cip509::{ + self, + rbac::{ + certs::{C509Cert, X509DerCert}, + pub_key::{Ed25519PublicKey, SimplePublicKeyType}, + CertKeyHash, + }, + Cip509, UuidV4, + }, + utils::general::decremented_index, +}; + +/// Registration chains. +pub struct RegistrationChain { + /// Inner part of the registration chain. + inner: Arc, +} + +impl RegistrationChain { + /// Create a new instance of registration chain. + /// The first new value should be the chain root. + /// + /// # Arguments + /// - `cip509` - The CIP509. + /// - `tracking_payment_keys` - The list of payment keys to track. + /// - `point` - The point (slot) of the transaction. + /// - `tx_idx` - The transaction index. + /// - `txn` - The transaction. + /// + /// # Errors + /// + /// Returns an error if data is invalid + pub fn new( + &self, point: Point, tracking_payment_keys: Vec, tx_idx: usize, + txn: &MultiEraTx, cip509: Cip509, + ) -> anyhow::Result { + let inner = RegistrationChainInner::new(cip509, tracking_payment_keys, point, tx_idx, txn)?; + + Ok(Self { + inner: Arc::new(inner), + }) + } + + /// Update the registration chain. + /// + /// # Arguments + /// - `point` - The point (slot) of the transaction. + /// - `tx_idx` - The transaction index. + /// - `txn` - The transaction. + /// - `cip509` - The CIP509. + /// + /// # Errors + /// + /// Returns an error if data is invalid + pub fn update( + &self, point: Point, tx_idx: usize, txn: &MultiEraTx, cip509: Cip509, + ) -> anyhow::Result { + let new_inner = self.inner.update(point, tx_idx, txn, cip509)?; + + Ok(Self { + inner: Arc::new(new_inner), + }) + } + + /// Get the current transaction ID hash. + #[must_use] + pub fn current_tx_id_hash(&self) -> Hash<32> { + self.inner.current_tx_id_hash + } + + /// Get a list of purpose for this registration chain. + #[must_use] + pub fn purpose(&self) -> &[UuidV4] { + &self.inner.purpose + } + + /// Get the map of index in array to point, transaction index, and x509 certificate. + #[must_use] + pub fn x509_certs(&self) -> &HashMap)> { + &self.inner.x509_certs + } + + /// Get the map of index in array to point, transaction index, and c509 certificate. + #[must_use] + pub fn c509_certs(&self) -> &HashMap { + &self.inner.c509_certs + } + + /// Get the map of index in array to point, transaction index, and public key. + #[must_use] + pub fn simple_keys(&self) -> &HashMap { + &self.inner.simple_keys + } + + /// Get a list of revocations. + #[must_use] + pub fn revocations(&self) -> &[(PointTxIdx, CertKeyHash)] { + &self.inner.revocations + } + + /// Get the map of role number to point, transaction index, and role data. + #[must_use] + pub fn role_data(&self) -> &HashMap { + &self.inner.role_data + } + + /// Get the list of payment keys to track. + #[must_use] + pub fn tracking_payment_keys(&self) -> &Vec { + &self.inner.tracking_payment_keys + } + + /// Get the map of payment key to its history. + #[must_use] + pub fn payment_history(&self) -> &HashMap> { + &self.inner.payment_history + } +} + +/// Inner structure of registration chain. +#[derive(Clone)] +struct RegistrationChainInner { + /// The current transaction ID hash (32 bytes) + current_tx_id_hash: Hash<32>, + /// List of purpose for this registration chain + purpose: Vec, + + // RBAC + /// Map of index in array to point, transaction index, and x509 certificate. + x509_certs: HashMap)>, + /// Map of index in array to point, transaction index, and c509 certificate. + c509_certs: HashMap, + /// Map of index in array to point, transaction index, and public key. + simple_keys: HashMap, + /// List of point, transaction index, and certificate key hash. + revocations: Vec<(PointTxIdx, CertKeyHash)>, + + // Role + /// Map of role number to point, transaction index, and role data. + role_data: HashMap, + /// List of payment keys to track. + tracking_payment_keys: Arc>, + /// Map of payment key to its history. + payment_history: HashMap>, +} + +impl RegistrationChainInner { + /// Create a new instance of registration chain. + /// The first new value should be the chain root. + /// + /// # Arguments + /// - `cip509` - The CIP509. + /// - `tracking_payment_keys` - The list of payment keys to track. + /// - `point` - The point (slot) of the transaction. + /// - `tx_idx` - The transaction index. + /// - `txn` - The transaction. + /// + /// # Errors + /// + /// Returns an error if data is invalid + fn new( + cip509: Cip509, tracking_payment_keys: Vec, point: Point, tx_idx: usize, + txn: &MultiEraTx, + ) -> anyhow::Result { + // Should be chain root, return immediately if not + if cip509.prv_tx_id.is_some() { + bail!("Invalid chain root, previous transaction ID should be None."); + } + + let mut validation_report = Vec::new(); + // Do the CIP509 validation, ensuring the basic validation pass. + if !cip509.validate(txn, tx_idx, &mut validation_report) { + // Log out the error if any + error!("CIP509 validation failed: {:?}", validation_report); + bail!("CIP509 validation failed, {:?}", validation_report); + } + + // Add purpose to the list + let purpose = vec![cip509.purpose]; + + let registration = cip509.x509_chunks.0; + let point_tx_idx = PointTxIdx::new(point, tx_idx); + + let x509_cert_map = chain_root_x509_certs(registration.x509_certs, &point_tx_idx); + let c509_cert_map = chain_root_c509_certs(registration.c509_certs, &point_tx_idx); + let public_key_map = chain_root_public_keys(registration.pub_keys, &point_tx_idx); + let revocations = revocations_list(registration.revocation_list, &point_tx_idx); + let role_data_map = chain_root_role_data(registration.role_set, txn, &point_tx_idx)?; + + let mut payment_history = HashMap::new(); + for tracking_key in &tracking_payment_keys { + // Keep record of payment history, the payment key that we want to track + let histories = update_payment_history(tracking_key, txn, &point_tx_idx)?; + payment_history.insert(tracking_key.clone(), histories); + } + + Ok(Self { + purpose, + current_tx_id_hash: txn.hash(), + x509_certs: x509_cert_map, + c509_certs: c509_cert_map, + simple_keys: public_key_map, + revocations, + role_data: role_data_map, + tracking_payment_keys: Arc::new(tracking_payment_keys), + payment_history, + }) + } + + /// Update the registration chain. + /// + /// # Arguments + /// - `point` - The point (slot) of the transaction. + /// - `tx_idx` - The transaction index. + /// - `txn` - The transaction. + /// - `cip509` - The CIP509. + /// + /// # Errors + /// + /// Returns an error if data is invalid + fn update( + &self, point: Point, tx_idx: usize, txn: &MultiEraTx, cip509: Cip509, + ) -> anyhow::Result { + let mut new_inner = self.clone(); + + let mut validation_report = Vec::new(); + // Do the CIP509 validation, ensuring the basic validation pass. + if !cip509.validate(txn, tx_idx, &mut validation_report) { + error!("CIP509 validation failed: {:?}", validation_report); + bail!("CIP509 validation failed, {:?}", validation_report); + } + + // Check and update the current transaction ID hash + if let Some(prv_tx_id) = cip509.prv_tx_id { + // Previous transaction ID in the CIP509 should equal to the current transaction ID + // or else it is not a part of the chain + if prv_tx_id == self.current_tx_id_hash { + new_inner.current_tx_id_hash = prv_tx_id; + } else { + bail!("Invalid previous transaction ID, not a part of this registration chain"); + } + } + + // Add purpose to the chain, if not already exist + let purpose = cip509.purpose; + if !self.purpose.contains(&purpose) { + new_inner.purpose.push(purpose); + } + + let registration = cip509.x509_chunks.0; + let point_tx_idx = PointTxIdx::new(point, tx_idx); + + update_x509_certs(&mut new_inner, registration.x509_certs, &point_tx_idx); + update_c509_certs(&mut new_inner, registration.c509_certs, &point_tx_idx)?; + update_public_keys(&mut new_inner, registration.pub_keys, &point_tx_idx); + + let revocations = revocations_list(registration.revocation_list, &point_tx_idx); + // Revocation list should be appended + new_inner.revocations.extend(revocations); + + update_role_data(&mut new_inner, registration.role_set, txn, &point_tx_idx)?; + + for tracking_key in self.tracking_payment_keys.iter() { + let histories = update_payment_history(tracking_key, txn, &point_tx_idx)?; + // If tracking payment key doesn't exist, insert an empty vector, + // then add the histories to the history vector + new_inner + .payment_history + .entry(tracking_key.clone()) + .or_default() + .extend(histories); + } + + Ok(new_inner) + } +} + +/// Process x509 certificate for chain root. +fn chain_root_x509_certs( + x509_certs: Option>, point_tx_idx: &PointTxIdx, +) -> HashMap)> { + let mut map = HashMap::new(); + if let Some(cert_list) = x509_certs { + for (idx, cert) in cert_list.iter().enumerate() { + // Chain root, expect only the certificate not undefined or delete + if let cip509::rbac::certs::X509DerCert::X509Cert(cert) = cert { + map.insert(idx, (point_tx_idx.clone(), cert.clone())); + } + } + } + map +} + +/// Update x509 certificates in the registration chain. +fn update_x509_certs( + new_inner: &mut RegistrationChainInner, x509_certs: Option>, + point_tx_idx: &PointTxIdx, +) { + if let Some(cert_list) = x509_certs { + for (idx, cert) in cert_list.iter().enumerate() { + match cert { + // Unchanged to that index, so continue + cip509::rbac::certs::X509DerCert::Undefined => continue, + // Delete the certificate + cip509::rbac::certs::X509DerCert::Deleted => { + new_inner.x509_certs.remove(&idx); + }, + // Add the new certificate + cip509::rbac::certs::X509DerCert::X509Cert(cert) => { + new_inner + .x509_certs + .insert(idx, (point_tx_idx.clone(), cert.clone())); + }, + } + } + } +} + +/// Process c509 certificates for chain root. +fn chain_root_c509_certs( + c509_certs: Option>, point_tx_idx: &PointTxIdx, +) -> HashMap { + let mut map = HashMap::new(); + if let Some(cert_list) = c509_certs { + for (idx, cert) in cert_list.iter().enumerate() { + if let cip509::rbac::certs::C509Cert::C509Certificate(cert) = cert { + // Chain root, expect only the certificate not undefined or delete + map.insert(idx, (point_tx_idx.clone(), *cert.clone())); + } + } + } + map +} + +/// Update c509 certificates in the registration chain. +fn update_c509_certs( + new_inner: &mut RegistrationChainInner, c509_certs: Option>, + point_tx_idx: &PointTxIdx, +) -> anyhow::Result<()> { + if let Some(cert_list) = c509_certs { + for (idx, cert) in cert_list.iter().enumerate() { + match cert { + // Unchanged to that index, so continue + cip509::rbac::certs::C509Cert::Undefined => continue, + // Delete the certificate + cip509::rbac::certs::C509Cert::Deleted => { + new_inner.c509_certs.remove(&idx); + }, + // Certificate reference + cip509::rbac::certs::C509Cert::C509CertInMetadatumReference(_) => { + bail!("Unsupported c509 certificate in metadatum reference") + }, + // Add the new certificate + cip509::rbac::certs::C509Cert::C509Certificate(c509) => { + new_inner + .c509_certs + .insert(idx, (point_tx_idx.clone(), *c509.clone())); + }, + } + } + } + Ok(()) +} + +/// Process public keys for chain root. +fn chain_root_public_keys( + pub_keys: Option>, point_tx_idx: &PointTxIdx, +) -> HashMap { + let mut map = HashMap::new(); + if let Some(key_list) = pub_keys { + for (idx, key) in key_list.iter().enumerate() { + // Chain root, expect only the public key not undefined or delete + if let cip509::rbac::pub_key::SimplePublicKeyType::Ed25519(key) = key { + map.insert(idx, (point_tx_idx.clone(), key.clone())); + } + } + } + map +} + +/// Update public keys in the registration chain. +fn update_public_keys( + new_inner: &mut RegistrationChainInner, pub_keys: Option>, + point_tx_idx: &PointTxIdx, +) { + if let Some(key_list) = pub_keys { + for (idx, cert) in key_list.iter().enumerate() { + match cert { + // Unchanged to that index, so continue + cip509::rbac::pub_key::SimplePublicKeyType::Undefined => continue, + // Delete the public key + cip509::rbac::pub_key::SimplePublicKeyType::Deleted => { + new_inner.simple_keys.remove(&idx); + }, + // Add the new public key + cip509::rbac::pub_key::SimplePublicKeyType::Ed25519(key) => { + new_inner + .simple_keys + .insert(idx, (point_tx_idx.clone(), key.clone())); + }, + } + } + } +} + +/// Process the revocation list. +fn revocations_list( + revocation_list: Option>, point_tx_idx: &PointTxIdx, +) -> Vec<(PointTxIdx, CertKeyHash)> { + let mut revocations = Vec::new(); + if let Some(revocations_data) = revocation_list { + for item in revocations_data { + revocations.push((point_tx_idx.clone(), item.clone())); + } + } + revocations +} + +/// Process the role data for chain root. +fn chain_root_role_data( + role_set: Option>, txn: &MultiEraTx, + point_tx_idx: &PointTxIdx, +) -> anyhow::Result> { + let mut role_data_map = HashMap::new(); + if let Some(role_set_data) = role_set { + for role_data in role_set_data { + let signing_key = role_data.role_signing_key.clone(); + let encryption_key = role_data.role_encryption_key.clone(); + + // Get the payment key + let payment_key = get_payment_key_from_tx(txn, role_data.payment_key)?; + + // Map of role number to point and role data + role_data_map.insert( + role_data.role_number, + ( + point_tx_idx.clone(), + RoleData::new( + signing_key, + encryption_key, + payment_key, + role_data.role_extended_data_keys.clone(), + ), + ), + ); + } + } + Ok(role_data_map) +} + +/// Update the role data in the registration chain. +fn update_role_data( + inner: &mut RegistrationChainInner, role_set: Option>, + txn: &MultiEraTx, point_tx_idx: &PointTxIdx, +) -> anyhow::Result<()> { + if let Some(role_set_data) = role_set { + for role_data in role_set_data { + // If there is new role singing key, use it, else use the old one + let signing_key = match role_data.role_signing_key { + Some(key) => Some(key), + None => { + match inner.role_data.get(&role_data.role_number) { + Some((_, role_data)) => role_data.signing_key_ref().clone(), + None => None, + } + }, + }; + + // If there is new role encryption key, use it, else use the old one + let encryption_key = match role_data.role_encryption_key { + Some(key) => Some(key), + None => { + match inner.role_data.get(&role_data.role_number) { + Some((_, role_data)) => role_data.encryption_ref().clone(), + None => None, + } + }, + }; + let payment_key = get_payment_key_from_tx(txn, role_data.payment_key)?; + + // Map of role number to point and role data + // Note that new role data will overwrite the old one + inner.role_data.insert( + role_data.role_number, + ( + point_tx_idx.clone(), + RoleData::new( + signing_key, + encryption_key, + payment_key, + role_data.role_extended_data_keys.clone(), + ), + ), + ); + } + } + Ok(()) +} + +/// Helper function for retrieving the payment key from the transaction. +fn get_payment_key_from_tx( + txn: &MultiEraTx, payment_key_ref: Option, +) -> anyhow::Result { + // The index should exist since it pass the basic validation + if let Some(key_ref) = payment_key_ref { + if let MultiEraTx::Conway(tx) = txn { + // Transaction output + if key_ref < 0 { + let index = decremented_index(key_ref.abs())?; + if let Some(output) = tx.transaction_body.outputs.get(index) { + // Conway era -> Post alonzo tx output + match output { + pallas::ledger::primitives::conway::PseudoTransactionOutput::PostAlonzo( + o, + ) => { + let payment_key: Ed25519PublicKey = + o.address.clone().try_into().map_err(|_| { + anyhow::anyhow!("Failed to convert Vec to Ed25519PublicKey in payment key reference") + })?; + return Ok(payment_key); + }, + // Not support legacy form of transaction output + pallas::ledger::primitives::conway::PseudoTransactionOutput::Legacy(_) => { + bail!("Unsupported transaction output type in payment key reference"); + }, + } + } + // Index doesn't exist + bail!("Payment key not found in transaction output"); + } + // Transaction input, currently unsupported because of the reference to transaction hash + bail!("Unsupported payment key reference to transaction input"); + } + } + Ok(Ed25519PublicKey::default()) +} + +/// Update the payment history given the tracking payment keys. +fn update_payment_history( + tracking_key: &Ed25519PublicKey, txn: &MultiEraTx, point_tx_idx: &PointTxIdx, +) -> anyhow::Result> { + let mut payment_history = Vec::new(); + if let MultiEraTx::Conway(tx) = txn { + // Conway era -> Post alonzo tx output + for (index, output) in tx.transaction_body.outputs.iter().enumerate() { + match output { + pallas::ledger::primitives::conway::PseudoTransactionOutput::PostAlonzo(o) => { + let address_bytes: Bytes = tracking_key.clone().into(); + if address_bytes == o.address { + let output_index: u16 = index.try_into().map_err(|_| { + anyhow::anyhow!("Cannot convert usize to u16 in update payment history") + })?; + + payment_history.push(PaymentHistory::new( + point_tx_idx.clone(), + txn.hash(), + output_index, + o.value.clone(), + )); + } + }, + pallas::ledger::primitives::conway::PseudoTransactionOutput::Legacy(_) => { + bail!("Unsupported transaction output type in update payment history"); + }, + } + } + } + Ok(payment_history) +} diff --git a/rust/rbac-registration/src/registration/cardano/payment_history.rs b/rust/rbac-registration/src/registration/cardano/payment_history.rs new file mode 100644 index 00000000000..13e4aabc5d5 --- /dev/null +++ b/rust/rbac-registration/src/registration/cardano/payment_history.rs @@ -0,0 +1,56 @@ +//! Payment history of the public key in tracking payment keys. + +use pallas::{crypto::hash::Hash, ledger::primitives::conway::Value}; + +use super::point_tx_idx::PointTxIdx; + +/// Payment history of the public key in tracking payment keys. +#[derive(Clone)] +pub struct PaymentHistory { + /// The point and transaction index. + point_tx_idx: PointTxIdx, + /// Transaction hash that this payment come from. + tx_hash: Hash<32>, + /// The transaction output index that this payment come from. + output_index: u16, + /// The value of the payment. + value: Value, +} + +impl PaymentHistory { + /// Create an instance of payment history. + pub(crate) fn new( + point_tx_idx: PointTxIdx, tx_hash: Hash<32>, output_index: u16, value: Value, + ) -> Self { + PaymentHistory { + point_tx_idx, + tx_hash, + output_index, + value, + } + } + + /// Get the point and transaction index. + #[must_use] + pub fn point_tx_idx(&self) -> &PointTxIdx { + &self.point_tx_idx + } + + /// Get the transaction hash. + #[must_use] + pub fn tx_hash(&self) -> Hash<32> { + self.tx_hash + } + + /// Get the transaction output index. + #[must_use] + pub fn output_index(&self) -> u16 { + self.output_index + } + + /// Get the value of the payment. + #[must_use] + pub fn value(&self) -> &Value { + &self.value + } +} diff --git a/rust/rbac-registration/src/registration/cardano/point_tx_idx.rs b/rust/rbac-registration/src/registration/cardano/point_tx_idx.rs new file mode 100644 index 00000000000..27247dd5c2f --- /dev/null +++ b/rust/rbac-registration/src/registration/cardano/point_tx_idx.rs @@ -0,0 +1,26 @@ +//! Point or absolute slot and transaction index. + +use pallas::network::miniprotocols::Point; + +/// Point (slot) and transaction index. +#[derive(Clone)] +pub struct PointTxIdx((Point, usize)); + +impl PointTxIdx { + /// Create an instance of point and transaction index. + pub(crate) fn new(point: Point, tx_idx: usize) -> Self { + PointTxIdx((point, tx_idx)) + } + + /// Get the point. + #[must_use] + pub fn point(&self) -> &Point { + &self.0 .0 + } + + /// Get the transaction index. + #[must_use] + pub fn tx_idx(&self) -> usize { + self.0 .1 + } +} diff --git a/rust/rbac-registration/src/registration/cardano/role_data.rs b/rust/rbac-registration/src/registration/cardano/role_data.rs new file mode 100644 index 00000000000..684671415e7 --- /dev/null +++ b/rust/rbac-registration/src/registration/cardano/role_data.rs @@ -0,0 +1,57 @@ +//! RBAC role data + +use std::collections::HashMap; + +use crate::cardano::cip509::rbac::{pub_key::Ed25519PublicKey, role_data::KeyLocalRef}; + +/// Role data +#[derive(Clone)] +pub struct RoleData { + /// A signing keys to the data within registration. + signing_key_ref: Option, + /// An encryption keys to the data within registration. + encryption_ref: Option, + /// A payment key where reward will be distributed to. + payment_key: Ed25519PublicKey, + /// Map of role extended data (10-99) to its data + role_extended_data: HashMap>, +} + +impl RoleData { + /// Create an instance of role data. + pub(crate) fn new( + signing_key_ref: Option, encryption_ref: Option, + payment_key: Ed25519PublicKey, role_extended_data: HashMap>, + ) -> Self { + RoleData { + signing_key_ref, + encryption_ref, + payment_key, + role_extended_data, + } + } + + /// Get the reference of signing keys. + #[must_use] + pub fn signing_key_ref(&self) -> &Option { + &self.signing_key_ref + } + + /// Get the reference of encryption keys. + #[must_use] + pub fn encryption_ref(&self) -> &Option { + &self.encryption_ref + } + + /// Get the payment key. + #[must_use] + pub fn payment_key(&self) -> &Ed25519PublicKey { + &self.payment_key + } + + /// Get the role extended data. + #[must_use] + pub fn role_extended_data(&self) -> &HashMap> { + &self.role_extended_data + } +} diff --git a/rust/rbac-registration/src/registration/mod.rs b/rust/rbac-registration/src/registration/mod.rs new file mode 100644 index 00000000000..a6a5c3c002d --- /dev/null +++ b/rust/rbac-registration/src/registration/mod.rs @@ -0,0 +1,3 @@ +//! Registration module + +pub mod cardano; From ccfd89e1cfba26351faba7438ce943d694d508cf Mon Sep 17 00:00:00 2001 From: bkioshn Date: Mon, 2 Dec 2024 12:31:28 +0700 Subject: [PATCH 38/45] fix: remove txn index and change test data to chain root Signed-off-by: bkioshn --- .../src/cardano/cip509/validation.rs | 33 ++++++++----------- .../src/test_data/cardano/conway_1.block | 2 +- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/validation.rs b/rust/rbac-registration/src/cardano/cip509/validation.rs index eae214d1e69..0b5cd18196b 100644 --- a/rust/rbac-registration/src/cardano/cip509/validation.rs +++ b/rust/rbac-registration/src/cardano/cip509/validation.rs @@ -334,7 +334,7 @@ fn validate_aux_helper( /// Negative ref is for transaction output. /// Positive ref is for transaction input. pub(crate) fn validate_payment_key( - txn: &MultiEraTx, txn_idx: usize, role_data: &RoleData, validation_report: &mut Vec, + txn: &MultiEraTx, role_data: &RoleData, validation_report: &mut Vec, ) -> Option { let function_name = "Validate Payment Key"; @@ -375,7 +375,6 @@ pub(crate) fn validate_payment_key( &o.address.to_vec(), validation_report, &witness, - txn_idx, ); }, pallas::ledger::primitives::conway::PseudoTransactionOutput::PostAlonzo( @@ -385,7 +384,6 @@ pub(crate) fn validate_payment_key( &o.address.to_vec(), validation_report, &witness, - txn_idx, ); }, }; @@ -427,19 +425,15 @@ pub(crate) fn validate_payment_key( /// Helper function for validating payment output key. fn validate_payment_output_key_helper( - output_address: &[u8], validation_report: &mut Vec, witness: &TxWitness, txn_idx: usize, + output_address: &[u8], validation_report: &mut Vec, witness: &TxWitness, ) -> Option { - let idx = match u16::try_from(txn_idx) { - Ok(value) => value, - Err(e) => { - validation_report.push(format!("Transaction index conversion failed: {e}")); - return None; - }, - }; // Extract the key hash from the output address if let Some(key) = extract_key_hash(output_address) { // Compare the key hash and return the result - return Some(compare_key_hash(&[key], witness, idx).is_ok()); + // Set transaction index to 0 because the list of transaction is manually constructed + // for TxWitness -> &[txn.clone()], so we can assume that the witness contains only + // the witness within this transaction. + return Some(compare_key_hash(&[key], witness, 0).is_ok()); } validation_report.push("Failed to extract payment key hash from address".to_string()); None @@ -460,6 +454,7 @@ pub(crate) fn validate_role_singing_key( validation_report.push(format!( "{function_name}, Role signing key should reference certificate, not public key", )); + println!("ja"); return false; } } @@ -522,7 +517,7 @@ mod tests { let transactions = multi_era_block.txs(); // Second transaction of this test data contains the CIP509 auxiliary data let tx = transactions - .get(1) + .get(3) .expect("Failed to get transaction index"); let aux_data = cip_509_aux_data(tx); let mut decoder = Decoder::new(aux_data.as_slice()); @@ -540,7 +535,7 @@ mod tests { let transactions = multi_era_block.txs(); // Second transaction of this test data contains the CIP509 auxiliary data let tx = transactions - .get(1) + .get(3) .expect("Failed to get transaction index"); validate_aux(tx, &mut validation_report); @@ -557,7 +552,7 @@ mod tests { let transactions = multi_era_block.txs(); // Second transaction of this test data contains the CIP509 auxiliary data let tx = transactions - .get(1) + .get(3) .expect("Failed to get transaction index"); let aux_data = cip_509_aux_data(tx); @@ -577,7 +572,7 @@ mod tests { let transactions = multi_era_block.txs(); // Second transaction of this test data contains the CIP509 auxiliary data let tx = transactions - .get(1) + .get(3) .expect("Failed to get transaction index"); let aux_data = cip_509_aux_data(tx); @@ -588,7 +583,7 @@ mod tests { if let Some(role_set) = &cip509.x509_chunks.0.role_set { for role in role_set { if role.role_number == 0 { - assert!(validate_payment_key(tx, 0, role, &mut validation_report,).unwrap()); + assert!(validate_payment_key(tx, role, &mut validation_report,).unwrap()); } } } @@ -604,7 +599,7 @@ mod tests { let transactions = multi_era_block.txs(); // Second transaction of this test data contains the CIP509 auxiliary data let tx = transactions - .get(1) + .get(3) .expect("Failed to get transaction index"); let aux_data = cip_509_aux_data(tx); @@ -641,7 +636,7 @@ mod tests { if let Some(role_set) = &cip509.x509_chunks.0.role_set { for role in role_set { if role.role_number == 0 { - assert!(validate_payment_key(tx, 0, role, &mut validation_report,).unwrap()); + assert!(validate_payment_key(tx, role, &mut validation_report,).unwrap()); } } } diff --git a/rust/rbac-registration/src/test_data/cardano/conway_1.block b/rust/rbac-registration/src/test_data/cardano/conway_1.block index 137c6e3be81..5361450212d 100644 --- a/rust/rbac-registration/src/test_data/cardano/conway_1.block +++ b/rust/rbac-registration/src/test_data/cardano/conway_1.block @@ -1 +1 @@ -820785828a1a002ccb261a0499865c58209bf00c1465594b9cbc4afcf4ab9abb70c1c96732407d5bc07b30b4b30e5bc0a15820de38e66a4d84e9197670fc33e571603b5c54f6b20067be727c9f6027e11b56b7582040f01e8e7c3dca1cab6bdf3be3b95b1966b6f8ba74bf1f2b20f647d77b21e272825840db68ec72a5b6b89e587ef774610b8c2a336abf35e2563e14638648333389b535248b0722657adf594dab1d8ad191f317d94adf8c06d93097952f30032fc5e5975850db002cc64f44772f221b37fb1cf74ebb451e329259a6744f2476f88bea536b39023234901abdde450a012cf47f26b6d385ea4f0e93ea64ac06bbbc056452fcdfee4a8ecc4a674542daae2921d8ded2031906e75820415d178ac638757394f411aaff696d5448926c4fd87dc8394d80b929c802a9e184582055f458677d1f7280698d9caf1df339d5af2972ec3757be29372747bbf8ce2c5f0519022b584024cfb714a8d74800f267785ee123ae49ee678d12b4f4b54799e176c089fa4a9ffc2af663346d2ad5e6e5a12044a155608066dac87058b74e90b2042abc6d170d820a005901c0fc80e58656078f05dc94a7d1c880c50c972e22462fc85e188e6e6f318e7af888abc4e31e7bf8888ce11e147d93452cf129886d201ab6a73b01d6eca26dd506027b6212ebc2e64fb3494980642daaaf374307e9d52a38a905c09d463c337e37c3b27667ec1fe958d860d31edeb70682a03a40dc40ed541ab68e887e01590bb693b1539bafebcaed000308e78f0ee7b41124b5cc5b5bdee0d93f967a9f4fc0b561222d22bb9ddce32e6fe4540607dc2bc5007e70ee4565af5c4eac81ad8e398eab665c595eb954c89a3ae27214beafc71ba043a2209fd2689e5748d03f9e4fa9c5b0ffa2f7f83833801ce5e93e744200546017894abb44ba15936ebca8675fbfee2df23b3d1bdddea6f01a2ba1d1dd578da5bbd5a13060dcb23bdb28654b6ea734d9d810d3a48c18eb7779da2f939dfa61b0b29c934fc04942769dcb397dd90f841c82b63a76028fb575d6eb48101a784546381822df66aa2b001045f2493001b794a5a308b0cb79054f593f99d5224ad2ed87e9822537b5699aed45656448be056a7a29acce6560bf8aa2b74c4834fcbca6399785f49f25efbf05698838dd57128aec11382f4b74d6c1fda4bae6392e74ceb99a72c591c2c21d501a3c7aca864482ab0084825820aab6bd3323da74be3ff8a5dd4032fb6f8ce7452a2d2abc0008e816db96ebff0202825820f28d02bd171a11fe59dad6744f742d8d96e69f4dfa59c9c3936fae3062144bab00825820f28d02bd171a11fe59dad6744f742d8d96e69f4dfa59c9c3936fae3062144bab02825820f62e2faea6659472b4b98415367fc143f64d935f7acf5186129737e20a50c063020183a300581d70b07d22a4dc75abdba1b8c80033a15b85305b76521a0114b17f291a8701821a001e8480a1581c362e3f869c98ce971ead0e2705c56df467ddd2aecb44f6f216c3e1d5a1484e6f64654665656401028201d818583ad87a9fd8799f581cbd95d582888acda57a20256bb03e4c4abb6bdf09a47d788605412c53d8799fd8799f1906d41b00000193761d6992ffffffff82581d60bd95d582888acda57a20256bb03e4c4abb6bdf09a47d788605412c531a0089544082581d60bd95d582888acda57a20256bb03e4c4abb6bdf09a47d788605412c531a00bc0b8c021a0007ac07031a049986c7081a0499864f0b582068f0db238ceffb3af04581959c68b310531097487ac7a9b4f8e1d850d01c92830d81825820f28d02bd171a11fe59dad6744f742d8d96e69f4dfa59c9c3936fae3062144bab010e81581cbd95d582888acda57a20256bb03e4c4abb6bdf09a47d788605412c531082581d60bd95d582888acda57a20256bb03e4c4abb6bdf09a47d788605412c531a00524801111a00370c3f1281825820180fb7cfedba19697b4d164867c49ad8bf2493f6dd51d860aa03cd3e46301cb600a6008182582081a561fbb27af9f739c1893e83ed232c582bc70ee7817703743af9fa0e7a06c6000181a200583900eb21979c03eed7207020b2a0b47565b5aafb1a2c3849b59a1fa8e6c5e075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d6473903011b000000024fa63d6a021a000316b107582065328b47a7b512ded7757865e3755c9ba7cd6161ff5142390fba09b489c63fd60e81581ce075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d64739030f0082a20081825820df06fec0a22f6839e6637f11dabb6c98995ffb30136983fe240e76d828a8017f58408870329282aabe756fae43d511bb70eb47bf8162afd108457078cc09cf12e0ef3a3ef484d93432f613fdf94014dddccc2d9d5da12188c649c6ac5cab3080030f05a182000182d87980821a00132df31a112de183a100828258208469288efa6f9cb49040b43dcff93f40969d72433f751afde50235012c016020584052f1396834e485d805a5bfbc5255b89535cfb1aa36344f69fba03304b3fc28182aeba41e304b5e6008fb43ebcb3aa192bba42fdbe55801347930ddccdd067c0582582076af5530fa318a370820270031d1838545a4ceed8696510627563d1114d4182b5840a06561722f5a03d015cf33f7d44984f8bb21fa96dc157dd7659464e212b36b2baeb3b68039977f388f012a64a62256db3ae379021c8541de211ad175f51d1d07a101a11901fda50050ca7a1457ef9f4c7f9c747f8c4a4cfa6c0150215f9394884fa3614b6b309dcbafaa410258204d3f576f26db29139981a69443c2325daa812cc353a31b5a4db794a5bcbb06c20b8858401bed020064f03bdf8a48de44553c0c4e47f98bc0d14d7dc0477ce5311b13e8bc89638915615816489e3e606e393783ae05ebb25162f675b28a87bad0b682126d58406373d47d399d46a5d1142d473d1af3a3f10b5be75d4bde3c343115d1cb8fa980c9a2fba3053014f2019745efc060c9691dda413990e68623e09d005005140b7b5840b6404813a00c48a2e009b82d065d394025900fcc1708167e9181e879761053aaf43d30a98565e5212cab02ca832ab0725788acf06786814fa857e956ca704e205840e69bd2a2ac78731e6b44c5335f7a44b0fd55a87d79a1cdda7270ddafe22af411a077002dcd63a39481e60234abb4635a92bc88351af09f2003acb86409b6c6b658401b10edebb7e1659f5cbe6a38dc2712c1c2b8656c2ffb3b8324865726008ef72937a0ca804415908954200a58a15c9cc40646ca57ae841189b262a698ae18c5725840a962bf68ac3c19c7009f6357cc60d069cc47c0d8532743c439c7cfa232996c3a75fab412247ac850388df73bca2a76f8b3a1fcb4dfd54d332f543bd1d5abcf675840eb50d6677fcdf8f26ddcfb758710d1aae18c33e25ddc931f326555175cfb76e596af36110e6ead5277e07f5d615deab4aa8a6d633b6cf499a79dae8e46036e9e583d2e3edf2cd098d0ed91ccce965cdd5fcf27accb1d927f742ff939daf4eff5c0cfaca38fe77e7a7af16aa34370e52b4d04ada650a8251e856ee145fe000218635840aee26347b9702d53b82f157a57f3f4960101e8e7e104215d587da715748196ef09aa8e79da3f4f7f7e2ffc5a32be6f46d5f4598c17309a34bb12c5bcd6e3f50380 \ No newline at end of file +820785828a1a002cf1361a049d798e5820f8105b5f1ff5805a1ad73a4bed95297c11a530957fd124fe116d847b9a8a2d18582054ca56226c90d5544bf6f558026b814027b634e60de0bc215237944110569c6c582037d6147adbf2907948d66f9e4751774bbe9d6c4ad562ce04ef66f0fc1e7ac3018258408efeee2071a87217e4e5d45c8afcefbca69a9f7521be99cf007cadd5657436f05e801b96f75465cc631dbca05f58c0f44406f3bf80f055ac47285dfe4e72f5185850dc503d811e615d063298b2688dfcdeb4c82b82a6509672ad7eaeafa87ec8af4144530d7ad542daf0f007b3ba2133ea5abebb253ec95180f3805eefd1a1cfebafd497bf0215506912fd50497db4ba150e19173458200cb42de7dcf6a9dd27dac89dd10e98af017f352c5d4c9d995fea811ed22221d484582074deff4c062c74d426154a221193a5fb9472c401d42217b07d617c03cc2e9bb00819023f584049885039bea644a49bce5493fd4dffb0c835074820bf1912e3b372bfbba36904c7a47ae3ad59da8148876d83868488b94fa81df458fd3bf3d3706f4ec11a5d00820a005901c0509399cc5ca4713330d1370f986def5b3096f61c96f3de54015334c5abccad85c0b4c99a898399ccdf5979a0b810be83479c2b4430c665d350878f08c1bdc80b7c4cdd132322a7ccf28796aedaf1767c7ba8d9029430f8e3ecc88963f671a07163f7d2677e3c31db8af39c51732fd158d4784bd48381006d4a892d186266279241a9e786e2b4fe28d83a9890694670950c6d981e96a1d182d5e02241cf36f6278ff8e7cf6206735d54e8bd026f696d3b46d31268357318c4e2a91b084b7a49975bf06ec920ca357a17f85d6fca8ac7b388c599e542ec32f0361e2fda5c8d43e3e3b52e31761953c114ca9f952eff1195245df3aeef714de2bc5dab7e971b325b99ed37346741bf75ef68b201a9a9f85978d83ad602e9c8e31987395473eb87b2f5a34c30882a1812dc5081f04e731071cb8a58c5c7fc4ba168028154b07827faa64a6fd86e0594804c8e74213860a0d0a602306b8468a2f7ee112b380f143437227bdc37e8efee149b5bbe436f1b1d23a1afecd4c2d17a2484827f655b85228d587e54d26530645bb4de4aa4a706d0676974ac64fa4c2bc158adecd78efbed7f7522e0627056391093fcf37967f35f3479878ef3e731e21eac3d787f7a2af2c784ab00828258206f135b9e227de387653f19561fdc0a014eacdb9c30c5d8da5fb8adb302f24c6d01825820dee38df4b12e03388390d6452765086e4348da004f13f4b3dff24cc2fdd99055000182a300581d70bdc22da682cd9aceed5fd48914789fc98c94abc79fed8b40cb8c431401821a001e8480a1581cc13ddf298a5d25aff2933695987912b4f1748bdf0df8e4b5d85f2360a14e50524550524f445f4f5241434c4501028201d81856d8799fd8799f1a0bf5b443ff1b0000019386d2d7a8ff82581d60c0359ebb7d0688d79064bd118c99c8b87b5853e3af59245bb97e84d21b000000011722b776021a0006a164031a049d7c7d081a049d75760b582078c96a0e0f77a9be3baedbc5e24fe7c5f4de3f13dc79ada0676d7a0fd12fa6d90d818258206f135b9e227de387653f19561fdc0a014eacdb9c30c5d8da5fb8adb302f24c6d010e81581cc0359ebb7d0688d79064bd118c99c8b87b5853e3af59245bb97e84d20f001082581d60c0359ebb7d0688d79064bd118c99c8b87b5853e3af59245bb97e84d21b0000000116dd0d9a111a004c4b40a900d9010282825820b7141e4950cd9311eb083caffda6dad64ca0956e44e2855316d4c1553da230e800825820b7141e4950cd9311eb083caffda6dad64ca0956e44e2855316d4c1553da230e8010dd9010281825820688a09a2a039214f244e8f8eed49bf4f190104ebe32f6d9680277d70901c20220112d9010281825820c47f5b7661fd8295674076b8b76db11d08d4ddafb2529a6159e059ed6128af73010182a300581d701db5ea0b1cb006f32a3c3f6438f6c4b3df7b2018b965a63d29b54d0301821a001b9f18a1581caa7f3d7240a9d06e947fcf387957e7f01fd4484a432a4b879cdbf24aa14d446a65644f7261636c654e465401028201d81858a6d8799f5840fe28ac9c796a0157a6da71b9738beae6a085d1debdd682838584e03592e6581a50bc341e879b8b197a88a6d175a01e8d51c72c4acbf3fbaac3fb7158e3c2500fd8799fd8799f1a00030d401a000381f7ffd8799fd8799fd87a9f1b00000193858940a8ffd87a80ffd8799fd87a9f1b000001938596fc48ffd87a80ffff43555344ff581caa7f3d7240a9d06e947fcf387957e7f01fd4484a432a4b879cdbf24aff82583900f8e2356e21af3563556c167c31bcc00f9fc061a8a852a16a42ffcc7927ce10b87147e3e19a5dc622b6a535ab9ce01cb2d586a3a690a74c0e1a00f81c6a021a000426b4031a049d7c7d081a049d78f90ed9010281581cf8e2356e21af3563556c167c31bcc00f9fc061a8a852a16a42ffcc790b5820cc99e113594d6781a6f5a4a16205a0ae3072c1f4414121e08df388fdeb656fd9a700d9010282825820d59aa48a2208563e6fac203419186430c87b5c07ed72c5c1947f5c2b8cdd810600825820ffc0aaef9f13145bee27320c87410bd607c9eeb7f9d624ad4d18266227ac9596010183a300581d709dcb619aefe878561bc3067b7a0e923f1ce961c38b7bfc1a283a873e01821a001bafeea1581cec64872c1965bbbaa8868aef2bd9a343b821a9f2c7787ea096f62262a154000643b043495036382047656e657261746f727301028201d8185884d8799fa6446e616d6549496d616765204e46544b6465736372697074696f6e514173736574204465736372697074696f6e45696d6167654a697066733a2f2f2e2e2e496d656469615479706549696d6167652f706e6744726f6c654475736572435f706b581c8e3b54aca70c6778daca5f9ec4e99a7817052e64f23979080f97beca01ff82583900f7a232fecd012dc3c556587593c5a5cbed826a49ce06c19ece9f350cb74a4ce22b780d2c9139c32806dd402df8947de0303ed3f976136a231a000f4240825839008e3b54aca70c6778daca5f9ec4e99a7817052e64f23979080f97beca718188b968725132a2cf6bb0925bad1f4b30e000990e526b2e9443611a24ad8844021a0003bf5c0b58201cf1170ee1369bd082ae8d39b0d99db7caacc10a79b41cde1122bd0f0c7e5bf00dd90102818258206a3f0a51813098b342c8fa71b5766922ca2cef531cdcf925da83c990c82dcb75050ed9010281581c8e3b54aca70c6778daca5f9ec4e99a7817052e64f23979080f97beca12d90102818258208a4ad7d31a93bc9c25430056bc7910b4cfe706bcd099e59e2332e041ade7132c00a600818258205bb5b41eae680aca501bf92d2830fef5dbe9ef7d74d605c018899031a7f74463000181a200583900eb21979c03eed7207020b2a0b47565b5aafb1a2c3849b59a1fa8e6c5e075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d6473903011b000000024f96ecf5021a0002fea1075820b1553a57d1cbb2ac233e98a4700d1dfd7972210cfd4e248ab4ade30afefd00d30e81581ce075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d64739030f0084a30081825820ef603099b2579d5ca273dce1f1257c11f054664ba972fd61c008b58a1b2325c158405faba20a8a371470fac259901656b63bb81201e1ef6768809b581ff0bab12cc6f236e6a52923e0ce4772f8e6d4c88db03da5db3d8b8a1770b7b0eb5e4a7e9b0f0681590bd7590bd401000033232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323233027222232325335333006533357346074607e0022646605a60586ae84004dd69aba135744002607c00206e64a666ae68c0ecc1000044c8c8c848cc00400c008c0b8d5d09aba2002375a6ae84004c0fc0040e0dd50022999ab9a303a303f002132321233001003002357426ae88c0fc00cd5d0981f00101b8890008a99ab9c490103505435001637540086ea8004cc09c8888c8d4014888d40188c88c94cd54cd4ccd54c0d40e0c0a80a4c0ac01ccc0b08004d40048888888888880100e44cd5ce24812d546865207472616e73616374696f6e206973206e6f74207369676e6564206279206f7261636c65206f776e657200030132533553355335333553036039302b02a253335302135001202103e215333573466ebcc118cd5d0182319aba0375000a6ec4080cd5d01ba835500b03937620400022a66a666036446a0044466603e446a00444a666ae68c12c004400c4cc11848ccc00401c00c00800c00c0040c0cd54c0e80ec8d400488cc0e0008cd54c0f40f88d400488cc0ec008ccd4004dc0a4000e048cdc0800a400000266aa607407646a0024466070004666a002466aa607c07e46a0024466078004604600200244666038066004002466aa607c07e46a0024466078004604000200266602e05c666050400204e6a004404e0060784426a004444a66a0082a666ae68cdd78018238a999ab9a3375e00408e2a666ae68cdc4000a400006e2666ae68cdc4800a410125e80206a07006e06e44072064407e6044014074266ae712401024c310003103a1335738921156f757470757420646f6573206e6f74206d6174636800031153355333573466e2400d20001335738921165072696365206d75737420626520706f7369746976650003103a15335533535353301275a6a00407e06e44a666a00442a666ae68cdc419b8000100935500c03a0331333573466e24004d540300e80c40d00c80c84d4cc049d69a80101f911a801112999a99980980a980f19b8100900b0021533500113330120150030081333012015003008034133301201500300803003a13357389212345787069726174696f6e2074696d65206973206e6f742070726f7065726c792073657400031030030333025200102435302635533530270092100116036202402f302d350080393302f3016337000020060542a666a602e6aa66a60320022602602a442a66a0022004442602e032402e02842a66a6666666ae900048c94ccd5cd181d98200008991999aab9f001202d23233335573e002405e46666aae7cd5d1001119299a9999999aba400123253335734608660900022646666aae7c00480d48cccd55cf9aba20022533530203574200642605e00206a406c07a078608e0020646ea800880c880c880c880c80e4854cd4c070d5d08029098159981a8010008188181aba1005203003703635744004068607e0020546ea800880a880a880a880a80c48400405480548c94ccd5cd181b181d800899191919091998008028018011bad357426ae88008dd69aba10013574460760046ae84c0e80040ccdd50009111a801111a801912999a9998040038020010a99a8018800819814819911192999a80190a999a80190a999a80290980200b0980180a8a999a80210980200b0980180a80c0060a999a80210980180a8980100a0a999a80190980180a8980100a00b8a999a801100700b0068a999a80110a999a80210980180a8980100a0a999a80190980180a8980100a00b8058a999a80190980100a098008098a999a80110980100a0980080980b12999a80110a999a80210a999a802109998040038010008b0b0b0060a999a80190a999a801909998038030010008b0b0b00580691a80091111111003891999999980091199ab9a3370e00400204004644a666ae68cdc38010008098a999ab9a3371200400201401044666ae68cdc400100081001191199ab9a3371200400204004644666ae68cdc480100081181011199ab9a3371000400204604044a666ae68cdc480100088008801112999ab9a33712004002200420024464a666ae68c0c8c0dc0044c8c8cc0994ccd5cd181a181c801099813198038029aba130380023006357426ae88c0e00080c54ccd5cd181a181c800899813198038029aba130380013006357426ae88c0e00040c4dd51aba135744606e0046ea8d5d0981b0008179baa0012325333573460620020522a666ae68c0c000407c0b4c0d0dd50009119192999ab9a303300100815333573460640022601460086ae84c0d400854ccd5cd1818800803017181a8009baa00122233355302302702a335530260272350012233024002300b001333553023027223500222533533355302802b301d01c235001223300a002005006100313302e00400301c001335530260272350012233024002330382253350011300a003221350022253353300c002008112223300200a0041300600300400211009212223001004112220012230302253350011003221330060023004001212223003004233333335748002403040304030460206eb4008806007c94cd5ce2481286578706563746564206f6e6c7920612073696e676c6520636f6e74696e75696e67206f7574707574001615335738920117496e76616c696420646174756d20696e206f757470757400164988880088c8c94ccd5cd1813000899091118010021aba13028002153335734604a002264244460020086ae84c0a000854ccd5cd181200080201098140009baa00111003253353007001213335530150192235300535300935003019222200422353007350042222004225333573466ebc01000854ccd5cd19baf0030011330220060051005100500e3300d00735300f3500201b22222222222200a15335738921024c660016232533357346040604a0022646424660020060046ae84d5d118128011aba1302400101d3754002444006660024002eb4888cc09088cccd55cf80090071191980e9980a180398138009803181300098021aba2003357420040306eac0048c94ccd5cd180e181080089919191919190919998008038028018011aba1357440046ae84004d5d10011aba10013574460420046ae84c080004064dd5000919191999aa999ab9a3370e90030008990911118020029aba13020002153335734603c00226424444600400a6ae84c08000854ccd5cd180e8008990911118008029aba13020002153335734603800226424444600600a6ae84c0800080648034803480348ccd54c048054cc03c894cd40088400c400403494ccd5cd19baf002350010181300600100d3300923253335734603e60480022646424660020060046ae84d5d118120011aba1302300101c37540026a60166a00802e44444444444401860400026ea8d40040408488c00800c48cc004894cd400804c40040208cc02488ccd400c04c008004d400403488ccd5cd19baf002001004007223301c2233335573e002400c466028600a6ae84008c00cd5d10010081bac001100d23253335734602860320022646464646464646464646464646464646464646464642466666666666600202e02a02602201e01a01601200e00a0060046ae84d5d10011aba1001357440046ae84004d5d10011aba1001357440046ae84004d5d10011aba1001357440046ae84004d5d10011aba1001357440046ae84004d5d10011aba1001357440046ae84004d5d1180c8011aba1301800101137540022002200c464a666ae68c044c0580044dd69aba1301500100e37540024424660020060044446006600400260244422444a66a00220044426600a004666aa600e01600a00800260224422444a66a00226a00600c442666a00a0186008004666aa600e01400a00800244002601e442244a66a00200c44266014600800466aa600c00e00800224002220024400444244660020080062a66ae712411f496e76616c696420646174756d20746f20636865636b4f776e4f757470757400161533573892010350543100162222222222220054c1014000370e90001b8748008dc3a40086e9520005573caae748c8c00400488cc00cc00800800530012cd8799f581cc0359ebb7d0688d79064bd118c99c8b87b5853e3af59245bb97e84d21a000dbba01a01499700ff00010581840001d8799f1b00000193858940a8d8799f1a0bf5b443ffff821a001862581a18087eb7a200d9010281825820c58b6e67ad72d3e4de04c0c143b15e0e60573ea9b740cf0a8fbfafb0475d50fd5840e8a05b2cc57a1952d1c3179b2b5019c1511fd49590dd2967b1fc5b3469251592698aca04e5b7a190b94f1d4a2545dce999aaa0921b50705e022bf02eb4b8720a05a182000082d87980821a000810e71a0b99de0ea20081825820f78c0ef2db02673d535e54b3e534281bce897181a3725593eb73f9d13cc7344a58407ca5fef9ff8a97006259f3308ea0320b2f361aaf38992c2316caea9bd62a8de30c99b9be9316fa36aca04a58eeaedc5f39335d714c05e09f3e282071466e4c0405a182000082d87980821a000619741a0893bcdfa100828258208469288efa6f9cb49040b43dcff93f40969d72433f751afde50235012c0160205840bf6cb0ebd00118d87bd8ea9a0e70f9f7954a3450dbbb2ba76a1558b1f7664d8c3dff9da85419f1f6a50e1a041d86de98ebcb511b22d885e0e092ffa7dc78910182582076af5530fa318a370820270031d1838545a4ceed8696510627563d1114d4182b58401f42ca3f529ee9765330674a56c3d70a885a8c62912a18145f51afaafb2da78b6319c70aa432f5f8d976a4d9dc309b1f17bcf89af1d08e939aef43f7bbea4004a103a11901fda40050ca7a1457ef9f4c7f9c747f8c4a4cfa6c0150d127265b6c2024bf7e254571bce41fa70b8758401b3f020064404f2b8fc22b3919605fb86cb9d3c7573ce58ffaedb716517ba0020ab9162510786a81643723fe5297dac6f96dd080032c60107110fa2fe7ddb570584018140643422a31e1cde248145cc8c55f5c53022b8e46c14d10282a880206d080033cc1bd382b1d51525a62210b69716949294b6305852895928c82f7b73cc70c58402f7d155c03e2abf9fd94d6e23b41f5ce57f595c8faff16fe899abe2bfe78d839d09b90a87fcd96f55ffbcfc99674fc5a392896703ceb8ffacb6b193beb1bd9a858405c94f4e2de6d001ac5813a71a03a962c1408a0581c2800093e112d939167909cae2d6af8fd08809295d4a04b360a737114e2c8d4c1d74f514cccc1d939502c6858409e6ac7cd4724420283e483c583821ca4c214c2dc15c4bd9d6582fd03fda5dc4202c39da5e5e50283dd2503bc03bc645d027d3d039c5c5ca543dc021ffd8d347b58408b716dad363d2b3776d27fc3acb2a63d1f99d49dfffebca526c518ba311787c30c97732f77b04e2844c7c3c5aa8bfdcd526d3e822da4d57aba35b15c1445cf0a579cc523cdd99bb7a177061a2110288883c0d971cebfea00186358400cd4f3828590fb603b04ce534efbce76320f0828b3773a3d5b80f0f8da8a3b134b33ab6e621c9a4e749f6fd61fede6c1ee6210c2264c176cdd8271e39a116d0680 \ No newline at end of file From 6356c44a09159aeed251c0dfb2562c3e916ee39f Mon Sep 17 00:00:00 2001 From: bkioshn Date: Mon, 2 Dec 2024 12:52:08 +0700 Subject: [PATCH 39/45] chore: fix comment Signed-off-by: bkioshn --- .../rbac-registration/src/cardano/cip509/validation.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/validation.rs b/rust/rbac-registration/src/cardano/cip509/validation.rs index 0b5cd18196b..1be2dee1f1b 100644 --- a/rust/rbac-registration/src/cardano/cip509/validation.rs +++ b/rust/rbac-registration/src/cardano/cip509/validation.rs @@ -515,7 +515,7 @@ mod tests { .expect("Failed to decode MultiEraBlock"); let transactions = multi_era_block.txs(); - // Second transaction of this test data contains the CIP509 auxiliary data + // Forth transaction of this test data contains the CIP509 auxiliary data let tx = transactions .get(3) .expect("Failed to get transaction index"); @@ -533,7 +533,7 @@ mod tests { .expect("Failed to decode MultiEraBlock"); let transactions = multi_era_block.txs(); - // Second transaction of this test data contains the CIP509 auxiliary data + // Forth transaction of this test data contains the CIP509 auxiliary data let tx = transactions .get(3) .expect("Failed to get transaction index"); @@ -550,7 +550,7 @@ mod tests { .expect("Failed to decode MultiEraBlock"); let transactions = multi_era_block.txs(); - // Second transaction of this test data contains the CIP509 auxiliary data + // Forth transaction of this test data contains the CIP509 auxiliary data let tx = transactions .get(3) .expect("Failed to get transaction index"); @@ -570,7 +570,7 @@ mod tests { .expect("Failed to decode MultiEraBlock"); let transactions = multi_era_block.txs(); - // Second transaction of this test data contains the CIP509 auxiliary data + // Forth transaction of this test data contains the CIP509 auxiliary data let tx = transactions .get(3) .expect("Failed to get transaction index"); @@ -597,7 +597,7 @@ mod tests { .expect("Failed to decode MultiEraBlock"); let transactions = multi_era_block.txs(); - // Second transaction of this test data contains the CIP509 auxiliary data + // Forth transaction of this test data contains the CIP509 auxiliary data let tx = transactions .get(3) .expect("Failed to get transaction index"); From efb04e4d05d39749086d318000eb96c88aa6583d Mon Sep 17 00:00:00 2001 From: bkioshn Date: Mon, 2 Dec 2024 12:59:46 +0700 Subject: [PATCH 40/45] fix: remove tx index from cip-509 validate function Signed-off-by: bkioshn --- rust/rbac-registration/src/cardano/cip509/mod.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/mod.rs b/rust/rbac-registration/src/cardano/cip509/mod.rs index 00649fb9450..35c5e320069 100644 --- a/rust/rbac-registration/src/cardano/cip509/mod.rs +++ b/rust/rbac-registration/src/cardano/cip509/mod.rs @@ -188,10 +188,9 @@ impl Cip509 { /// /// # Parameters /// * `txn` - Transaction data was attached to and to be validated/decoded against. - /// * `txn_idx` - Transaction Index /// * `validation_report` - Validation report to store the validation result. pub fn validate( - &self, txn: &MultiEraTx, txn_idx: usize, validation_report: &mut Vec, + &self, txn: &MultiEraTx, validation_report: &mut Vec, ) -> bool { let tx_input_validate = validate_txn_inputs_hash(self, txn, validation_report).unwrap_or(false); @@ -207,7 +206,7 @@ impl Cip509 { stake_key_validate = validate_stake_public_key(self, txn, validation_report).unwrap_or(false); payment_key_validate = - validate_payment_key(txn, txn_idx, role, validation_report) + validate_payment_key(txn, role, validation_report) .unwrap_or(false); signing_key = validate_role_singing_key(role, validation_report); } From b97a0954af6d01faeacf188509c28ff692e1b4c9 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Mon, 2 Dec 2024 13:48:55 +0700 Subject: [PATCH 41/45] fix: test data Signed-off-by: bkioshn --- rust/rbac-registration/src/cardano/transaction/witness.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/rbac-registration/src/cardano/transaction/witness.rs b/rust/rbac-registration/src/cardano/transaction/witness.rs index 3355538e90a..736f6701356 100644 --- a/rust/rbac-registration/src/cardano/transaction/witness.rs +++ b/rust/rbac-registration/src/cardano/transaction/witness.rs @@ -113,7 +113,7 @@ mod tests { let txs_conway = conway_block.txs(); let tx_witness_conway = TxWitness::new(&txs_conway).expect("Failed to create TxWitness"); let vkey1_hash: [u8; 28] = - hex::decode("bd95d582888acda57a20256bb03e4c4abb6bdf09a47d788605412c53") + hex::decode("c0359ebb7d0688d79064bd118c99c8b87b5853e3af59245bb97e84d2") .expect("Failed to decode vkey1_hash") .try_into() .expect("Invalid length of vkey1_hash"); From d0bf56386a8cadf57ef0ff02b949dd1a2ca9f5ae Mon Sep 17 00:00:00 2001 From: bkioshn Date: Mon, 2 Dec 2024 13:55:05 +0700 Subject: [PATCH 42/45] chore: fix format --- rust/rbac-registration/src/cardano/cip509/mod.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/mod.rs b/rust/rbac-registration/src/cardano/cip509/mod.rs index 35c5e320069..e88528f464b 100644 --- a/rust/rbac-registration/src/cardano/cip509/mod.rs +++ b/rust/rbac-registration/src/cardano/cip509/mod.rs @@ -189,9 +189,7 @@ impl Cip509 { /// # Parameters /// * `txn` - Transaction data was attached to and to be validated/decoded against. /// * `validation_report` - Validation report to store the validation result. - pub fn validate( - &self, txn: &MultiEraTx, validation_report: &mut Vec, - ) -> bool { + pub fn validate(&self, txn: &MultiEraTx, validation_report: &mut Vec) -> bool { let tx_input_validate = validate_txn_inputs_hash(self, txn, validation_report).unwrap_or(false); let aux_validate = validate_aux(txn, validation_report).unwrap_or(false); @@ -206,8 +204,7 @@ impl Cip509 { stake_key_validate = validate_stake_public_key(self, txn, validation_report).unwrap_or(false); payment_key_validate = - validate_payment_key(txn, role, validation_report) - .unwrap_or(false); + validate_payment_key(txn, role, validation_report).unwrap_or(false); signing_key = validate_role_singing_key(role, validation_report); } } From 18ea3aaf64a4ad30fc1a8815857a56944eff969a Mon Sep 17 00:00:00 2001 From: bkioshn Date: Mon, 2 Dec 2024 14:16:00 +0700 Subject: [PATCH 43/45] fix: remove txn index from validate Signed-off-by: bkioshn --- .../src/registration/cardano/mod.rs | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs index 318772903a2..ed60ec3a856 100644 --- a/rust/rbac-registration/src/registration/cardano/mod.rs +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -189,7 +189,7 @@ impl RegistrationChainInner { let mut validation_report = Vec::new(); // Do the CIP509 validation, ensuring the basic validation pass. - if !cip509.validate(txn, tx_idx, &mut validation_report) { + if !cip509.validate(txn, &mut validation_report) { // Log out the error if any error!("CIP509 validation failed: {:?}", validation_report); bail!("CIP509 validation failed, {:?}", validation_report); @@ -245,7 +245,7 @@ impl RegistrationChainInner { let mut validation_report = Vec::new(); // Do the CIP509 validation, ensuring the basic validation pass. - if !cip509.validate(txn, tx_idx, &mut validation_report) { + if !cip509.validate(txn, &mut validation_report) { error!("CIP509 validation failed: {:?}", validation_report); bail!("CIP509 validation failed, {:?}", validation_report); } @@ -478,22 +478,18 @@ fn update_role_data( // If there is new role singing key, use it, else use the old one let signing_key = match role_data.role_signing_key { Some(key) => Some(key), - None => { - match inner.role_data.get(&role_data.role_number) { - Some((_, role_data)) => role_data.signing_key_ref().clone(), - None => None, - } + None => match inner.role_data.get(&role_data.role_number) { + Some((_, role_data)) => role_data.signing_key_ref().clone(), + None => None, }, }; // If there is new role encryption key, use it, else use the old one let encryption_key = match role_data.role_encryption_key { Some(key) => Some(key), - None => { - match inner.role_data.get(&role_data.role_number) { - Some((_, role_data)) => role_data.encryption_ref().clone(), - None => None, - } + None => match inner.role_data.get(&role_data.role_number) { + Some((_, role_data)) => role_data.encryption_ref().clone(), + None => None, }, }; let payment_key = get_payment_key_from_tx(txn, role_data.payment_key)?; From 3479ed4364f222182e7c00b028e0233bd33ccc4b Mon Sep 17 00:00:00 2001 From: bkioshn Date: Mon, 2 Dec 2024 14:29:21 +0700 Subject: [PATCH 44/45] fix: format --- .../src/registration/cardano/mod.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs index ed60ec3a856..160d4dfa238 100644 --- a/rust/rbac-registration/src/registration/cardano/mod.rs +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -478,18 +478,22 @@ fn update_role_data( // If there is new role singing key, use it, else use the old one let signing_key = match role_data.role_signing_key { Some(key) => Some(key), - None => match inner.role_data.get(&role_data.role_number) { - Some((_, role_data)) => role_data.signing_key_ref().clone(), - None => None, + None => { + match inner.role_data.get(&role_data.role_number) { + Some((_, role_data)) => role_data.signing_key_ref().clone(), + None => None, + } }, }; // If there is new role encryption key, use it, else use the old one let encryption_key = match role_data.role_encryption_key { Some(key) => Some(key), - None => match inner.role_data.get(&role_data.role_number) { - Some((_, role_data)) => role_data.encryption_ref().clone(), - None => None, + None => { + match inner.role_data.get(&role_data.role_number) { + Some((_, role_data)) => role_data.encryption_ref().clone(), + None => None, + } }, }; let payment_key = get_payment_key_from_tx(txn, role_data.payment_key)?; From d60fb501f135e39a00d7f282305d1ebc47c691e6 Mon Sep 17 00:00:00 2001 From: bkioshn <35752733+bkioshn@users.noreply.github.com> Date: Mon, 2 Dec 2024 22:36:39 +0700 Subject: [PATCH 45/45] fix(rust/rbac-registration): Tracking payment key type (#93) * fix: payment key shelley address and add test for registration chain Signed-off-by: bkioshn * chore: comment * fix: format * fix: use ShelleyPaymentPart Signed-off-by: bkioshn * fix: track ShelleyAddress Signed-off-by: bkioshn * fix: remove tracking_payment_key and use tracking_payment_history Signed-off-by: bkioshn --------- Signed-off-by: bkioshn Co-authored-by: Steven Johnson --- .../src/registration/cardano/mod.rs | 202 +++++++++++++----- .../src/registration/cardano/role_data.rs | 10 +- .../src/test_data/cardano/conway_4.block | 1 + 3 files changed, 156 insertions(+), 57 deletions(-) create mode 100644 rust/rbac-registration/src/test_data/cardano/conway_4.block diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs index 160d4dfa238..3895d8971b0 100644 --- a/rust/rbac-registration/src/registration/cardano/mod.rs +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -9,7 +9,11 @@ use std::{collections::HashMap, sync::Arc}; use anyhow::bail; use c509_certificate::c509::C509; use pallas::{ - codec::utils::Bytes, crypto::hash::Hash, ledger::traverse::MultiEraTx, + crypto::hash::Hash, + ledger::{ + addresses::{Address, ShelleyAddress, ShelleyPaymentPart}, + traverse::MultiEraTx, + }, network::miniprotocols::Point, }; use payment_history::PaymentHistory; @@ -51,8 +55,8 @@ impl RegistrationChain { /// /// Returns an error if data is invalid pub fn new( - &self, point: Point, tracking_payment_keys: Vec, tx_idx: usize, - txn: &MultiEraTx, cip509: Cip509, + point: Point, tracking_payment_keys: &[ShelleyAddress], tx_idx: usize, txn: &MultiEraTx, + cip509: Cip509, ) -> anyhow::Result { let inner = RegistrationChainInner::new(cip509, tracking_payment_keys, point, tx_idx, txn)?; @@ -124,16 +128,10 @@ impl RegistrationChain { &self.inner.role_data } - /// Get the list of payment keys to track. + /// Get the map of tracked payment keys to its history. #[must_use] - pub fn tracking_payment_keys(&self) -> &Vec { - &self.inner.tracking_payment_keys - } - - /// Get the map of payment key to its history. - #[must_use] - pub fn payment_history(&self) -> &HashMap> { - &self.inner.payment_history + pub fn tracking_payment_history(&self) -> &HashMap> { + &self.inner.tracking_payment_history } } @@ -158,10 +156,8 @@ struct RegistrationChainInner { // Role /// Map of role number to point, transaction index, and role data. role_data: HashMap, - /// List of payment keys to track. - tracking_payment_keys: Arc>, - /// Map of payment key to its history. - payment_history: HashMap>, + /// Map of tracked payment key to its history. + tracking_payment_history: HashMap>, } impl RegistrationChainInner { @@ -179,7 +175,7 @@ impl RegistrationChainInner { /// /// Returns an error if data is invalid fn new( - cip509: Cip509, tracking_payment_keys: Vec, point: Point, tx_idx: usize, + cip509: Cip509, tracking_payment_keys: &[ShelleyAddress], point: Point, tx_idx: usize, txn: &MultiEraTx, ) -> anyhow::Result { // Should be chain root, return immediately if not @@ -207,12 +203,13 @@ impl RegistrationChainInner { let revocations = revocations_list(registration.revocation_list, &point_tx_idx); let role_data_map = chain_root_role_data(registration.role_set, txn, &point_tx_idx)?; - let mut payment_history = HashMap::new(); - for tracking_key in &tracking_payment_keys { - // Keep record of payment history, the payment key that we want to track - let histories = update_payment_history(tracking_key, txn, &point_tx_idx)?; - payment_history.insert(tracking_key.clone(), histories); + let mut tracking_payment_history = HashMap::new(); + // Create a payment history for each tracking payment key + for tracking_key in tracking_payment_keys { + tracking_payment_history.insert(tracking_key.clone(), Vec::new()); } + // Keep record of payment history, the payment key that we want to track + update_tracking_payment_history(&mut tracking_payment_history, txn, &point_tx_idx)?; Ok(Self { purpose, @@ -222,8 +219,7 @@ impl RegistrationChainInner { simple_keys: public_key_map, revocations, role_data: role_data_map, - tracking_payment_keys: Arc::new(tracking_payment_keys), - payment_history, + tracking_payment_history, }) } @@ -280,16 +276,11 @@ impl RegistrationChainInner { update_role_data(&mut new_inner, registration.role_set, txn, &point_tx_idx)?; - for tracking_key in self.tracking_payment_keys.iter() { - let histories = update_payment_history(tracking_key, txn, &point_tx_idx)?; - // If tracking payment key doesn't exist, insert an empty vector, - // then add the histories to the history vector - new_inner - .payment_history - .entry(tracking_key.clone()) - .or_default() - .extend(histories); - } + update_tracking_payment_history( + &mut new_inner.tracking_payment_history, + txn, + &point_tx_idx, + )?; Ok(new_inner) } @@ -448,7 +439,7 @@ fn chain_root_role_data( let encryption_key = role_data.role_encryption_key.clone(); // Get the payment key - let payment_key = get_payment_key_from_tx(txn, role_data.payment_key)?; + let payment_key = get_payment_addr_from_tx(txn, role_data.payment_key)?; // Map of role number to point and role data role_data_map.insert( @@ -496,7 +487,7 @@ fn update_role_data( } }, }; - let payment_key = get_payment_key_from_tx(txn, role_data.payment_key)?; + let payment_key = get_payment_addr_from_tx(txn, role_data.payment_key)?; // Map of role number to point and role data // Note that new role data will overwrite the old one @@ -517,10 +508,10 @@ fn update_role_data( Ok(()) } -/// Helper function for retrieving the payment key from the transaction. -fn get_payment_key_from_tx( +/// Helper function for retrieving the Shelley address from the transaction. +fn get_payment_addr_from_tx( txn: &MultiEraTx, payment_key_ref: Option, -) -> anyhow::Result { +) -> anyhow::Result> { // The index should exist since it pass the basic validation if let Some(key_ref) = payment_key_ref { if let MultiEraTx::Conway(tx) = txn { @@ -533,11 +524,13 @@ fn get_payment_key_from_tx( pallas::ledger::primitives::conway::PseudoTransactionOutput::PostAlonzo( o, ) => { - let payment_key: Ed25519PublicKey = - o.address.clone().try_into().map_err(|_| { - anyhow::anyhow!("Failed to convert Vec to Ed25519PublicKey in payment key reference") - })?; - return Ok(payment_key); + let address = + Address::from_bytes(&o.address).map_err(|e| anyhow::anyhow!(e))?; + + if let Address::Shelley(addr) = address { + return Ok(Some(addr.payment().clone())); + } + bail!("Unsupported address type in payment key reference"); }, // Not support legacy form of transaction output pallas::ledger::primitives::conway::PseudoTransactionOutput::Legacy(_) => { @@ -552,26 +545,34 @@ fn get_payment_key_from_tx( bail!("Unsupported payment key reference to transaction input"); } } - Ok(Ed25519PublicKey::default()) + Ok(None) } /// Update the payment history given the tracking payment keys. -fn update_payment_history( - tracking_key: &Ed25519PublicKey, txn: &MultiEraTx, point_tx_idx: &PointTxIdx, -) -> anyhow::Result> { - let mut payment_history = Vec::new(); +fn update_tracking_payment_history( + tracking_payment_history: &mut HashMap>, txn: &MultiEraTx, + point_tx_idx: &PointTxIdx, +) -> anyhow::Result<()> { if let MultiEraTx::Conway(tx) = txn { // Conway era -> Post alonzo tx output for (index, output) in tx.transaction_body.outputs.iter().enumerate() { match output { pallas::ledger::primitives::conway::PseudoTransactionOutput::PostAlonzo(o) => { - let address_bytes: Bytes = tracking_key.clone().into(); - if address_bytes == o.address { + let address = + Address::from_bytes(&o.address).map_err(|e| anyhow::anyhow!(e))?; + let shelley_payment = if let Address::Shelley(addr) = address { + addr.clone() + } else { + bail!("Unsupported address type in update payment history"); + }; + // If the payment key from the output exist in the payment history, add the + // history + if let Some(vec) = tracking_payment_history.get_mut(&shelley_payment) { let output_index: u16 = index.try_into().map_err(|_| { anyhow::anyhow!("Cannot convert usize to u16 in update payment history") })?; - payment_history.push(PaymentHistory::new( + vec.push(PaymentHistory::new( point_tx_idx.clone(), txn.hash(), output_index, @@ -585,5 +586,100 @@ fn update_payment_history( } } } - Ok(payment_history) + Ok(()) +} + +#[cfg(test)] +mod test { + use minicbor::{Decode, Decoder}; + use pallas::{ledger::traverse::MultiEraTx, network::miniprotocols::Point}; + + use super::RegistrationChain; + use crate::cardano::{cip509::Cip509, transaction::raw_aux_data::RawAuxData}; + + fn cip_509_aux_data(tx: &MultiEraTx<'_>) -> Vec { + let raw_auxiliary_data = tx + .as_conway() + .unwrap() + .clone() + .auxiliary_data + .map(|aux| aux.raw_cbor()); + + let raw_cbor_data = match raw_auxiliary_data { + pallas::codec::utils::Nullable::Some(data) => Ok(data), + _ => Err("Auxiliary data not found"), + }; + + let auxiliary_data = RawAuxData::new(raw_cbor_data.expect("Failed to get raw cbor data")); + auxiliary_data + .get_metadata(509) + .expect("Failed to get metadata") + .to_vec() + } + + fn conway_1() -> Vec { + hex::decode(include_str!("../../test_data/cardano/conway_1.block")) + .expect("Failed to decode hex block.") + } + + fn conway_4() -> Vec { + hex::decode(include_str!("../../test_data/cardano/conway_4.block")) + .expect("Failed to decode hex block.") + } + + #[test] + fn test_new_and_update_registration() { + let conway_block_data_1 = conway_1(); + let point_1 = Point::new( + 77_429_134, + hex::decode("62483f96613b4c48acd28de482eb735522ac180df61766bdb476a7bf83e7bb98") + .unwrap(), + ); + let multi_era_block_1 = + pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data_1) + .expect("Failed to decode MultiEraBlock"); + + let transactions_1 = multi_era_block_1.txs(); + // Forth transaction of this test data contains the CIP509 auxiliary data + let tx_1 = transactions_1 + .get(3) + .expect("Failed to get transaction index"); + + let aux_data_1 = cip_509_aux_data(tx_1); + let mut decoder = Decoder::new(aux_data_1.as_slice()); + let cip509_1 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); + let tracking_payment_keys = vec![]; + + let registration_chain = + RegistrationChain::new(point_1.clone(), &tracking_payment_keys, 3, tx_1, cip509_1); + // Able to add chain root to the registration chain + assert!(registration_chain.is_ok()); + + let conway_block_data_4 = conway_4(); + let point_4 = Point::new( + 77_436_369, + hex::decode("b174fc697126f05046b847d47e60d66cbedaf25240027f9c07f27150889aac24") + .unwrap(), + ); + + let multi_era_block_4 = + pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data_4) + .expect("Failed to decode MultiEraBlock"); + + let transactions_4 = multi_era_block_4.txs(); + // Second transaction of this test data contains the CIP509 auxiliary data + let tx = transactions_4 + .get(1) + .expect("Failed to get transaction index"); + + let aux_data_4 = cip_509_aux_data(tx); + let mut decoder = Decoder::new(aux_data_4.as_slice()); + let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509"); + + // Update the registration chain + assert!(registration_chain + .unwrap() + .update(point_4.clone(), 1, tx, cip509) + .is_ok()); + } } diff --git a/rust/rbac-registration/src/registration/cardano/role_data.rs b/rust/rbac-registration/src/registration/cardano/role_data.rs index 684671415e7..b6b4d947d7c 100644 --- a/rust/rbac-registration/src/registration/cardano/role_data.rs +++ b/rust/rbac-registration/src/registration/cardano/role_data.rs @@ -2,7 +2,9 @@ use std::collections::HashMap; -use crate::cardano::cip509::rbac::{pub_key::Ed25519PublicKey, role_data::KeyLocalRef}; +use pallas::ledger::addresses::ShelleyPaymentPart; + +use crate::cardano::cip509::rbac::role_data::KeyLocalRef; /// Role data #[derive(Clone)] @@ -12,7 +14,7 @@ pub struct RoleData { /// An encryption keys to the data within registration. encryption_ref: Option, /// A payment key where reward will be distributed to. - payment_key: Ed25519PublicKey, + payment_key: Option, /// Map of role extended data (10-99) to its data role_extended_data: HashMap>, } @@ -21,7 +23,7 @@ impl RoleData { /// Create an instance of role data. pub(crate) fn new( signing_key_ref: Option, encryption_ref: Option, - payment_key: Ed25519PublicKey, role_extended_data: HashMap>, + payment_key: Option, role_extended_data: HashMap>, ) -> Self { RoleData { signing_key_ref, @@ -45,7 +47,7 @@ impl RoleData { /// Get the payment key. #[must_use] - pub fn payment_key(&self) -> &Ed25519PublicKey { + pub fn payment_key(&self) -> &Option { &self.payment_key } diff --git a/rust/rbac-registration/src/test_data/cardano/conway_4.block b/rust/rbac-registration/src/test_data/cardano/conway_4.block new file mode 100644 index 00000000000..b16edd16df5 --- /dev/null +++ b/rust/rbac-registration/src/test_data/cardano/conway_4.block @@ -0,0 +1 @@ +820785828a1a002cf24d1a049d95d15820a82d38572884fc69c5b1105dd190651e4fc5c821878a1a166b6bca897983b83d58206f281ba212f118ac018aaeb805dda9f6fd54fe28cecafe7bec1347509510797f5820870dca606397b85d2e4ee1e46bdb9b784a092a1ec25cb8146cf4046456ff861c825840c085ebef69c078f6fcfd53a2eb2e209b6bd1a517390c8971f079fe55a4b30c82641385a618acf269bceb2d3c83a4ad6addc8901cc3bb359da7875c672ed5a6a55850758500c16a229759d672f0c5aea206fe924238915de34dfdf54413d438a27c4bdb453a080d1fe6ce0f3427452d20aeb1d1907d034899d19021e951a1badeef442de3f4f1bd4e9a4ce5924870f068a4021911c65820c8fc1a8e30b35038644284be081128c4a7ad8b3c39edbfeea871084b9b3566cb845820b7e792177f506de2a34d1c4099f7a1687ff7af54caa093d85c5755e709f757fd0d190241584095a171db3d0e3808bca5e84970ead6b0a736e1d5a2b5fc8c169c8fab7bc1a5d3d60b0b582fbbc8c23d43b3301c4b3eede3ac5438bddfebe1cc8e5a8d71a94802820a005901c05ae6633913fc08585a1ddf905f10b2cc1134d914f9f656b58e72cad542e53a90ce0cc9c6f0e819f7243431a5416cbc1a3fa67d8ab8c547a09f78c86c20073208dc3b328c9d70a24a912aa4e170b9d6fbe8e55c03b52dc296435c86a358bc3f20e89f4c92748dff2ed24a71b8377b4683851a52cfd023dccaa92239d9e989fe8d1d360529c15417937f60530a085ee82dd4ed5f1bc46d0bcf2ab68d4729063a7a5c5074cdb289655f23954f7eebfd418e3a133bb53cfa0bd5ff544fc9d0599640b46619d3bfc5d9782c98ee86fcaa1efe8503980fd9219db77c3003ed2cee13d85f510f9a3381d442f5314a91c52ccf01cc796b9fb7ea14d046a00c3f96e6ee2ec28896f72808952cda9aec2feb690ecb59c1a729816f602a58b06fbab79977a9e96ff7eed2dd262eb86cee8f35d4bad55be2919413ff5cd8fbab3acc25f608630fa7796debcdae0239aafb45e949053af2523bb112b5f6be5fc4ece0ac55f59783466565dad12d42b76fb3ad45112c25d0f4c0880b88ad0ff87f207538c5c7cb96690633a72de47fd36980846cfba4865e324b7ccf1ca80382a4eedc72caf1a0d828049fbd7b3906877b89980bd2f0ca5ffc229e667d56fd11c1dd6e3c0ba7ed82ab0082825820276df9bb9d245ea87e6def1a995ad919edacc907c6cbf028e0e552b1c53c10df0182582062e4dfcb06b8401ea1c405d6290c6aede934c7a6cd31ae000c2a5dd34961af5b000182a300581d70bdc22da682cd9aceed5fd48914789fc98c94abc79fed8b40cb8c431401821a001e8480a1581cc13ddf298a5d25aff2933695987912b4f1748bdf0df8e4b5d85f2360a14e50524550524f445f4f5241434c4501028201d81856d8799fd8799f1a0bf85ea8ff1b000001938742fe98ff82581d60c0359ebb7d0688d79064bd118c99c8b87b5853e3af59245bb97e84d21b000000011587887e021a0006a164031a049d9933081a049d922c0b5820e33c25b2f65204adbf1a8f29ba4c7157c35f9479346e2d6717e23d6786e8b6340d81825820276df9bb9d245ea87e6def1a995ad919edacc907c6cbf028e0e552b1c53c10df010e81581cc0359ebb7d0688d79064bd118c99c8b87b5853e3af59245bb97e84d20f001082581d60c0359ebb7d0688d79064bd118c99c8b87b5853e3af59245bb97e84d21b000000011541dea2111a004c4b40a600818258206695b9cac9230af5c8ee50747b1ca3c78a854d181c7e5c6c371de01b80274d31000181a200583900eb21979c03eed7207020b2a0b47565b5aafb1a2c3849b59a1fa8e6c5e075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d6473903011b000000024f93e7f8021a000304fd07582091e3a1050ba7f37e9d94938ec94a9d187f24b363374c109e3fcc44fedb5de8b70e81581ce075be10ec5c575caffb68b08c31470666d4fe1aeea07c16d64739030f0082a30081825820ef603099b2579d5ca273dce1f1257c11f054664ba972fd61c008b58a1b2325c1584049e2d6c6a948a7d1398f200213a65cd15ac2dd491e903892aaca6ad487717f3cad6c66e201dd8fd74f69898f77aa3de953e83df61f2822c01a30eb992f58f0060681590bd7590bd401000033232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323233027222232325335333006533357346074607e0022646605a60586ae84004dd69aba135744002607c00206e64a666ae68c0ecc1000044c8c8c848cc00400c008c0b8d5d09aba2002375a6ae84004c0fc0040e0dd50022999ab9a303a303f002132321233001003002357426ae88c0fc00cd5d0981f00101b8890008a99ab9c490103505435001637540086ea8004cc09c8888c8d4014888d40188c88c94cd54cd4ccd54c0d40e0c0a80a4c0ac01ccc0b08004d40048888888888880100e44cd5ce24812d546865207472616e73616374696f6e206973206e6f74207369676e6564206279206f7261636c65206f776e657200030132533553355335333553036039302b02a253335302135001202103e215333573466ebcc118cd5d0182319aba0375000a6ec4080cd5d01ba835500b03937620400022a66a666036446a0044466603e446a00444a666ae68c12c004400c4cc11848ccc00401c00c00800c00c0040c0cd54c0e80ec8d400488cc0e0008cd54c0f40f88d400488cc0ec008ccd4004dc0a4000e048cdc0800a400000266aa607407646a0024466070004666a002466aa607c07e46a0024466078004604600200244666038066004002466aa607c07e46a0024466078004604000200266602e05c666050400204e6a004404e0060784426a004444a66a0082a666ae68cdd78018238a999ab9a3375e00408e2a666ae68cdc4000a400006e2666ae68cdc4800a410125e80206a07006e06e44072064407e6044014074266ae712401024c310003103a1335738921156f757470757420646f6573206e6f74206d6174636800031153355333573466e2400d20001335738921165072696365206d75737420626520706f7369746976650003103a15335533535353301275a6a00407e06e44a666a00442a666ae68cdc419b8000100935500c03a0331333573466e24004d540300e80c40d00c80c84d4cc049d69a80101f911a801112999a99980980a980f19b8100900b0021533500113330120150030081333012015003008034133301201500300803003a13357389212345787069726174696f6e2074696d65206973206e6f742070726f7065726c792073657400031030030333025200102435302635533530270092100116036202402f302d350080393302f3016337000020060542a666a602e6aa66a60320022602602a442a66a0022004442602e032402e02842a66a6666666ae900048c94ccd5cd181d98200008991999aab9f001202d23233335573e002405e46666aae7cd5d1001119299a9999999aba400123253335734608660900022646666aae7c00480d48cccd55cf9aba20022533530203574200642605e00206a406c07a078608e0020646ea800880c880c880c880c80e4854cd4c070d5d08029098159981a8010008188181aba1005203003703635744004068607e0020546ea800880a880a880a880a80c48400405480548c94ccd5cd181b181d800899191919091998008028018011bad357426ae88008dd69aba10013574460760046ae84c0e80040ccdd50009111a801111a801912999a9998040038020010a99a8018800819814819911192999a80190a999a80190a999a80290980200b0980180a8a999a80210980200b0980180a80c0060a999a80210980180a8980100a0a999a80190980180a8980100a00b8a999a801100700b0068a999a80110a999a80210980180a8980100a0a999a80190980180a8980100a00b8058a999a80190980100a098008098a999a80110980100a0980080980b12999a80110a999a80210a999a802109998040038010008b0b0b0060a999a80190a999a801909998038030010008b0b0b00580691a80091111111003891999999980091199ab9a3370e00400204004644a666ae68cdc38010008098a999ab9a3371200400201401044666ae68cdc400100081001191199ab9a3371200400204004644666ae68cdc480100081181011199ab9a3371000400204604044a666ae68cdc480100088008801112999ab9a33712004002200420024464a666ae68c0c8c0dc0044c8c8cc0994ccd5cd181a181c801099813198038029aba130380023006357426ae88c0e00080c54ccd5cd181a181c800899813198038029aba130380013006357426ae88c0e00040c4dd51aba135744606e0046ea8d5d0981b0008179baa0012325333573460620020522a666ae68c0c000407c0b4c0d0dd50009119192999ab9a303300100815333573460640022601460086ae84c0d400854ccd5cd1818800803017181a8009baa00122233355302302702a335530260272350012233024002300b001333553023027223500222533533355302802b301d01c235001223300a002005006100313302e00400301c001335530260272350012233024002330382253350011300a003221350022253353300c002008112223300200a0041300600300400211009212223001004112220012230302253350011003221330060023004001212223003004233333335748002403040304030460206eb4008806007c94cd5ce2481286578706563746564206f6e6c7920612073696e676c6520636f6e74696e75696e67206f7574707574001615335738920117496e76616c696420646174756d20696e206f757470757400164988880088c8c94ccd5cd1813000899091118010021aba13028002153335734604a002264244460020086ae84c0a000854ccd5cd181200080201098140009baa00111003253353007001213335530150192235300535300935003019222200422353007350042222004225333573466ebc01000854ccd5cd19baf0030011330220060051005100500e3300d00735300f3500201b22222222222200a15335738921024c660016232533357346040604a0022646424660020060046ae84d5d118128011aba1302400101d3754002444006660024002eb4888cc09088cccd55cf80090071191980e9980a180398138009803181300098021aba2003357420040306eac0048c94ccd5cd180e181080089919191919190919998008038028018011aba1357440046ae84004d5d10011aba10013574460420046ae84c080004064dd5000919191999aa999ab9a3370e90030008990911118020029aba13020002153335734603c00226424444600400a6ae84c08000854ccd5cd180e8008990911118008029aba13020002153335734603800226424444600600a6ae84c0800080648034803480348ccd54c048054cc03c894cd40088400c400403494ccd5cd19baf002350010181300600100d3300923253335734603e60480022646424660020060046ae84d5d118120011aba1302300101c37540026a60166a00802e44444444444401860400026ea8d40040408488c00800c48cc004894cd400804c40040208cc02488ccd400c04c008004d400403488ccd5cd19baf002001004007223301c2233335573e002400c466028600a6ae84008c00cd5d10010081bac001100d23253335734602860320022646464646464646464646464646464646464646464642466666666666600202e02a02602201e01a01601200e00a0060046ae84d5d10011aba1001357440046ae84004d5d10011aba1001357440046ae84004d5d10011aba1001357440046ae84004d5d10011aba1001357440046ae84004d5d10011aba1001357440046ae84004d5d1180c8011aba1301800101137540022002200c464a666ae68c044c0580044dd69aba1301500100e37540024424660020060044446006600400260244422444a66a00220044426600a004666aa600e01600a00800260224422444a66a00226a00600c442666a00a0186008004666aa600e01400a00800244002601e442244a66a00200c44266014600800466aa600c00e00800224002220024400444244660020080062a66ae712411f496e76616c696420646174756d20746f20636865636b4f776e4f757470757400161533573892010350543100162222222222220054c1014000370e90001b8748008dc3a40086e9520005573caae748c8c00400488cc00cc00800800530012cd8799f581cc0359ebb7d0688d79064bd118c99c8b87b5853e3af59245bb97e84d21a000dbba01a01499700ff00010581840001d8799f1b0000019385f96798d8799f1a0bf85ea8ffff821a001862581a18087eb7a100828258208469288efa6f9cb49040b43dcff93f40969d72433f751afde50235012c01602058404ac843657462bd1b0f2db7e1abfa2fa3ccd3105ad2278649e155ceb3cf4e44b3cc2532815570cee26a55910a04db4de50cd3c07784ecfacf4c74706f54cd470382582076af5530fa318a370820270031d1838545a4ceed8696510627563d1114d4182b5840c9fe85cdcef2b32d76958d564be1b7bc0124a99594cc9e69e1a4d0cbe8f96eed4ca94fe6f2471382d19d74f610a6a5e01e56d8f6dc2a5519a090ec2e00dc3e05a101a11901fda50050ca7a1457ef9f4c7f9c747f8c4a4cfa6c01505be7b97fa11335b727bbf095a3f0fb7c0258206695b9cac9230af5c8ee50747b1ca3c78a854d181c7e5c6c371de01b80274d310b8758401b3e020864c84e53c9f7a9c54b8d09c6047bc2049aed7438c5575ed4ee265182dd808a52ae8b12083cb540fa9ccc7e0ec3a7b52ad5f134682286f658ff0083f558400dc73df538d858d8d8b89e5247f2000f1f47c43b0c68204480001fc71a179f0685400804400c2484e3013b03195a0a79c94206484bc93b10cbcb474999a5e5a55840b17f9619347e158e3a2a44e3e33eb72d648ae645a0e63d83fde739ee0d7ff6baeaf5767357770a697c2a92095bfe292e427bbc2fee56211ec78389dfa56d13c758400363abd573e840dead26d80c600380b5bc64a3c285550096634883e3a7265b4f18ca836d1d23b991a02a45ac365c9665acb787484da6ee21a10a1212ee5e5e11584012912b55d77a3e82444744ca4581c84877c958f9583f7910e4251d15161126e91b1d11e72525271b11e5870e0f0a0f94f18e080908f7f4f6918af68de8fc1bdd5840ec35fc967e64936c930f279eb8d043d31112e6a71aaaa76fc3455e9d30b90ea671971e5c9ecd4be5b4d87ab3e11f7de3b8984ea353e9a2d8d65c6ad6f9102c46581876789c8667c739c00da3176c46a1b030c4281c4e62afcf3a186358401824797b7e12054dabb6fef573ceba9c3bbed87b522a4141c2357d05d63ffabee59add33aac71f4573e43520a1c000d0c4e7fa2adf7f8e92718320527d23e60580 \ No newline at end of file