From 33700c921552b7620997cd4a704f736293dc073a Mon Sep 17 00:00:00 2001 From: bkioshn Date: Thu, 2 Jan 2025 18:25:38 +0700 Subject: [PATCH 01/17] fix(cardano-blockchain-types): key registration Signed-off-by: bkioshn --- .../src/cip36/key_registration.rs | 379 +++++++++++------- 1 file changed, 245 insertions(+), 134 deletions(-) diff --git a/rust/cardano-blockchain-types/src/cip36/key_registration.rs b/rust/cardano-blockchain-types/src/cip36/key_registration.rs index 54ebc12a2ae..5d4bed73f8a 100644 --- a/rust/cardano-blockchain-types/src/cip36/key_registration.rs +++ b/rust/cardano-blockchain-types/src/cip36/key_registration.rs @@ -5,8 +5,6 @@ //! //! -use std::collections::HashSet; - use anyhow::Context; use ed25519_dalek::VerifyingKey; use minicbor::{decode, Decode, Decoder}; @@ -77,76 +75,109 @@ impl Decode<'_, ()> for Cip36KeyRegistration { let mut cip36_key_registration = Cip36KeyRegistration::default(); // Record of founded keys. Check for duplicate keys in the map - let mut found_keys: HashSet = HashSet::new(); + let mut found_keys: Vec = Vec::new(); + + // Record of errors found during decoding + let mut err_report = Vec::new(); - for _ in 0..map_len { + for index in 0..map_len { let key: u16 = decode_helper(d, "key in CIP36 Key Registration", ctx)?; if let Some(key) = Cip36KeyRegistrationKeys::from_repr(key) { match key { Cip36KeyRegistrationKeys::VotingKey => { - if !found_keys.insert(key as u16) { - return Err(decode::Error::message( - "Duplicate key in CIP36 Key Registration voting key", - )); + if found_keys.contains(&key) { + err_report.push(format!( + "Duplicate key in CIP36 Key Registration voting key at item {} in map", index + 1), + ); + continue; + } + if let Some((is_cip36, voting_keys)) = decode_voting_key(d, &mut err_report) + { + cip36_key_registration.is_cip36 = is_cip36; + cip36_key_registration.voting_pks = voting_keys; } - let (is_cip36, voting_keys) = decode_voting_key(d)?; - cip36_key_registration.is_cip36 = Some(is_cip36); - cip36_key_registration.voting_pks = voting_keys; }, Cip36KeyRegistrationKeys::StakePk => { - if !found_keys.insert(key as u16) { - return Err(decode::Error::message( - "Duplicate key in CIP36 Key Registration stake public key", - )); + if found_keys.contains(&key) { + err_report.push(format!( + "Duplicate key in CIP36 Key Registration stake public key at item {} in map", index + 1), + ); + continue; + } + if let Some(stake_pk) = decode_stake_pk(d, &mut err_report) { + cip36_key_registration.stake_pk = stake_pk; } - let stake_pk = decode_stake_pk(d)?; - cip36_key_registration.stake_pk = stake_pk; }, Cip36KeyRegistrationKeys::PaymentAddr => { - if !found_keys.insert(key as u16) { - return Err(decode::Error::message( - "Duplicate key in CIP36 Key Registration payment address", - )); + if found_keys.contains(&key) { + err_report.push(format!( + "Duplicate key in CIP36 Key Registration payment address at item {} in map", index + 1), + ); + continue; + } + if let Some(shelley_addr) = decode_payment_addr(d, &mut err_report) { + cip36_key_registration.payment_addr = Some(shelley_addr.clone()); + cip36_key_registration.is_payable = !shelley_addr.payment().is_script(); } - let shelley_addr = decode_payment_addr(d)?; - cip36_key_registration.payment_addr = Some(shelley_addr.clone()); - cip36_key_registration.is_payable = !shelley_addr.payment().is_script(); }, Cip36KeyRegistrationKeys::Nonce => { - if !found_keys.insert(key as u16) { - return Err(decode::Error::message( - "Duplicate key in CIP36 Key Registration nonce", + if found_keys.contains(&key) { + err_report.push(format!( + "Duplicate key in CIP36 Key Registration nonce at item {} in map", + index + 1 )); + continue; + } + if let Some(nonce) = decode_nonce(d, &mut err_report) { + cip36_key_registration.nonce = nonce; } - let raw_nonce = decode_nonce(d)?; - cip36_key_registration.raw_nonce = raw_nonce; }, Cip36KeyRegistrationKeys::Purpose => { - if !found_keys.insert(key as u16) { - return Err(decode::Error::message( - "Duplicate key in CIP36 Key Registration purpose", + if found_keys.contains(&key) { + err_report.push(format!( + "Duplicate key in CIP36 Key Registration purpose at item {} in map", + index + 1 )); + continue; + } + if let Some(purpose) = decode_purpose(d, &mut err_report) { + cip36_key_registration.purpose = purpose; } - let purpose = decode_purpose(d)?; - cip36_key_registration.purpose = purpose; }, } + + // Update the founded keys. + found_keys.push(key); } } - // Check if all the required keys are present. - if found_keys.contains(&(Cip36KeyRegistrationKeys::VotingKey as u16)) - && found_keys.contains(&(Cip36KeyRegistrationKeys::StakePk as u16)) - && found_keys.contains(&(Cip36KeyRegistrationKeys::PaymentAddr as u16)) - && found_keys.contains(&(Cip36KeyRegistrationKeys::Nonce as u16)) - { - Ok(cip36_key_registration) - } else { - Err(decode::Error::message( - "Missing required key in CIP36 Key Registration", - )) + if !found_keys.contains(&Cip36KeyRegistrationKeys::VotingKey) { + err_report + .push("Missing required key in CIP36 Key Registration: Voting Key".to_string()); + } + + if !found_keys.contains(&Cip36KeyRegistrationKeys::StakePk) { + err_report.push( + "Missing required key in CIP36 Key Registration: Stake Public Key".to_string(), + ); + } + + if !found_keys.contains(&Cip36KeyRegistrationKeys::PaymentAddr) { + err_report.push( + "Missing required key in CIP36 Key Registration: Payment Address".to_string(), + ); + } + + if !found_keys.contains(&Cip36KeyRegistrationKeys::Nonce) { + err_report.push("Missing required key in CIP36 Key Registration: Nonce".to_string()); + } + + if !err_report.is_empty() { + return Err(decode::Error::message(format!("{err_report:?}"))); } + + Ok(cip36_key_registration) } } @@ -154,80 +185,129 @@ impl Decode<'_, ()> for Cip36KeyRegistration { /// /// # Returns /// -/// A tuple containing a boolean value, true if it is CIP36 format, false if it is CIP15 -/// format and a vector of voting public keys. -fn decode_voting_key(d: &mut Decoder) -> Result<(bool, Vec), decode::Error> { +/// - A tuple containing +/// - A boolean value, true if it is CIP36 format, false if it is CIP15, None if not +/// either CIP36 or CIP15. +/// - A vector of `VotingPubKey`, if the `voting_pk` vector cannot be converted to +/// verifying key, assign `voting_pk` to None. +/// - Return None if there is an error with decoding. +fn decode_voting_key( + d: &mut Decoder, err_report: &mut Vec, +) -> Option<(Option, Vec)> { let mut voting_keys = Vec::new(); - let mut is_cip36 = false; - - match d.datatype()? { - // CIP15 type registration (single voting key). - // ```cddl - // legacy_key_registration = $cip36_vote_pub_key - // $cip36_vote_pub_key /= bytes .size 32 - // ``` - minicbor::data::Type::Bytes => { - let pub_key = decode_bytes(d, "CIP36 Key Registration voting key, single voting key")?; - let vk = voting_pk_vec_to_verifying_key(&pub_key).map_err(|e| { - decode::Error::message(format!( - "CIP36 Key Registration voting key, singe voting key, {e}" - )) - })?; - // Since there is 1 voting key, all the weight goes to this key = 1. - voting_keys.push(VotingPubKey { - voting_pk: vk, - weight: 1, - }); - }, - // CIP36 type registration (multiple voting keys). - // ```cddl - // [+delegation] - // delegation = [$cip36_vote_pub_key, $weight] - // $cip36_vote_pub_key /= bytes .size 32 - // ``` - minicbor::data::Type::Array => { - is_cip36 = true; - let len = - decode_array_len(d, "CIP36 Key Registration voting key, multiple voting keys")?; - for _ in 0..len { - let len = decode_array_len(d, "CIP36 Key Registration voting key, delegations")?; - // This fixed array should be a length of 2 (voting key, weight). - if len != 2 { - return Err(decode::Error::message(format!( + let mut is_cip36 = None; + + match d.datatype() { + Ok(dt) => { + match dt { + // CIP15 type registration (single voting key). + // ```cddl + // legacy_key_registration = $cip36_vote_pub_key + // $cip36_vote_pub_key /= bytes .size 32 + // ``` + minicbor::data::Type::Bytes => { + is_cip36 = Some(false); + let pub_key = + decode_bytes(d, "CIP36 Key Registration voting key, single voting key") + .map_err(|e| { + err_report.push(format!("{e}")); + }) + .ok()?; + let vk = match voting_pk_vec_to_verifying_key(&pub_key) { + Ok(vk) => Some(vk), + Err(e) => { + err_report.push(format!( + "CIP36 Key Registration voting key, single voting key, {e}" + )); + None + }, + }; + + // Since there is 1 voting key, all the weight goes to this key = 1. + voting_keys.push(VotingPubKey { + voting_pk: vk, + weight: 1, + }); + }, + // CIP36 type registration (multiple voting keys). + // ```cddl + // [+delegation] + // delegation = [$cip36_vote_pub_key, $weight] + // $cip36_vote_pub_key /= bytes .size 32 + // ``` + minicbor::data::Type::Array => { + is_cip36 = Some(true); + let len = decode_array_len( + d, + "CIP36 Key Registration voting key, multiple voting keys", + ) + .map_err(|e| { + err_report.push(format!("{e}")); + }) + .ok()?; + + for _ in 0..len { + let len = + decode_array_len(d, "CIP36 Key Registration voting key, delegations") + .map_err(|e| { + err_report.push(format!("{e}")); + }) + .ok()?; + // This fixed array should be a length of 2 (voting key, weight). + if len != 2 { + err_report.push(format!( "Invalid length for CIP36 Key Registration voting key delegations, expected 2, got {len}" - ))); - } - // The first entry. - let pub_key = decode_bytes( + )); + return None; + } + // The first entry. + let pub_key = decode_bytes( d, "CIP36 Key Registration voting key, delegation array first entry (voting public key)", - )?; - // The second entry. - let weight: u32 = decode_helper( + ).map_err(|e| { + err_report.push(format!("{e}")); + }).ok()?; + + // The second entry. + let weight: u32 = decode_helper( d, "CIP36 Key Registration voting key, delegation array second entry (weight)", &mut (), - )?; - - let vk = voting_pk_vec_to_verifying_key(&pub_key).map_err(|e| { - decode::Error::message(format!( - "CIP36 Key Registration voting key, multiple voting keys, {e}" - )) - })?; - - voting_keys.push(VotingPubKey { - voting_pk: vk, - weight, - }); + ) + .map_err(|e| { + err_report.push(format!("{e}")); + }) + .ok()?; + + let vk = match voting_pk_vec_to_verifying_key(&pub_key) { + Ok(vk) => Some(vk), + Err(e) => { + err_report.push(format!( + "CIP36 Key Registration voting key, multiple voting keys, {e}" + )); + // Don't early return, continue with the next key. + None + }, + }; + + voting_keys.push(VotingPubKey { + voting_pk: vk, + weight, + }); + } + }, + + _ => { + err_report.push("Invalid datatype for CIP36 Key Registration voting key, should be either Array or Bytes".to_string()); + }, } }, - _ => { - return Err(decode::Error::message( - "Invalid datatype for CIP36 Key Registration voting key", - )) + Err(e) => { + err_report.push(format!("Decoding voting key, invalid data type: {e}")); + return None; }, } - Ok((is_cip36, voting_keys)) + Some((is_cip36, voting_keys)) } /// Helper function for converting `&[u8]` to `VerifyingKey`. @@ -247,11 +327,22 @@ fn voting_pk_vec_to_verifying_key(pub_key: &[u8]) -> anyhow::Result Result { - let pub_key = decode_bytes(d, "CIP36 Key Registration stake public key")?; - voting_pk_vec_to_verifying_key(&pub_key).map_err(|e| { - decode::Error::message(format!("CIP36 Key Registration stake public key, {e}")) - }) +/// None if cannot converted `Vec` to `VerifyingKey` or decoding error. +fn decode_stake_pk(d: &mut Decoder, err_report: &mut Vec) -> Option { + let pub_key = decode_bytes(d, "CIP36 Key Registration stake public key") + .map_err(|e| { + err_report.push(format!("{e}")); + }) + .ok()?; + match voting_pk_vec_to_verifying_key(&pub_key) { + Ok(vk) => Some(vk), + Err(e) => { + err_report.push(format!( + "CIP36 Key Registration voting key, multiple voting keys, {e}" + )); + None + }, + } } /// Helper function for decoding the payment address. @@ -264,17 +355,24 @@ fn decode_stake_pk(d: &mut Decoder) -> Result { /// # Returns /// /// The payment address as a `ShelleyAddress`. -fn decode_payment_addr(d: &mut Decoder) -> Result { - let raw_addr = decode_bytes(d, "CIP36 Key Registration payment address")?; - let address = Address::from_bytes(&raw_addr).map_err(|e| { - decode::Error::message(format!("CIP36 Key Registration payment address, {e}")) - })?; +/// None if cannot converted `Vec` to `ShelleyAddress` or decoding error. +fn decode_payment_addr(d: &mut Decoder, err_report: &mut Vec) -> Option { + let raw_addr = decode_bytes(d, "CIP36 Key Registration payment address") + .map_err(|e| { + err_report.push(format!("{e}")); + }) + .ok()?; + let address = Address::from_bytes(&raw_addr) + .map_err(|e| err_report.push(format!("CIP36 Key Registration payment address, {e}"))) + .ok()?; + if let Address::Shelley(addr) = address { - Ok(addr.clone()) + Some(addr.clone()) } else { - Err(decode::Error::message(format!( + err_report.push(format!( "Invalid CIP36 Key Registration payment address, expected Shelley address, got {address}" - ))) + )); + None } } @@ -288,8 +386,13 @@ fn decode_payment_addr(d: &mut Decoder) -> Result /// # Returns /// /// Raw nonce. -fn decode_nonce(d: &mut Decoder) -> Result { +/// None if decoding error. +fn decode_nonce(d: &mut Decoder, err_report: &mut Vec) -> Option { decode_helper(d, "CIP36 Key Registration nonce", &mut ()) + .map_err(|e| { + err_report.push(format!("{e}")); + }) + .ok() } /// Helper function for decoding the purpose. @@ -302,8 +405,12 @@ fn decode_nonce(d: &mut Decoder) -> Result { /// # Returns /// /// The purpose. -fn decode_purpose(d: &mut Decoder) -> Result { +fn decode_purpose(d: &mut Decoder, err_report: &mut Vec) -> Option { decode_helper(d, "CIP36 Key Registration purpose", &mut ()) + .map_err(|e| { + err_report.push(format!("{e}")); + }) + .ok() } #[cfg(test)] @@ -318,7 +425,9 @@ mod tests { "5839004777561E7D9EC112EC307572FAEC1AFF61FF0CFED68DF4CD5C847F1872B617657881E30AD17C46E4010C9CB3EBB2440653A34D32219C83E9" ).expect("cannot decode hex"); let mut decoder = Decoder::new(&hex_data); - let address = decode_payment_addr(&mut decoder); + let mut err_report = Vec::new(); + let address = decode_payment_addr(&mut decoder, &mut err_report); + assert!(err_report.is_empty()); assert_eq!(address.unwrap().to_vec().len(), 57); } @@ -330,8 +439,10 @@ mod tests { ) .expect("cannot decode hex"); let mut decoder = Decoder::new(&hex_data); - let stake_pk = decode_stake_pk(&mut decoder); - assert!(stake_pk.is_ok()); + let mut err_report = Vec::new(); + let stake_pk = decode_stake_pk(&mut decoder, &mut err_report); + assert!(err_report.is_empty()); + assert!(stake_pk.is_some()); } #[test] @@ -343,10 +454,10 @@ mod tests { ) .expect("cannot decode hex"); let mut decoder = Decoder::new(&hex_data); - - let (is_cip36, voting_pk) = decode_voting_key(&mut decoder).expect("Failed to decode"); - - assert!(is_cip36); + let mut err_report = Vec::new(); + let (is_cip36, voting_pk) = decode_voting_key(&mut decoder, &mut err_report).unwrap(); + assert!(err_report.is_empty()); + assert!(is_cip36.unwrap()); assert_eq!(voting_pk.len(), 1); } @@ -359,10 +470,10 @@ mod tests { ) .expect("cannot decode hex"); let mut decoder = Decoder::new(&hex_data); - - let (is_cip36, voting_pk) = decode_voting_key(&mut decoder).expect("Failed to decode"); - - assert!(!is_cip36); + let mut err_report = Vec::new(); + let (is_cip36, voting_pk) = decode_voting_key(&mut decoder, &mut err_report).unwrap(); + assert!(err_report.is_empty()); + assert!(!is_cip36.unwrap()); assert_eq!(voting_pk.len(), 1); } } From 5fb2094ceff8149113535b2c7e399c8ac4fa5d5f Mon Sep 17 00:00:00 2001 From: bkioshn Date: Thu, 2 Jan 2025 18:25:53 +0700 Subject: [PATCH 02/17] fix(cardano-blockchain-types): registration witness Signed-off-by: bkioshn --- .../src/cip36/registration_witness.rs | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/rust/cardano-blockchain-types/src/cip36/registration_witness.rs b/rust/cardano-blockchain-types/src/cip36/registration_witness.rs index 9d818c629bd..43a1039fdf7 100644 --- a/rust/cardano-blockchain-types/src/cip36/registration_witness.rs +++ b/rust/cardano-blockchain-types/src/cip36/registration_witness.rs @@ -25,24 +25,35 @@ impl Decode<'_, ()> for Cip36RegistrationWitness { fn decode(d: &mut Decoder, ctx: &mut ()) -> Result { let map_len = decode_map_len(d, "CIP36 Registration Witness")?; + // Record of errors found during decoding + let mut err_report = Vec::new(); + // Expected only 1 key in the map. if map_len != 1 { - return Err(decode::Error::message(format!( + err_report.push(format!( "Invalid CIP36 Registration Witness map length, expected 1, got {map_len}" - ))); + )); } let key: u16 = decode_helper(d, "key in CIP36 Registration Witness", ctx)?; // The key needs to be 1. if key != 1 { - return Err(decode::Error::message(format!( + err_report.push(format!( "Invalid CIP36 Registration Witness key, expected key 1, got {key}" - ))); + )); } let sig_bytes = decode_bytes(d, "CIP36 Registration Witness signature")?; - let signature = ed25519_dalek::Signature::from_slice(&sig_bytes).ok(); + let signature = ed25519_dalek::Signature::from_slice(&sig_bytes) + .map_err(|_| { + err_report.push("Invalid CIP36 Registration Witness signature".to_string()); + }) + .ok(); + + if !err_report.is_empty() { + return Err(decode::Error::message(format!("{err_report:?}"))); + } Ok(Cip36RegistrationWitness { signature }) } From b646bb751f46f5317b84f8d3481f6786df20108e Mon Sep 17 00:00:00 2001 From: bkioshn Date: Thu, 2 Jan 2025 18:26:07 +0700 Subject: [PATCH 03/17] fix(cardano-blockchain-types): voting pk Signed-off-by: bkioshn --- rust/cardano-blockchain-types/src/cip36/voting_pk.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rust/cardano-blockchain-types/src/cip36/voting_pk.rs b/rust/cardano-blockchain-types/src/cip36/voting_pk.rs index 6f3d70aa751..c859d96819e 100644 --- a/rust/cardano-blockchain-types/src/cip36/voting_pk.rs +++ b/rust/cardano-blockchain-types/src/cip36/voting_pk.rs @@ -3,11 +3,10 @@ use ed25519_dalek::VerifyingKey; /// Voting public key containing the public key and weight. -#[allow(dead_code)] #[derive(Clone, Debug)] pub struct VotingPubKey { /// Voting public key. - pub voting_pk: VerifyingKey, + pub voting_pk: Option, /// Voting key associated weight. pub weight: u32, } From 2e025d6fd25ad618cb8e77e6d15ca0876e4c0a5c Mon Sep 17 00:00:00 2001 From: bkioshn Date: Thu, 2 Jan 2025 18:26:24 +0700 Subject: [PATCH 04/17] fix(cardano-blockchain-types): validation test Signed-off-by: bkioshn --- rust/cardano-blockchain-types/src/cip36/validation.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/cardano-blockchain-types/src/cip36/validation.rs b/rust/cardano-blockchain-types/src/cip36/validation.rs index ee043b5acf8..31610e4ba80 100644 --- a/rust/cardano-blockchain-types/src/cip36/validation.rs +++ b/rust/cardano-blockchain-types/src/cip36/validation.rs @@ -150,7 +150,7 @@ mod tests { fn test_validate_voting_keys() { let mut cip36 = create_empty_cip36(true); cip36.key_registration.voting_pks.push(VotingPubKey { - voting_pk: VerifyingKey::default(), + voting_pk: Some(VerifyingKey::default()), weight: 1, }); let mut report = Vec::new(); @@ -165,11 +165,11 @@ mod tests { fn test_validate_invalid_voting_keys() { let mut cip36 = create_empty_cip36(true); cip36.key_registration.voting_pks.push(VotingPubKey { - voting_pk: VerifyingKey::default(), + voting_pk: Some(VerifyingKey::default()), weight: 1, }); cip36.key_registration.voting_pks.push(VotingPubKey { - voting_pk: VerifyingKey::default(), + voting_pk: Some(VerifyingKey::default()), weight: 1, }); let mut report = Vec::new(); From 82501bed33c9fb21406866764f0635ac37a1d584 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Thu, 2 Jan 2025 18:26:55 +0700 Subject: [PATCH 05/17] fix(cardano-blockchain-types): cip36 constructor Signed-off-by: bkioshn --- .../cardano-blockchain-types/src/cip36/mod.rs | 62 ++++++++++++++++--- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/rust/cardano-blockchain-types/src/cip36/mod.rs b/rust/cardano-blockchain-types/src/cip36/mod.rs index b21f9058714..c0a2b4f38ee 100644 --- a/rust/cardano-blockchain-types/src/cip36/mod.rs +++ b/rust/cardano-blockchain-types/src/cip36/mod.rs @@ -5,14 +5,16 @@ pub mod registration_witness; mod validation; pub mod voting_pk; +use anyhow::bail; use ed25519_dalek::VerifyingKey; use key_registration::Cip36KeyRegistration; +use minicbor::{Decode, Decoder}; use pallas::ledger::addresses::ShelleyAddress; use registration_witness::Cip36RegistrationWitness; use validation::{validate_payment_address_network, validate_signature, validate_voting_keys}; use voting_pk::VotingPubKey; -use crate::{MetadatumValue, Network}; +use crate::{MetadatumLabel, MetadatumValue, Network, Slot, TransactionAuxData}; /// CIP-36 Catalyst registration #[derive(Clone, Default, Debug)] @@ -41,16 +43,62 @@ pub struct Cip36Validation { impl Cip36 { /// Create an instance of CIP-36. - #[must_use] + /// The CIP-36 registration contains the key registration (61284) + /// and registration witness (61285) metadata. + /// + /// # Parameters + /// + /// * `aux_data` - The transaction auxiliary data. + /// * `is_catalyst_strict` - Is this a Catalyst strict registration? + /// * `slot` - The slot number of the auxiliary data. + /// + /// # Errors + /// + /// If the CIP-36 key registration or registration witness metadata is not found. + /// or if the CIP-36 key registration or registration witness metadata cannot be + /// decoded. pub fn new( - key_registration: Cip36KeyRegistration, registration_witness: Cip36RegistrationWitness, - is_catalyst_strict: bool, - ) -> Self { - Self { + aux_data: &TransactionAuxData, is_catalyst_strict: bool, slot: Slot, + ) -> anyhow::Result { + let Some(k61284) = aux_data.metadata(MetadatumLabel::CIP036_REGISTRATION) else { + bail!("CIP-36 key registration metadata not found") + }; + let Some(k61285) = aux_data.metadata(MetadatumLabel::CIP036_WITNESS) else { + bail!("CIP-36 registration witness metadata not found") + }; + + let mut key_registration = Decoder::new(k61284.as_ref()); + let mut registration_witness = Decoder::new(k61285.as_ref()); + + let key_registration = match Cip36KeyRegistration::decode(&mut key_registration, &mut ()) { + Ok(mut metadata) => { + let nonce = if is_catalyst_strict && metadata.raw_nonce > slot.into() { + slot + } else { + metadata.raw_nonce.into() + }; + + metadata.nonce = nonce.into(); + metadata + }, + Err(e) => { + bail!("Failed to construct CIP-36 key registration, {e}") + }, + }; + + let registration_witness = + match Cip36RegistrationWitness::decode(&mut registration_witness, &mut ()) { + Ok(metadata) => metadata, + Err(e) => { + bail!("Failed to construct CIP-36 registration witness {e}") + }, + }; + + Ok(Self { key_registration, registration_witness, is_catalyst_strict, - } + }) } /// Get the `is_cip36` flag from the registration. From 860d3f3714572255b4d13c6e14491b43d79fc89e Mon Sep 17 00:00:00 2001 From: bkioshn Date: Thu, 2 Jan 2025 19:01:49 +0700 Subject: [PATCH 06/17] fix(cardano-blockchain-types): format Signed-off-by: bkioshn --- .../src/cip36/key_registration.rs | 25 ++++++------------- 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/rust/cardano-blockchain-types/src/cip36/key_registration.rs b/rust/cardano-blockchain-types/src/cip36/key_registration.rs index 5d4bed73f8a..70b9e0df5f7 100644 --- a/rust/cardano-blockchain-types/src/cip36/key_registration.rs +++ b/rust/cardano-blockchain-types/src/cip36/key_registration.rs @@ -255,29 +255,18 @@ fn decode_voting_key( .ok()?; // This fixed array should be a length of 2 (voting key, weight). if len != 2 { - err_report.push(format!( - "Invalid length for CIP36 Key Registration voting key delegations, expected 2, got {len}" - )); + err_report.push(format!("Invalid length for CIP36 Key Registration voting key delegations, expected 2, got {len}")); return None; } // The first entry. - let pub_key = decode_bytes( - d, - "CIP36 Key Registration voting key, delegation array first entry (voting public key)", - ).map_err(|e| { - err_report.push(format!("{e}")); - }).ok()?; + let pub_key = decode_bytes(d, "CIP36 Key Registration voting key, delegation array first entry (voting public key)").map_err(|e| { + err_report.push(format!("{e}")); + }).ok()?; // The second entry. - let weight: u32 = decode_helper( - d, - "CIP36 Key Registration voting key, delegation array second entry (weight)", - &mut (), - ) - .map_err(|e| { - err_report.push(format!("{e}")); - }) - .ok()?; + let weight: u32 = decode_helper(d, "CIP36 Key Registration voting key, delegation array second entry (weight)", &mut (),).map_err(|e| { + err_report.push(format!("{e}")); + }).ok()?; let vk = match voting_pk_vec_to_verifying_key(&pub_key) { Ok(vk) => Some(vk), From c0afb63953473205aa4894183403bceec52c5d74 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Fri, 3 Jan 2025 14:20:09 +0700 Subject: [PATCH 07/17] fix(cardano-blockchain-types): move cip36 to be under metadata Signed-off-by: bkioshn --- rust/cardano-blockchain-types/src/lib.rs | 6 +++--- .../src/{ => metadata}/cip36/key_registration.rs | 0 .../src/{ => metadata}/cip36/mod.rs | 0 .../src/{ => metadata}/cip36/registration_witness.rs | 0 .../src/{ => metadata}/cip36/validation.rs | 2 +- .../src/{ => metadata}/cip36/voting_pk.rs | 0 rust/cardano-blockchain-types/src/metadata/mod.rs | 3 +++ 7 files changed, 7 insertions(+), 4 deletions(-) rename rust/cardano-blockchain-types/src/{ => metadata}/cip36/key_registration.rs (100%) rename rust/cardano-blockchain-types/src/{ => metadata}/cip36/mod.rs (100%) rename rust/cardano-blockchain-types/src/{ => metadata}/cip36/registration_witness.rs (100%) rename rust/cardano-blockchain-types/src/{ => metadata}/cip36/validation.rs (99%) rename rust/cardano-blockchain-types/src/{ => metadata}/cip36/voting_pk.rs (100%) create mode 100644 rust/cardano-blockchain-types/src/metadata/mod.rs diff --git a/rust/cardano-blockchain-types/src/lib.rs b/rust/cardano-blockchain-types/src/lib.rs index 0a7b091e691..7733d915052 100644 --- a/rust/cardano-blockchain-types/src/lib.rs +++ b/rust/cardano-blockchain-types/src/lib.rs @@ -1,10 +1,10 @@ //! Catalyst Enhanced `MultiEraBlock` Structures mod auxdata; -mod cip36; pub mod conversion; mod fork; pub mod hashes; +mod metadata; mod multi_era_block_data; mod network; mod point; @@ -21,11 +21,11 @@ pub use auxdata::{ metadatum_value::MetadatumValue, scripts::{Script, ScriptArray, ScriptType, TransactionScripts}, }; -pub use cip36::{ +pub use fork::Fork; +pub use metadata::cip36::{ key_registration::Cip36KeyRegistration, registration_witness::Cip36RegistrationWitness, voting_pk::VotingPubKey, Cip36, Cip36Validation, }; -pub use fork::Fork; pub use multi_era_block_data::MultiEraBlock; pub use network::Network; pub use point::Point; diff --git a/rust/cardano-blockchain-types/src/cip36/key_registration.rs b/rust/cardano-blockchain-types/src/metadata/cip36/key_registration.rs similarity index 100% rename from rust/cardano-blockchain-types/src/cip36/key_registration.rs rename to rust/cardano-blockchain-types/src/metadata/cip36/key_registration.rs diff --git a/rust/cardano-blockchain-types/src/cip36/mod.rs b/rust/cardano-blockchain-types/src/metadata/cip36/mod.rs similarity index 100% rename from rust/cardano-blockchain-types/src/cip36/mod.rs rename to rust/cardano-blockchain-types/src/metadata/cip36/mod.rs diff --git a/rust/cardano-blockchain-types/src/cip36/registration_witness.rs b/rust/cardano-blockchain-types/src/metadata/cip36/registration_witness.rs similarity index 100% rename from rust/cardano-blockchain-types/src/cip36/registration_witness.rs rename to rust/cardano-blockchain-types/src/metadata/cip36/registration_witness.rs diff --git a/rust/cardano-blockchain-types/src/cip36/validation.rs b/rust/cardano-blockchain-types/src/metadata/cip36/validation.rs similarity index 99% rename from rust/cardano-blockchain-types/src/cip36/validation.rs rename to rust/cardano-blockchain-types/src/metadata/cip36/validation.rs index 31610e4ba80..abd9a2ef79c 100644 --- a/rust/cardano-blockchain-types/src/cip36/validation.rs +++ b/rust/cardano-blockchain-types/src/metadata/cip36/validation.rs @@ -91,7 +91,7 @@ mod tests { use super::validate_purpose; use crate::{ - cip36::{ + metadata::cip36::{ key_registration::Cip36KeyRegistration, registration_witness::Cip36RegistrationWitness, validate_payment_address_network, validate_voting_keys, voting_pk::VotingPubKey, }, diff --git a/rust/cardano-blockchain-types/src/cip36/voting_pk.rs b/rust/cardano-blockchain-types/src/metadata/cip36/voting_pk.rs similarity index 100% rename from rust/cardano-blockchain-types/src/cip36/voting_pk.rs rename to rust/cardano-blockchain-types/src/metadata/cip36/voting_pk.rs diff --git a/rust/cardano-blockchain-types/src/metadata/mod.rs b/rust/cardano-blockchain-types/src/metadata/mod.rs new file mode 100644 index 00000000000..a4fc3152d15 --- /dev/null +++ b/rust/cardano-blockchain-types/src/metadata/mod.rs @@ -0,0 +1,3 @@ +//! Metadata module + +pub mod cip36; From 57a43845f3a8fd5f61433afea71faaf58ab8c07f Mon Sep 17 00:00:00 2001 From: bkioshn Date: Fri, 3 Jan 2025 15:52:34 +0700 Subject: [PATCH 08/17] fix(cardano-blockchain-types): use decode_helper from cbork-utils Signed-off-by: bkioshn --- rust/cardano-blockchain-types/Cargo.toml | 1 + rust/cardano-blockchain-types/src/lib.rs | 1 - .../src/metadata/cip36/key_registration.rs | 2 +- .../metadata/cip36/registration_witness.rs | 3 +- .../src/utils/decode_helper.rs | 238 ------------------ .../cardano-blockchain-types/src/utils/mod.rs | 3 - 6 files changed, 3 insertions(+), 245 deletions(-) delete mode 100644 rust/cardano-blockchain-types/src/utils/decode_helper.rs delete mode 100644 rust/cardano-blockchain-types/src/utils/mod.rs diff --git a/rust/cardano-blockchain-types/Cargo.toml b/rust/cardano-blockchain-types/Cargo.toml index 7e3091cafbd..3ac68bc3931 100644 --- a/rust/cardano-blockchain-types/Cargo.toml +++ b/rust/cardano-blockchain-types/Cargo.toml @@ -21,6 +21,7 @@ workspace = true pallas = { 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" } # pallas-hardano = { version = "0.30.1", git = "https://github.com/input-output-hk/catalyst-pallas.git", rev = "9b5183c8b90b90fe2cc319d986e933e9518957b3" } +cbork-utils = { git = "https://github.com/input-output-hk/catalyst-libs.git", branch = "main" } ouroboros = "0.18.4" tracing = "0.1.41" diff --git a/rust/cardano-blockchain-types/src/lib.rs b/rust/cardano-blockchain-types/src/lib.rs index 7733d915052..5360d9c0598 100644 --- a/rust/cardano-blockchain-types/src/lib.rs +++ b/rust/cardano-blockchain-types/src/lib.rs @@ -11,7 +11,6 @@ mod point; mod slot; mod txn_index; mod txn_witness; -pub mod utils; pub use auxdata::{ aux_data::TransactionAuxData, diff --git a/rust/cardano-blockchain-types/src/metadata/cip36/key_registration.rs b/rust/cardano-blockchain-types/src/metadata/cip36/key_registration.rs index 70b9e0df5f7..bdaa6fefb02 100644 --- a/rust/cardano-blockchain-types/src/metadata/cip36/key_registration.rs +++ b/rust/cardano-blockchain-types/src/metadata/cip36/key_registration.rs @@ -6,13 +6,13 @@ //! use anyhow::Context; +use cbork_utils::decode_helper::{decode_array_len, decode_bytes, decode_helper, decode_map_len}; use ed25519_dalek::VerifyingKey; use minicbor::{decode, Decode, Decoder}; use pallas::ledger::addresses::{Address, ShelleyAddress}; use strum::FromRepr; use super::voting_pk::VotingPubKey; -use crate::utils::decode_helper::{decode_array_len, decode_bytes, decode_helper, decode_map_len}; /// CIP-36 key registration - 61284 /// diff --git a/rust/cardano-blockchain-types/src/metadata/cip36/registration_witness.rs b/rust/cardano-blockchain-types/src/metadata/cip36/registration_witness.rs index 43a1039fdf7..e54cbbf711c 100644 --- a/rust/cardano-blockchain-types/src/metadata/cip36/registration_witness.rs +++ b/rust/cardano-blockchain-types/src/metadata/cip36/registration_witness.rs @@ -3,10 +3,9 @@ //! //! +use cbork_utils::decode_helper::{decode_bytes, decode_helper, decode_map_len}; use minicbor::{decode, Decode, Decoder}; -use crate::utils::decode_helper::{decode_bytes, decode_helper, decode_map_len}; - /// CIP-36 registration witness - 61285 /// /// ```cddl diff --git a/rust/cardano-blockchain-types/src/utils/decode_helper.rs b/rust/cardano-blockchain-types/src/utils/decode_helper.rs deleted file mode 100644 index 7ebf2d7afb1..00000000000 --- a/rust/cardano-blockchain-types/src/utils/decode_helper.rs +++ /dev/null @@ -1,238 +0,0 @@ -//! CBOR decoding helper functions. - -use minicbor::{data::Tag, decode, Decoder}; - -/// Generic helper function for decoding different types. -/// -/// # Errors -/// -/// Error if the decoding fails. -pub fn decode_helper<'a, T, C>( - d: &mut Decoder<'a>, from: &str, context: &mut C, -) -> Result -where T: minicbor::Decode<'a, C> { - T::decode(d, context).map_err(|e| { - decode::Error::message(format!( - "Failed to decode {:?} in {from}: {e}", - std::any::type_name::() - )) - }) -} - -/// Helper function for decoding bytes. -/// -/// # Errors -/// -/// Error if the decoding fails. -pub fn decode_bytes(d: &mut Decoder, from: &str) -> Result, decode::Error> { - d.bytes().map(<[u8]>::to_vec).map_err(|e| { - decode::Error::message(format!( - "Failed to decode bytes in {from}: - {e}" - )) - }) -} - -/// Helper function for decoding array. -/// -/// # Errors -/// -/// Error if the decoding fails. -pub fn decode_array_len(d: &mut Decoder, from: &str) -> Result { - d.array() - .map_err(|e| { - decode::Error::message(format!( - "Failed to decode array in {from}: - {e}" - )) - })? - .ok_or(decode::Error::message(format!( - "Failed to decode array in {from}, unexpected indefinite length", - ))) -} - -/// Helper function for decoding map. -/// -/// # Errors -/// -/// Error if the decoding fails. -pub fn decode_map_len(d: &mut Decoder, from: &str) -> Result { - d.map() - .map_err(|e| decode::Error::message(format!("Failed to decode map in {from}: {e}")))? - .ok_or(decode::Error::message(format!( - "Failed to decode map in {from}, unexpected indefinite length", - ))) -} - -/// Helper function for decoding tag. -/// -/// # Errors -/// -/// Error if the decoding fails. -pub fn decode_tag(d: &mut Decoder, from: &str) -> Result { - d.tag() - .map_err(|e| decode::Error::message(format!("Failed to decode tag in {from}: {e}"))) -} - -/// Decode any in CDDL, only support basic datatype -/// -/// # Errors -/// -/// Error if the decoding fails. -pub fn decode_any(d: &mut Decoder, from: &str) -> Result, decode::Error> { - match d.datatype()? { - minicbor::data::Type::String => { - match decode_helper::(d, &format!("{from} Any"), &mut ()) { - Ok(i) => Ok(i.as_bytes().to_vec()), - Err(e) => Err(e), - } - }, - minicbor::data::Type::U8 => { - match decode_helper::(d, &format!("{from} Any"), &mut ()) { - Ok(i) => Ok(i.to_be_bytes().to_vec()), - Err(e) => Err(e), - } - }, - minicbor::data::Type::U16 => { - match decode_helper::(d, &format!("{from} Any"), &mut ()) { - Ok(i) => Ok(i.to_be_bytes().to_vec()), - Err(e) => Err(e), - } - }, - minicbor::data::Type::U32 => { - match decode_helper::(d, &format!("{from} Any"), &mut ()) { - Ok(i) => Ok(i.to_be_bytes().to_vec()), - Err(e) => Err(e), - } - }, - minicbor::data::Type::U64 => { - match decode_helper::(d, &format!("{from} Any"), &mut ()) { - Ok(i) => Ok(i.to_be_bytes().to_vec()), - Err(e) => Err(e), - } - }, - minicbor::data::Type::I8 => { - match decode_helper::(d, &format!("{from} Any"), &mut ()) { - Ok(i) => Ok(i.to_be_bytes().to_vec()), - Err(e) => Err(e), - } - }, - minicbor::data::Type::I16 => { - match decode_helper::(d, &format!("{from} Any"), &mut ()) { - Ok(i) => Ok(i.to_be_bytes().to_vec()), - Err(e) => Err(e), - } - }, - minicbor::data::Type::I32 => { - match decode_helper::(d, &format!("{from} Any"), &mut ()) { - Ok(i) => Ok(i.to_be_bytes().to_vec()), - Err(e) => Err(e), - } - }, - minicbor::data::Type::I64 => { - match decode_helper::(d, &format!("{from} Any"), &mut ()) { - Ok(i) => Ok(i.to_be_bytes().to_vec()), - Err(e) => Err(e), - } - }, - minicbor::data::Type::Bytes => Ok(decode_bytes(d, &format!("{from} Any"))?), - minicbor::data::Type::Array => { - Ok(decode_array_len(d, &format!("{from} Any"))? - .to_be_bytes() - .to_vec()) - }, - _ => { - Err(decode::Error::message(format!( - "{from} Any, Data type not supported" - ))) - }, - } -} - -#[cfg(test)] -mod tests { - - use minicbor::Encoder; - - use super::*; - - #[test] - fn test_decode_any_bytes() { - let mut buf = Vec::new(); - let mut e = Encoder::new(&mut buf); - e.bytes(&[1, 2, 3, 4]).expect("Error encoding bytes"); - - let mut d = Decoder::new(&buf); - let result = decode_any(&mut d, "test").expect("Error decoding bytes"); - assert_eq!(result, vec![1, 2, 3, 4]); - } - - #[test] - fn test_decode_any_string() { - let mut buf = Vec::new(); - let mut e = Encoder::new(&mut buf); - e.str("hello").expect("Error encoding string"); - - let mut d = Decoder::new(&buf); - let result = decode_any(&mut d, "test").expect("Error decoding string"); - assert_eq!(result, b"hello".to_vec()); - } - - #[test] - fn test_decode_any_array() { - // The array should contain a supported type - let mut buf = Vec::new(); - let mut e = Encoder::new(&mut buf); - e.array(2).expect("Error encoding array"); - e.u8(1).expect("Error encoding u8"); - e.u8(2).expect("Error encoding u8"); - let mut d = Decoder::new(&buf); - let result = decode_any(&mut d, "test").expect("Error decoding array"); - // The decode of array is just a length of the array - assert_eq!( - u64::from_be_bytes(result.try_into().expect("Error converting bytes to u64")), - 2 - ); - } - - #[test] - fn test_decode_any_u32() { - let mut buf = Vec::new(); - let mut e = Encoder::new(&mut buf); - let num: u32 = 123_456_789; - e.u32(num).expect("Error encoding u32"); - - let mut d = Decoder::new(&buf); - let result = decode_any(&mut d, "test").expect("Error decoding u32"); - assert_eq!( - u32::from_be_bytes(result.try_into().expect("Error converting bytes to u32")), - num - ); - } - - #[test] - fn test_decode_any_i32() { - let mut buf = Vec::new(); - let mut e = Encoder::new(&mut buf); - let num: i32 = -123_456_789; - e.i32(num).expect("Error encoding i32"); - let mut d = Decoder::new(&buf); - let result = decode_any(&mut d, "test").expect("Error decoding i32"); - assert_eq!( - i32::from_be_bytes(result.try_into().expect("Error converting bytes to i32")), - num - ); - } - - #[test] - fn test_decode_any_unsupported_type() { - let mut buf = Vec::new(); - let mut e = Encoder::new(&mut buf); - e.null().expect("Error encoding null"); // Encode a null type which is unsupported - - let mut d = Decoder::new(&buf); - let result = decode_any(&mut d, "test"); - // Should print out the error message with the location of the error - assert!(result.is_err()); - } -} diff --git a/rust/cardano-blockchain-types/src/utils/mod.rs b/rust/cardano-blockchain-types/src/utils/mod.rs deleted file mode 100644 index 7c2dd72ccfa..00000000000 --- a/rust/cardano-blockchain-types/src/utils/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -//! Utility functions. - -pub mod decode_helper; From 3847005592cbbbfb2458944369a635e46b75f01f Mon Sep 17 00:00:00 2001 From: bkioshn Date: Fri, 3 Jan 2025 16:37:18 +0700 Subject: [PATCH 09/17] fix(cardano-blockchain-types): visibility, cip36 constructor Signed-off-by: bkioshn --- rust/cardano-blockchain-types/src/lib.rs | 5 +--- .../src/metadata/cip36/key_registration.rs | 14 +++------ .../src/metadata/cip36/mod.rs | 30 +++++++++++++++---- .../metadata/cip36/registration_witness.rs | 2 +- .../src/metadata/cip36/validation.rs | 24 +++++++-------- .../src/metadata/cip36/voting_pk.rs | 24 +++++++++++++-- 6 files changed, 65 insertions(+), 34 deletions(-) diff --git a/rust/cardano-blockchain-types/src/lib.rs b/rust/cardano-blockchain-types/src/lib.rs index 5360d9c0598..b2ae2a1bf96 100644 --- a/rust/cardano-blockchain-types/src/lib.rs +++ b/rust/cardano-blockchain-types/src/lib.rs @@ -21,10 +21,7 @@ pub use auxdata::{ scripts::{Script, ScriptArray, ScriptType, TransactionScripts}, }; pub use fork::Fork; -pub use metadata::cip36::{ - key_registration::Cip36KeyRegistration, registration_witness::Cip36RegistrationWitness, - voting_pk::VotingPubKey, Cip36, Cip36Validation, -}; +pub use metadata::cip36::{voting_pk::VotingPubKey, Cip36}; pub use multi_era_block_data::MultiEraBlock; pub use network::Network; pub use point::Point; diff --git a/rust/cardano-blockchain-types/src/metadata/cip36/key_registration.rs b/rust/cardano-blockchain-types/src/metadata/cip36/key_registration.rs index bdaa6fefb02..2dafa6bb9f5 100644 --- a/rust/cardano-blockchain-types/src/metadata/cip36/key_registration.rs +++ b/rust/cardano-blockchain-types/src/metadata/cip36/key_registration.rs @@ -28,7 +28,7 @@ use super::voting_pk::VotingPubKey; /// ``` #[allow(clippy::module_name_repetitions)] #[derive(Clone, Default, Debug)] -pub struct Cip36KeyRegistration { +pub(crate) struct Cip36KeyRegistration { /// Is this CIP36 or CIP15 format. pub is_cip36: Option, /// Voting public keys (called Delegations in the CIP-36 Spec). @@ -55,7 +55,7 @@ pub struct Cip36KeyRegistration { /// Enum of CIP36 registration (61284) with its associated unsigned integer key. #[derive(FromRepr, Debug, PartialEq)] #[repr(u16)] -pub enum Cip36KeyRegistrationKeys { +enum Cip36KeyRegistrationKeys { /// Voting key. VotingKey = 1, /// Stake public key. @@ -224,10 +224,7 @@ fn decode_voting_key( }; // Since there is 1 voting key, all the weight goes to this key = 1. - voting_keys.push(VotingPubKey { - voting_pk: vk, - weight: 1, - }); + voting_keys.push(VotingPubKey::new(vk, 1)); }, // CIP36 type registration (multiple voting keys). // ```cddl @@ -279,10 +276,7 @@ fn decode_voting_key( }, }; - voting_keys.push(VotingPubKey { - voting_pk: vk, - weight, - }); + voting_keys.push(VotingPubKey::new(vk, weight)); } }, diff --git a/rust/cardano-blockchain-types/src/metadata/cip36/mod.rs b/rust/cardano-blockchain-types/src/metadata/cip36/mod.rs index c0a2b4f38ee..742285b4fed 100644 --- a/rust/cardano-blockchain-types/src/metadata/cip36/mod.rs +++ b/rust/cardano-blockchain-types/src/metadata/cip36/mod.rs @@ -30,7 +30,7 @@ pub struct Cip36 { /// Validation value for CIP-36. #[allow(clippy::struct_excessive_bools, clippy::module_name_repetitions)] #[derive(Clone, Default, Debug)] -pub struct Cip36Validation { +pub(crate) struct Cip36Validation { /// Is the signature valid? (signature in 61285) pub is_valid_signature: bool, /// Is the payment address on the correct network? @@ -58,7 +58,7 @@ impl Cip36 { /// or if the CIP-36 key registration or registration witness metadata cannot be /// decoded. pub fn new( - aux_data: &TransactionAuxData, is_catalyst_strict: bool, slot: Slot, + aux_data: &TransactionAuxData, is_catalyst_strict: bool, slot: Slot, network: Network, ) -> anyhow::Result { let Some(k61284) = aux_data.metadata(MetadatumLabel::CIP036_REGISTRATION) else { bail!("CIP-36 key registration metadata not found") @@ -94,11 +94,25 @@ impl Cip36 { }, }; - Ok(Self { + let cip36 = Self { key_registration, registration_witness, is_catalyst_strict, - }) + }; + + let mut validation_report = Vec::new(); + // If the code reach here, then the CIP36 decoding is successful. + let validation = cip36.validate(network, k61284, &mut validation_report); + + if validation.is_valid_signature + && validation.is_valid_payment_address_network + && validation.is_valid_voting_keys + && validation.is_valid_purpose + { + Ok(cip36) + } else { + bail!("CIP-36 validation failed: {validation:?}, Reports: {validation_report:?}") + } } /// Get the `is_cip36` flag from the registration. @@ -144,6 +158,12 @@ impl Cip36 { self.key_registration.raw_nonce } + /// Is the payment address in the registration payable? + #[must_use] + pub fn is_payable(&self) -> bool { + self.key_registration.is_payable + } + /// Get the signature from the registration witness. #[must_use] pub fn signature(&self) -> Option { @@ -172,7 +192,7 @@ impl Cip36 { /// * `network` - The blockchain network. /// * `metadata` - The metadata value to be validated. /// * `validation_report` - Validation report to store the validation result. - pub fn validate( + fn validate( &self, network: Network, metadata: &MetadatumValue, validation_report: &mut Vec, ) -> Cip36Validation { let is_valid_signature = validate_signature(self, metadata, validation_report); diff --git a/rust/cardano-blockchain-types/src/metadata/cip36/registration_witness.rs b/rust/cardano-blockchain-types/src/metadata/cip36/registration_witness.rs index e54cbbf711c..1d3d3ab9b7b 100644 --- a/rust/cardano-blockchain-types/src/metadata/cip36/registration_witness.rs +++ b/rust/cardano-blockchain-types/src/metadata/cip36/registration_witness.rs @@ -15,7 +15,7 @@ use minicbor::{decode, Decode, Decoder}; /// ``` #[allow(clippy::module_name_repetitions)] #[derive(Clone, Default, Debug)] -pub struct Cip36RegistrationWitness { +pub(crate) struct Cip36RegistrationWitness { /// Signature of the registration data. pub signature: Option, } diff --git a/rust/cardano-blockchain-types/src/metadata/cip36/validation.rs b/rust/cardano-blockchain-types/src/metadata/cip36/validation.rs index abd9a2ef79c..a5ac4b3bac0 100644 --- a/rust/cardano-blockchain-types/src/metadata/cip36/validation.rs +++ b/rust/cardano-blockchain-types/src/metadata/cip36/validation.rs @@ -149,10 +149,10 @@ mod tests { #[test] fn test_validate_voting_keys() { let mut cip36 = create_empty_cip36(true); - cip36.key_registration.voting_pks.push(VotingPubKey { - voting_pk: Some(VerifyingKey::default()), - weight: 1, - }); + cip36 + .key_registration + .voting_pks + .push(VotingPubKey::new(Some(VerifyingKey::default()), 1)); let mut report = Vec::new(); let valid = validate_voting_keys(&cip36, &mut report); @@ -164,14 +164,14 @@ mod tests { #[test] fn test_validate_invalid_voting_keys() { let mut cip36 = create_empty_cip36(true); - cip36.key_registration.voting_pks.push(VotingPubKey { - voting_pk: Some(VerifyingKey::default()), - weight: 1, - }); - cip36.key_registration.voting_pks.push(VotingPubKey { - voting_pk: Some(VerifyingKey::default()), - weight: 1, - }); + cip36 + .key_registration + .voting_pks + .push(VotingPubKey::new(Some(VerifyingKey::default()), 1)); + cip36 + .key_registration + .voting_pks + .push(VotingPubKey::new(Some(VerifyingKey::default()), 1)); let mut report = Vec::new(); let valid = validate_voting_keys(&cip36, &mut report); diff --git a/rust/cardano-blockchain-types/src/metadata/cip36/voting_pk.rs b/rust/cardano-blockchain-types/src/metadata/cip36/voting_pk.rs index c859d96819e..a35ce68dd59 100644 --- a/rust/cardano-blockchain-types/src/metadata/cip36/voting_pk.rs +++ b/rust/cardano-blockchain-types/src/metadata/cip36/voting_pk.rs @@ -6,7 +6,27 @@ use ed25519_dalek::VerifyingKey; #[derive(Clone, Debug)] pub struct VotingPubKey { /// Voting public key. - pub voting_pk: Option, + voting_pk: Option, /// Voting key associated weight. - pub weight: u32, + weight: u32, +} + +impl VotingPubKey { + /// Create a new voting public key. + #[must_use] + pub fn new(voting_pk: Option, weight: u32) -> Self { + Self { voting_pk, weight } + } + + /// Get the voting public key. + #[must_use] + pub fn voting_pk(&self) -> Option<&VerifyingKey> { + self.voting_pk.as_ref() + } + + /// Get the voting key weight. + #[must_use] + pub fn weight(&self) -> u32 { + self.weight + } } From 9df8616f5861b5680f565b59b9f85427b1c77468 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Fri, 3 Jan 2025 18:32:20 +0700 Subject: [PATCH 10/17] fix(cardano-blockchain-types): cip36 constructor Signed-off-by: bkioshn --- .../src/metadata/cip36/mod.rs | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/rust/cardano-blockchain-types/src/metadata/cip36/mod.rs b/rust/cardano-blockchain-types/src/metadata/cip36/mod.rs index 742285b4fed..dfb63ebc6ac 100644 --- a/rust/cardano-blockchain-types/src/metadata/cip36/mod.rs +++ b/rust/cardano-blockchain-types/src/metadata/cip36/mod.rs @@ -14,7 +14,7 @@ use registration_witness::Cip36RegistrationWitness; use validation::{validate_payment_address_network, validate_signature, validate_voting_keys}; use voting_pk::VotingPubKey; -use crate::{MetadatumLabel, MetadatumValue, Network, Slot, TransactionAuxData}; +use crate::{MetadatumLabel, MetadatumValue, MultiEraBlock, Network, TxnIndex}; /// CIP-36 Catalyst registration #[derive(Clone, Default, Debug)] @@ -48,9 +48,9 @@ impl Cip36 { /// /// # Parameters /// - /// * `aux_data` - The transaction auxiliary data. + /// * `block` - The block containing the auxiliary data. + /// * `txn_idx` - The transaction index that contain the auxiliary data. /// * `is_catalyst_strict` - Is this a Catalyst strict registration? - /// * `slot` - The slot number of the auxiliary data. /// /// # Errors /// @@ -58,27 +58,30 @@ impl Cip36 { /// or if the CIP-36 key registration or registration witness metadata cannot be /// decoded. pub fn new( - aux_data: &TransactionAuxData, is_catalyst_strict: bool, slot: Slot, network: Network, + block: &MultiEraBlock, txn_idx: TxnIndex, is_catalyst_strict: bool, ) -> anyhow::Result { - let Some(k61284) = aux_data.metadata(MetadatumLabel::CIP036_REGISTRATION) else { + let Some(k61284) = block.txn_metadata(txn_idx, MetadatumLabel::CIP036_REGISTRATION) else { bail!("CIP-36 key registration metadata not found") }; - let Some(k61285) = aux_data.metadata(MetadatumLabel::CIP036_WITNESS) else { + let Some(k61285) = block.txn_metadata(txn_idx, MetadatumLabel::CIP036_WITNESS) else { bail!("CIP-36 registration witness metadata not found") }; + let slot = block.decode().slot(); + let network = block.network(); + let mut key_registration = Decoder::new(k61284.as_ref()); let mut registration_witness = Decoder::new(k61285.as_ref()); let key_registration = match Cip36KeyRegistration::decode(&mut key_registration, &mut ()) { Ok(mut metadata) => { - let nonce = if is_catalyst_strict && metadata.raw_nonce > slot.into() { + let nonce = if is_catalyst_strict && metadata.raw_nonce > slot { slot } else { - metadata.raw_nonce.into() + metadata.raw_nonce }; - metadata.nonce = nonce.into(); + metadata.nonce = nonce; metadata }, Err(e) => { From b4038202ad53ed74f72923c648c8e67f43544cdb Mon Sep 17 00:00:00 2001 From: bkioshn Date: Fri, 3 Jan 2025 21:08:15 +0700 Subject: [PATCH 11/17] fix(cardano-blockchain-types): handle unset data Signed-off-by: bkioshn --- .../src/metadata/cip36/key_registration.rs | 82 ++++++++++--------- .../src/metadata/cip36/mod.rs | 16 ++-- .../src/metadata/cip36/validation.rs | 41 ++++++---- 3 files changed, 77 insertions(+), 62 deletions(-) diff --git a/rust/cardano-blockchain-types/src/metadata/cip36/key_registration.rs b/rust/cardano-blockchain-types/src/metadata/cip36/key_registration.rs index 2dafa6bb9f5..f12dc23b886 100644 --- a/rust/cardano-blockchain-types/src/metadata/cip36/key_registration.rs +++ b/rust/cardano-blockchain-types/src/metadata/cip36/key_registration.rs @@ -30,26 +30,33 @@ use super::voting_pk::VotingPubKey; #[derive(Clone, Default, Debug)] pub(crate) struct Cip36KeyRegistration { /// Is this CIP36 or CIP15 format. + /// None if not either CIP36 or CIP15. pub is_cip36: Option, /// Voting public keys (called Delegations in the CIP-36 Spec). /// Field 1 in the CIP-36 61284 Spec. pub voting_pks: Vec, /// Stake public key to associate with the voting keys. /// Field 2 in the CIP-36 61284 Spec. - pub stake_pk: VerifyingKey, + /// None if it is not set. + pub stake_pk: Option, /// Payment Address to associate with the voting keys. /// Field 3 in the CIP-36 61284 Spec. + /// None if it is not set. pub payment_addr: Option, /// Nonce (nonce that has been slot corrected). /// Field 4 in the CIP-36 61284 Spec. - pub nonce: u64, + /// None if it is not set. + pub nonce: Option, /// Registration Purpose (Always 0 for Catalyst). /// Field 5 in the CIP-36 61284 Spec. - pub purpose: u64, + /// None if it is not set. + pub purpose: Option, /// Raw nonce (nonce that has not had slot correction applied). - pub raw_nonce: u64, + /// None if it is not set. + pub raw_nonce: Option, /// Is payment address payable? (not a script) - pub is_payable: bool, + /// None if it is not set. + pub is_payable: Option, } /// Enum of CIP36 registration (61284) with its associated unsigned integer key. @@ -74,9 +81,6 @@ impl Decode<'_, ()> for Cip36KeyRegistration { let mut cip36_key_registration = Cip36KeyRegistration::default(); - // Record of founded keys. Check for duplicate keys in the map - let mut found_keys: Vec = Vec::new(); - // Record of errors found during decoding let mut err_report = Vec::new(); @@ -86,11 +90,11 @@ impl Decode<'_, ()> for Cip36KeyRegistration { if let Some(key) = Cip36KeyRegistrationKeys::from_repr(key) { match key { Cip36KeyRegistrationKeys::VotingKey => { - if found_keys.contains(&key) { + if !cip36_key_registration.voting_pks.is_empty() { err_report.push(format!( "Duplicate key in CIP36 Key Registration voting key at item {} in map", index + 1), ); - continue; + continue; } if let Some((is_cip36, voting_keys)) = decode_voting_key(d, &mut err_report) { @@ -99,18 +103,18 @@ impl Decode<'_, ()> for Cip36KeyRegistration { } }, Cip36KeyRegistrationKeys::StakePk => { - if found_keys.contains(&key) { + if cip36_key_registration.stake_pk.is_some() { err_report.push(format!( "Duplicate key in CIP36 Key Registration stake public key at item {} in map", index + 1), ); continue; } if let Some(stake_pk) = decode_stake_pk(d, &mut err_report) { - cip36_key_registration.stake_pk = stake_pk; + cip36_key_registration.stake_pk = Some(stake_pk); } }, Cip36KeyRegistrationKeys::PaymentAddr => { - if found_keys.contains(&key) { + if cip36_key_registration.payment_addr.is_some() { err_report.push(format!( "Duplicate key in CIP36 Key Registration payment address at item {} in map", index + 1), ); @@ -118,58 +122,48 @@ impl Decode<'_, ()> for Cip36KeyRegistration { } if let Some(shelley_addr) = decode_payment_addr(d, &mut err_report) { cip36_key_registration.payment_addr = Some(shelley_addr.clone()); - cip36_key_registration.is_payable = !shelley_addr.payment().is_script(); + cip36_key_registration.is_payable = Some(!shelley_addr.payment().is_script()); } }, Cip36KeyRegistrationKeys::Nonce => { - if found_keys.contains(&key) { + if cip36_key_registration.raw_nonce.is_some() { err_report.push(format!( - "Duplicate key in CIP36 Key Registration nonce at item {} in map", - index + 1 - )); + "Duplicate key in CIP36 Key Registration nonce at item {} in map", index + 1), + ); continue; } if let Some(nonce) = decode_nonce(d, &mut err_report) { - cip36_key_registration.nonce = nonce; + cip36_key_registration.raw_nonce = Some(nonce); } }, Cip36KeyRegistrationKeys::Purpose => { - if found_keys.contains(&key) { + if cip36_key_registration.purpose.is_some() { err_report.push(format!( - "Duplicate key in CIP36 Key Registration purpose at item {} in map", - index + 1 - )); + "Duplicate key in CIP36 Key Registration purpose at item {} in map", index + 1), + ); continue; } if let Some(purpose) = decode_purpose(d, &mut err_report) { - cip36_key_registration.purpose = purpose; + cip36_key_registration.purpose = Some(purpose); } }, } - - // Update the founded keys. - found_keys.push(key); } } - if !found_keys.contains(&Cip36KeyRegistrationKeys::VotingKey) { - err_report - .push("Missing required key in CIP36 Key Registration: Voting Key".to_string()); + if cip36_key_registration.voting_pks.is_empty() { + err_report.push("Missing required key in CIP36 Key Registration: Voting Key".to_string()); } - if !found_keys.contains(&Cip36KeyRegistrationKeys::StakePk) { - err_report.push( - "Missing required key in CIP36 Key Registration: Stake Public Key".to_string(), - ); + if cip36_key_registration.stake_pk.is_none() { + err_report.push("Missing required key in CIP36 Key Registration: Stake Public Key".to_string()); } - if !found_keys.contains(&Cip36KeyRegistrationKeys::PaymentAddr) { - err_report.push( - "Missing required key in CIP36 Key Registration: Payment Address".to_string(), - ); + if cip36_key_registration.payment_addr.is_none() { + err_report.push("Missing required key in CIP36 Key Registration: Payment Address".to_string()); } - if !found_keys.contains(&Cip36KeyRegistrationKeys::Nonce) { + if cip36_key_registration.raw_nonce.is_none() { err_report.push("Missing required key in CIP36 Key Registration: Nonce".to_string()); } @@ -459,4 +453,14 @@ mod tests { assert!(!is_cip36.unwrap()); assert_eq!(voting_pk.len(), 1); } + + #[test] + fn test_decode_nonce() { + let hex_data = hex::decode("1A014905D1").expect("cannot decode hex"); + let mut decoder = Decoder::new(&hex_data); + let mut err_report = Vec::new(); + let nonce = decode_nonce(&mut decoder, &mut err_report); + assert!(err_report.is_empty()); + assert_eq!(nonce.unwrap(), 21562833); + } } diff --git a/rust/cardano-blockchain-types/src/metadata/cip36/mod.rs b/rust/cardano-blockchain-types/src/metadata/cip36/mod.rs index dfb63ebc6ac..8a40049a372 100644 --- a/rust/cardano-blockchain-types/src/metadata/cip36/mod.rs +++ b/rust/cardano-blockchain-types/src/metadata/cip36/mod.rs @@ -75,8 +75,8 @@ impl Cip36 { let key_registration = match Cip36KeyRegistration::decode(&mut key_registration, &mut ()) { Ok(mut metadata) => { - let nonce = if is_catalyst_strict && metadata.raw_nonce > slot { - slot + let nonce = if is_catalyst_strict && metadata.raw_nonce > Some(slot) { + Some(slot) } else { metadata.raw_nonce }; @@ -133,8 +133,8 @@ impl Cip36 { /// Get the stake public key from the registration. #[must_use] - pub fn stake_pk(&self) -> VerifyingKey { - self.key_registration.stake_pk + pub fn stake_pk(&self) -> Option<&VerifyingKey> { + self.key_registration.stake_pk.as_ref() } /// Get the payment address from the registration. @@ -145,25 +145,25 @@ impl Cip36 { /// Get the nonce from the registration. #[must_use] - pub fn nonce(&self) -> u64 { + pub fn nonce(&self) -> Option { self.key_registration.nonce } /// Get the purpose from the registration. #[must_use] - pub fn purpose(&self) -> u64 { + pub fn purpose(&self) -> Option { self.key_registration.purpose } /// Get the raw nonce from the registration. #[must_use] - pub fn raw_nonce(&self) -> u64 { + pub fn raw_nonce(&self) -> Option { self.key_registration.raw_nonce } /// Is the payment address in the registration payable? #[must_use] - pub fn is_payable(&self) -> bool { + pub fn is_payable(&self) -> Option { self.key_registration.is_payable } diff --git a/rust/cardano-blockchain-types/src/metadata/cip36/validation.rs b/rust/cardano-blockchain-types/src/metadata/cip36/validation.rs index a5ac4b3bac0..60fda21f736 100644 --- a/rust/cardano-blockchain-types/src/metadata/cip36/validation.rs +++ b/rust/cardano-blockchain-types/src/metadata/cip36/validation.rs @@ -29,11 +29,16 @@ pub(crate) fn validate_signature( return false; }; - if let Ok(()) = cip36.stake_pk().verify_strict(hash.as_bytes(), &sig) { - true + if let Some(stake_pk) = cip36.stake_pk() { + if let Ok(()) = stake_pk.verify_strict(hash.as_bytes(), &sig) { + return true; + } else { + validation_report.push("Validate CIP36 Signature, cannot verify signature".to_string()); + return false; + } } else { - validation_report.push("Validate CIP36 Signature, cannot verify signature".to_string()); - false + validation_report.push("Validate CIP36 Signature, stake public key is missing".to_string()); + return false; } } @@ -73,14 +78,19 @@ pub(crate) fn validate_voting_keys(cip36: &Cip36, validation_report: &mut Vec) -> bool { - if cip36.is_strict_catalyst() && cip36.purpose() != PROJECT_CATALYST_PURPOSE { - validation_report.push(format!( - "Validate CIP-36 Purpose, registration contains unknown purpose: {}", - cip36.purpose() - )); - return false; + if let Some(purpose) = cip36.purpose() { + if cip36.is_strict_catalyst() && purpose != PROJECT_CATALYST_PURPOSE { + validation_report.push(format!( + "Validate CIP-36 Purpose, registration contains unknown purpose: {purpose}" + )); + return false; + } + true + } else { + validation_report + .push("Validate CIP-36 Purpose, registration purpose is missing".to_string()); + false } - true } #[cfg(test)] @@ -186,20 +196,21 @@ mod tests { #[test] fn test_validate_purpose() { - let cip36 = create_empty_cip36(true); + let mut cip36 = create_empty_cip36(true); + cip36.key_registration.purpose = Some(0); let mut report = Vec::new(); let valid = validate_purpose(&cip36, &mut report); assert_eq!(report.len(), 0); - assert_eq!(cip36.purpose(), 0); + assert_eq!(cip36.purpose(), Some(0)); assert!(valid); } #[test] fn test_validate_invalid_purpose() { let mut cip36 = create_empty_cip36(true); - cip36.key_registration.purpose = 1; + cip36.key_registration.purpose = Some(1); let mut report = Vec::new(); let valid = validate_purpose(&cip36, &mut report); @@ -209,7 +220,7 @@ mod tests { .first() .expect("Failed to get the first index") .contains("unknown purpose")); - assert_eq!(cip36.purpose(), 1); + assert_eq!(cip36.purpose(), Some(1)); assert!(!valid); } } From d269b1788cbb57c1e699b0b15f3f878be65228e5 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Fri, 3 Jan 2025 21:37:10 +0700 Subject: [PATCH 12/17] fix(cardano-blockchain-types): cip36 err report Signed-off-by: bkioshn --- .../src/metadata/cip36/key_registration.rs | 57 ++++++++++++------- .../src/metadata/cip36/mod.rs | 2 +- .../src/metadata/cip36/validation.rs | 37 +++++------- 3 files changed, 51 insertions(+), 45 deletions(-) diff --git a/rust/cardano-blockchain-types/src/metadata/cip36/key_registration.rs b/rust/cardano-blockchain-types/src/metadata/cip36/key_registration.rs index f12dc23b886..a3171d919f8 100644 --- a/rust/cardano-blockchain-types/src/metadata/cip36/key_registration.rs +++ b/rust/cardano-blockchain-types/src/metadata/cip36/key_registration.rs @@ -49,8 +49,8 @@ pub(crate) struct Cip36KeyRegistration { pub nonce: Option, /// Registration Purpose (Always 0 for Catalyst). /// Field 5 in the CIP-36 61284 Spec. - /// None if it is not set. - pub purpose: Option, + /// Default to 0. + pub purpose: u64, /// Raw nonce (nonce that has not had slot correction applied). /// None if it is not set. pub raw_nonce: Option, @@ -81,6 +81,9 @@ impl Decode<'_, ()> for Cip36KeyRegistration { let mut cip36_key_registration = Cip36KeyRegistration::default(); + // Record of founded keys. Check for duplicate keys in the map + let mut found_keys: Vec = Vec::new(); + // Record of errors found during decoding let mut err_report = Vec::new(); @@ -90,11 +93,11 @@ impl Decode<'_, ()> for Cip36KeyRegistration { if let Some(key) = Cip36KeyRegistrationKeys::from_repr(key) { match key { Cip36KeyRegistrationKeys::VotingKey => { - if !cip36_key_registration.voting_pks.is_empty() { + if found_keys.contains(&key) { err_report.push(format!( "Duplicate key in CIP36 Key Registration voting key at item {} in map", index + 1), ); - continue; + continue; } if let Some((is_cip36, voting_keys)) = decode_voting_key(d, &mut err_report) { @@ -103,7 +106,7 @@ impl Decode<'_, ()> for Cip36KeyRegistration { } }, Cip36KeyRegistrationKeys::StakePk => { - if cip36_key_registration.stake_pk.is_some() { + if found_keys.contains(&key) { err_report.push(format!( "Duplicate key in CIP36 Key Registration stake public key at item {} in map", index + 1), ); @@ -114,7 +117,7 @@ impl Decode<'_, ()> for Cip36KeyRegistration { } }, Cip36KeyRegistrationKeys::PaymentAddr => { - if cip36_key_registration.payment_addr.is_some() { + if found_keys.contains(&key) { err_report.push(format!( "Duplicate key in CIP36 Key Registration payment address at item {} in map", index + 1), ); @@ -122,14 +125,16 @@ impl Decode<'_, ()> for Cip36KeyRegistration { } if let Some(shelley_addr) = decode_payment_addr(d, &mut err_report) { cip36_key_registration.payment_addr = Some(shelley_addr.clone()); - cip36_key_registration.is_payable = Some(!shelley_addr.payment().is_script()); + cip36_key_registration.is_payable = + Some(!shelley_addr.payment().is_script()); } }, Cip36KeyRegistrationKeys::Nonce => { - if cip36_key_registration.raw_nonce.is_some() { + if found_keys.contains(&key) { err_report.push(format!( - "Duplicate key in CIP36 Key Registration nonce at item {} in map", index + 1), - ); + "Duplicate key in CIP36 Key Registration nonce at item {} in map", + index + 1 + )); continue; } if let Some(nonce) = decode_nonce(d, &mut err_report) { @@ -137,33 +142,41 @@ impl Decode<'_, ()> for Cip36KeyRegistration { } }, Cip36KeyRegistrationKeys::Purpose => { - if cip36_key_registration.purpose.is_some() { + if found_keys.contains(&key) { err_report.push(format!( - "Duplicate key in CIP36 Key Registration purpose at item {} in map", index + 1), - ); + "Duplicate key in CIP36 Key Registration purpose at item {} in map", + index + 1 + )); continue; } if let Some(purpose) = decode_purpose(d, &mut err_report) { - cip36_key_registration.purpose = Some(purpose); + cip36_key_registration.purpose = purpose; } }, } + // Update the founded keys. + found_keys.push(key); } } - if cip36_key_registration.voting_pks.is_empty() { - err_report.push("Missing required key in CIP36 Key Registration: Voting Key".to_string()); + if !found_keys.contains(&Cip36KeyRegistrationKeys::VotingKey) { + err_report + .push("Missing required key in CIP36 Key Registration: Voting Key".to_string()); } - if cip36_key_registration.stake_pk.is_none() { - err_report.push("Missing required key in CIP36 Key Registration: Stake Public Key".to_string()); + if !found_keys.contains(&Cip36KeyRegistrationKeys::StakePk) { + err_report.push( + "Missing required key in CIP36 Key Registration: Stake Public Key".to_string(), + ); } - if cip36_key_registration.payment_addr.is_none() { - err_report.push("Missing required key in CIP36 Key Registration: Payment Address".to_string()); + if !found_keys.contains(&Cip36KeyRegistrationKeys::PaymentAddr) { + err_report.push( + "Missing required key in CIP36 Key Registration: Payment Address".to_string(), + ); } - if cip36_key_registration.raw_nonce.is_none() { + if !found_keys.contains(&Cip36KeyRegistrationKeys::Nonce) { err_report.push("Missing required key in CIP36 Key Registration: Nonce".to_string()); } @@ -461,6 +474,6 @@ mod tests { let mut err_report = Vec::new(); let nonce = decode_nonce(&mut decoder, &mut err_report); assert!(err_report.is_empty()); - assert_eq!(nonce.unwrap(), 21562833); + assert_eq!(nonce.unwrap(), 21_562_833); } } diff --git a/rust/cardano-blockchain-types/src/metadata/cip36/mod.rs b/rust/cardano-blockchain-types/src/metadata/cip36/mod.rs index 8a40049a372..a38c2581882 100644 --- a/rust/cardano-blockchain-types/src/metadata/cip36/mod.rs +++ b/rust/cardano-blockchain-types/src/metadata/cip36/mod.rs @@ -151,7 +151,7 @@ impl Cip36 { /// Get the purpose from the registration. #[must_use] - pub fn purpose(&self) -> Option { + pub fn purpose(&self) -> u64 { self.key_registration.purpose } diff --git a/rust/cardano-blockchain-types/src/metadata/cip36/validation.rs b/rust/cardano-blockchain-types/src/metadata/cip36/validation.rs index 60fda21f736..61102a27806 100644 --- a/rust/cardano-blockchain-types/src/metadata/cip36/validation.rs +++ b/rust/cardano-blockchain-types/src/metadata/cip36/validation.rs @@ -32,14 +32,13 @@ pub(crate) fn validate_signature( if let Some(stake_pk) = cip36.stake_pk() { if let Ok(()) = stake_pk.verify_strict(hash.as_bytes(), &sig) { return true; - } else { - validation_report.push("Validate CIP36 Signature, cannot verify signature".to_string()); - return false; } - } else { - validation_report.push("Validate CIP36 Signature, stake public key is missing".to_string()); + validation_report.push("Validate CIP36 Signature, cannot verify signature".to_string()); return false; } + + validation_report.push("Validate CIP36 Signature, stake public key is missing".to_string()); + false } /// Validate the payment address network against the given network. @@ -78,19 +77,14 @@ pub(crate) fn validate_voting_keys(cip36: &Cip36, validation_report: &mut Vec) -> bool { - if let Some(purpose) = cip36.purpose() { - if cip36.is_strict_catalyst() && purpose != PROJECT_CATALYST_PURPOSE { - validation_report.push(format!( - "Validate CIP-36 Purpose, registration contains unknown purpose: {purpose}" - )); - return false; - } - true - } else { - validation_report - .push("Validate CIP-36 Purpose, registration purpose is missing".to_string()); - false + if cip36.is_strict_catalyst() && cip36.purpose() != PROJECT_CATALYST_PURPOSE { + validation_report.push(format!( + "Validate CIP-36 Purpose, registration contains unknown purpose: {}", + cip36.purpose() + )); + return false; } + true } #[cfg(test)] @@ -196,21 +190,20 @@ mod tests { #[test] fn test_validate_purpose() { - let mut cip36 = create_empty_cip36(true); - cip36.key_registration.purpose = Some(0); + let cip36 = create_empty_cip36(true); let mut report = Vec::new(); let valid = validate_purpose(&cip36, &mut report); assert_eq!(report.len(), 0); - assert_eq!(cip36.purpose(), Some(0)); + assert_eq!(cip36.purpose(), 0); assert!(valid); } #[test] fn test_validate_invalid_purpose() { let mut cip36 = create_empty_cip36(true); - cip36.key_registration.purpose = Some(1); + cip36.key_registration.purpose = 1; let mut report = Vec::new(); let valid = validate_purpose(&cip36, &mut report); @@ -220,7 +213,7 @@ mod tests { .first() .expect("Failed to get the first index") .contains("unknown purpose")); - assert_eq!(cip36.purpose(), Some(1)); + assert_eq!(cip36.purpose(), 1); assert!(!valid); } } From d82fa5f0d5632805573b3768eefcc1678a08bf80 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Sat, 4 Jan 2025 09:15:49 +0700 Subject: [PATCH 13/17] fix(cardano-blockchain-types): update cbor-utils tag Signed-off-by: bkioshn --- rust/cardano-blockchain-types/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/cardano-blockchain-types/Cargo.toml b/rust/cardano-blockchain-types/Cargo.toml index 3a5dcb6b254..01a04d4c1b2 100644 --- a/rust/cardano-blockchain-types/Cargo.toml +++ b/rust/cardano-blockchain-types/Cargo.toml @@ -21,7 +21,7 @@ workspace = true pallas = { 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" } # pallas-hardano = { version = "0.30.1", git = "https://github.com/input-output-hk/catalyst-pallas.git", rev = "9b5183c8b90b90fe2cc319d986e933e9518957b3" } -cbork-utils = { git = "https://github.com/input-output-hk/catalyst-libs.git", branch = "main" } +cbork-utils = { version = "0.0.1", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "v0.0.11" } ouroboros = "0.18.4" tracing = "0.1.41" From be138fec8312bd8ed989e8a5c1f7efa439c4df1e Mon Sep 17 00:00:00 2001 From: bkioshn Date: Mon, 6 Jan 2025 08:41:27 +0700 Subject: [PATCH 14/17] fix(cardano-blockchain-types): cip36 now contain validation Signed-off-by: bkioshn --- .../src/metadata/cip36/mod.rs | 81 +++------ .../src/metadata/cip36/validation.rs | 167 ++++++++++++------ 2 files changed, 141 insertions(+), 107 deletions(-) diff --git a/rust/cardano-blockchain-types/src/metadata/cip36/mod.rs b/rust/cardano-blockchain-types/src/metadata/cip36/mod.rs index a38c2581882..dcdc4de6ad7 100644 --- a/rust/cardano-blockchain-types/src/metadata/cip36/mod.rs +++ b/rust/cardano-blockchain-types/src/metadata/cip36/mod.rs @@ -11,10 +11,10 @@ use key_registration::Cip36KeyRegistration; use minicbor::{Decode, Decoder}; use pallas::ledger::addresses::ShelleyAddress; use registration_witness::Cip36RegistrationWitness; -use validation::{validate_payment_address_network, validate_signature, validate_voting_keys}; +use validation::{validate_cip36, Cip36Validation}; use voting_pk::VotingPubKey; -use crate::{MetadatumLabel, MetadatumValue, MultiEraBlock, Network, TxnIndex}; +use crate::{MetadatumLabel, MultiEraBlock, TxnIndex}; /// CIP-36 Catalyst registration #[derive(Clone, Default, Debug)] @@ -25,20 +25,9 @@ pub struct Cip36 { registration_witness: Cip36RegistrationWitness, /// Is this a Catalyst strict registration? is_catalyst_strict: bool, -} - -/// Validation value for CIP-36. -#[allow(clippy::struct_excessive_bools, clippy::module_name_repetitions)] -#[derive(Clone, Default, Debug)] -pub(crate) struct Cip36Validation { - /// Is the signature valid? (signature in 61285) - pub is_valid_signature: bool, - /// Is the payment address on the correct network? - pub is_valid_payment_address_network: bool, - /// Is the voting keys valid? - pub is_valid_voting_keys: bool, - /// Is the purpose valid? (Always 0 for Catalyst) - pub is_valid_purpose: bool, + /// CIP36 validation. + #[allow(dead_code)] + validation: Cip36Validation, } impl Cip36 { @@ -97,24 +86,29 @@ impl Cip36 { }, }; + let mut validation_report = Vec::new(); + // If the code reach here, then the CIP36 decoding is successful. + let validation = validate_cip36( + &key_registration, + ®istration_witness, + is_catalyst_strict, + network, + k61284, + &mut validation_report, + ); + let cip36 = Self { key_registration, registration_witness, is_catalyst_strict, + validation, }; - let mut validation_report = Vec::new(); - // If the code reach here, then the CIP36 decoding is successful. - let validation = cip36.validate(network, k61284, &mut validation_report); - - if validation.is_valid_signature - && validation.is_valid_payment_address_network - && validation.is_valid_voting_keys - && validation.is_valid_purpose - { + if validation_report.is_empty() { Ok(cip36) } else { - bail!("CIP-36 validation failed: {validation:?}, Reports: {validation_report:?}") + // If there are validation errors, the CIP36 is invalid + bail!("CIP-36 validation failed: {cip36:?}, Reports: {validation_report:?}") } } @@ -179,36 +173,9 @@ impl Cip36 { self.is_catalyst_strict } - /// Validation for CIP-36 - /// The validation include the following: - /// * Signature validation of the registration witness 61285 against the stake public - /// key in key registration 61284. - /// * Payment address network validation against the network. The given network should - /// match the network tag within the payment address. - /// * Purpose validation, the purpose should be 0 for Catalyst (when - /// `is_strict_catalyst` is true). - /// * Voting keys validation, Catalyst supports only a single voting key per - /// registration when `is_strict_catalyst` is true. - /// - /// # Parameters - /// - /// * `network` - The blockchain network. - /// * `metadata` - The metadata value to be validated. - /// * `validation_report` - Validation report to store the validation result. - fn validate( - &self, network: Network, metadata: &MetadatumValue, validation_report: &mut Vec, - ) -> Cip36Validation { - let is_valid_signature = validate_signature(self, metadata, validation_report); - let is_valid_payment_address_network = - validate_payment_address_network(self, network, validation_report).unwrap_or_default(); - let is_valid_voting_keys = validate_voting_keys(self, validation_report); - let is_valid_purpose = validation::validate_purpose(self, validation_report); - - Cip36Validation { - is_valid_signature, - is_valid_payment_address_network, - is_valid_voting_keys, - is_valid_purpose, - } + /// Get the CIP-36 validation. + #[must_use] + pub fn validation(&self) -> &Cip36Validation { + &self.validation } } diff --git a/rust/cardano-blockchain-types/src/metadata/cip36/validation.rs b/rust/cardano-blockchain-types/src/metadata/cip36/validation.rs index 61102a27806..704d322988c 100644 --- a/rust/cardano-blockchain-types/src/metadata/cip36/validation.rs +++ b/rust/cardano-blockchain-types/src/metadata/cip36/validation.rs @@ -1,6 +1,6 @@ //! Validation function for CIP-36 -use super::Cip36; +use super::{Cip36KeyRegistration, Cip36RegistrationWitness}; use crate::{MetadatumValue, Network}; /// Project Catalyst Purpose @@ -12,10 +12,69 @@ pub const PROJECT_CATALYST_PURPOSE: u64 = 0; /// 19 EF64 # unsigned(61284) pub const SIGNDATA_PREAMBLE: [u8; 4] = [0xA1, 0x19, 0xEF, 0x64]; +/// Validation value for CIP-36. +#[allow(clippy::struct_excessive_bools, clippy::module_name_repetitions)] +#[derive(Clone, Default, Debug)] +pub struct Cip36Validation { + /// Is the signature valid? (signature in 61285) + pub is_valid_signature: bool, + /// Is the payment address on the correct network? + pub is_valid_payment_address_network: bool, + /// Is the voting keys valid? + pub is_valid_voting_keys: bool, + /// Is the purpose valid? (Always 0 for Catalyst) + pub is_valid_purpose: bool, +} + +/// Validation for CIP-36 +/// The validation include the following: +/// * Signature validation of the registration witness 61285 against the stake public key +/// in key registration 61284. +/// * Payment address network validation against the network. The given network should +/// match the network tag within the payment address. +/// * Purpose validation, the purpose should be 0 for Catalyst (when `is_strict_catalyst` +/// is true). +/// * Voting keys validation, Catalyst supports only a single voting key per registration +/// when `is_strict_catalyst` is true. +/// +/// # Parameters +/// +/// * `network` - The blockchain network. +/// * `metadata` - The metadata value to be validated. +/// * `validation_report` - Validation report to store the validation result. +pub(crate) fn validate_cip36( + key_registration: &Cip36KeyRegistration, registration_witness: &Cip36RegistrationWitness, + is_strict_catalyst: bool, network: Network, metadata: &MetadatumValue, + validation_report: &mut Vec, +) -> Cip36Validation { + // Need to make sure that when return false, the validation_report is updated. + let is_valid_signature = validate_signature( + key_registration, + registration_witness, + metadata, + validation_report, + ); + let is_valid_payment_address_network = + validate_payment_address_network(key_registration, network, validation_report) + .unwrap_or_default(); + let is_valid_voting_keys = + validate_voting_keys(key_registration, is_strict_catalyst, validation_report); + let is_valid_purpose = + validate_purpose(key_registration, is_strict_catalyst, validation_report); + + Cip36Validation { + is_valid_signature, + is_valid_payment_address_network, + is_valid_voting_keys, + is_valid_purpose, + } +} + /// Validate the signature against the public key. #[allow(clippy::too_many_lines)] -pub(crate) fn validate_signature( - cip36: &Cip36, metadata: &MetadatumValue, validation_report: &mut Vec, +fn validate_signature( + key_registration: &Cip36KeyRegistration, registration_witness: &Cip36RegistrationWitness, + metadata: &MetadatumValue, validation_report: &mut Vec, ) -> bool { let hash = blake2b_simd::Params::new() .hash_length(32) @@ -24,12 +83,12 @@ pub(crate) fn validate_signature( .update(metadata.as_ref()) .finalize(); - let Some(sig) = cip36.signature() else { + let Some(sig) = registration_witness.signature else { validation_report.push("Validate CIP36 Signature, signature is invalid".to_string()); return false; }; - if let Some(stake_pk) = cip36.stake_pk() { + if let Some(stake_pk) = key_registration.stake_pk { if let Ok(()) = stake_pk.verify_strict(hash.as_bytes(), &sig) { return true; } @@ -42,10 +101,10 @@ pub(crate) fn validate_signature( } /// Validate the payment address network against the given network. -pub(crate) fn validate_payment_address_network( - cip36: &Cip36, network: Network, validation_report: &mut Vec, +fn validate_payment_address_network( + key_registration: &Cip36KeyRegistration, network: Network, validation_report: &mut Vec, ) -> Option { - if let Some(address) = cip36.payment_address() { + if let Some(address) = &key_registration.payment_addr { let network_tag = address.network(); let valid = match network { Network::Mainnet => network_tag.value() == 1, @@ -59,16 +118,22 @@ pub(crate) fn validate_payment_address_network( Some(valid) } else { + validation_report.push( + "Validate CIP36 payment address network, cannot find payment address in the registration".to_string() + ); None } } /// Validate the voting keys. -pub(crate) fn validate_voting_keys(cip36: &Cip36, validation_report: &mut Vec) -> bool { - if cip36.is_strict_catalyst() && cip36.voting_pks().len() != 1 { +fn validate_voting_keys( + key_registration: &Cip36KeyRegistration, is_strict_catalyst: bool, + validation_report: &mut Vec, +) -> bool { + if is_strict_catalyst && key_registration.voting_pks.len() != 1 { validation_report.push(format!( "Validate CIP-36 Voting Keys, Catalyst supports only a single voting key per registration, found {}", - cip36.voting_pks().len() + key_registration.voting_pks.len() )); return false; } @@ -76,11 +141,14 @@ pub(crate) fn validate_voting_keys(cip36: &Cip36, validation_report: &mut Vec) -> bool { - if cip36.is_strict_catalyst() && cip36.purpose() != PROJECT_CATALYST_PURPOSE { +fn validate_purpose( + key_registration: &Cip36KeyRegistration, is_strict_catalyst: bool, + validation_report: &mut Vec, +) -> bool { + if is_strict_catalyst && key_registration.purpose != PROJECT_CATALYST_PURPOSE { validation_report.push(format!( "Validate CIP-36 Purpose, registration contains unknown purpose: {}", - cip36.purpose() + key_registration.purpose )); return false; } @@ -96,33 +164,28 @@ mod tests { use super::validate_purpose; use crate::{ metadata::cip36::{ - key_registration::Cip36KeyRegistration, registration_witness::Cip36RegistrationWitness, - validate_payment_address_network, validate_voting_keys, voting_pk::VotingPubKey, + key_registration::Cip36KeyRegistration, + validation::{validate_payment_address_network, validate_voting_keys}, + voting_pk::VotingPubKey, }, - Cip36, Network, + Network, }; - fn create_empty_cip36(strict: bool) -> Cip36 { - Cip36 { - key_registration: Cip36KeyRegistration::default(), - registration_witness: Cip36RegistrationWitness::default(), - is_catalyst_strict: strict, - } - } - #[test] fn test_validate_payment_address_network() { - let mut cip36 = create_empty_cip36(true); // cSpell:disable let addr = Address::from_bech32("addr_test1qprhw4s70k0vzyhvxp6h97hvrtlkrlcvlmtgmaxdtjz87xrjkctk27ypuv9dzlzxusqse89naweygpjn5dxnygvus05sdq9h07").expect("Failed to create address"); // cSpell:enable let Address::Shelley(shelley_addr) = addr else { panic!("Invalid address type") }; - cip36.key_registration.payment_addr = Some(shelley_addr); + let key_registration = Cip36KeyRegistration { + payment_addr: Some(shelley_addr), + ..Default::default() + }; let mut report = Vec::new(); - - let valid = validate_payment_address_network(&cip36, Network::Preprod, &mut report); + let valid = + validate_payment_address_network(&key_registration, Network::Preprod, &mut report); assert_eq!(report.len(), 0); assert_eq!(valid, Some(true)); @@ -130,17 +193,19 @@ mod tests { #[test] fn test_validate_invalid_payment_address_network() { - let mut cip36 = create_empty_cip36(true); // cSpell:disable let addr = Address::from_bech32("addr_test1qprhw4s70k0vzyhvxp6h97hvrtlkrlcvlmtgmaxdtjz87xrjkctk27ypuv9dzlzxusqse89naweygpjn5dxnygvus05sdq9h07").expect("Failed to create address"); // cSpell:enable let Address::Shelley(shelley_addr) = addr else { panic!("Invalid address type") }; - cip36.key_registration.payment_addr = Some(shelley_addr); + let key_registration = Cip36KeyRegistration { + payment_addr: Some(shelley_addr), + ..Default::default() + }; let mut report = Vec::new(); - - let valid = validate_payment_address_network(&cip36, Network::Mainnet, &mut report); + let valid = + validate_payment_address_network(&key_registration, Network::Mainnet, &mut report); assert_eq!(report.len(), 1); assert!(report @@ -152,14 +217,14 @@ mod tests { #[test] fn test_validate_voting_keys() { - let mut cip36 = create_empty_cip36(true); - cip36 - .key_registration + let mut key_registration = Cip36KeyRegistration::default(); + + key_registration .voting_pks .push(VotingPubKey::new(Some(VerifyingKey::default()), 1)); let mut report = Vec::new(); - let valid = validate_voting_keys(&cip36, &mut report); + let valid = validate_voting_keys(&key_registration, true, &mut report); assert_eq!(report.len(), 0); assert!(valid); @@ -167,18 +232,18 @@ mod tests { #[test] fn test_validate_invalid_voting_keys() { - let mut cip36 = create_empty_cip36(true); - cip36 - .key_registration + let mut key_registration = Cip36KeyRegistration::default(); + + key_registration .voting_pks .push(VotingPubKey::new(Some(VerifyingKey::default()), 1)); - cip36 - .key_registration + + key_registration .voting_pks .push(VotingPubKey::new(Some(VerifyingKey::default()), 1)); let mut report = Vec::new(); - let valid = validate_voting_keys(&cip36, &mut report); + let valid = validate_voting_keys(&key_registration, true, &mut report); assert_eq!(report.len(), 1); assert!(report @@ -190,30 +255,32 @@ mod tests { #[test] fn test_validate_purpose() { - let cip36 = create_empty_cip36(true); + let key_registration = Cip36KeyRegistration::default(); let mut report = Vec::new(); - let valid = validate_purpose(&cip36, &mut report); + let valid = validate_purpose(&key_registration, true, &mut report); assert_eq!(report.len(), 0); - assert_eq!(cip36.purpose(), 0); + assert_eq!(key_registration.purpose, 0); assert!(valid); } #[test] fn test_validate_invalid_purpose() { - let mut cip36 = create_empty_cip36(true); - cip36.key_registration.purpose = 1; + let key_registration = Cip36KeyRegistration { + purpose: 1, + ..Default::default() + }; let mut report = Vec::new(); - let valid = validate_purpose(&cip36, &mut report); + let valid = validate_purpose(&key_registration, true, &mut report); assert_eq!(report.len(), 1); assert!(report .first() .expect("Failed to get the first index") .contains("unknown purpose")); - assert_eq!(cip36.purpose(), 1); + assert_eq!(key_registration.purpose, 1); assert!(!valid); } } From 0957f71269ff8f47633120680540667b8e781ff1 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Mon, 6 Jan 2025 08:48:54 +0700 Subject: [PATCH 15/17] fix(cardano-blockchain-types): cip36 validation implement getter Signed-off-by: bkioshn --- .../src/metadata/cip36/validation.rs | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/rust/cardano-blockchain-types/src/metadata/cip36/validation.rs b/rust/cardano-blockchain-types/src/metadata/cip36/validation.rs index 704d322988c..4a46fb24591 100644 --- a/rust/cardano-blockchain-types/src/metadata/cip36/validation.rs +++ b/rust/cardano-blockchain-types/src/metadata/cip36/validation.rs @@ -17,13 +17,35 @@ pub const SIGNDATA_PREAMBLE: [u8; 4] = [0xA1, 0x19, 0xEF, 0x64]; #[derive(Clone, Default, Debug)] pub struct Cip36Validation { /// Is the signature valid? (signature in 61285) - pub is_valid_signature: bool, + is_valid_signature: bool, /// Is the payment address on the correct network? - pub is_valid_payment_address_network: bool, + is_valid_payment_address_network: bool, /// Is the voting keys valid? - pub is_valid_voting_keys: bool, + is_valid_voting_keys: bool, /// Is the purpose valid? (Always 0 for Catalyst) - pub is_valid_purpose: bool, + is_valid_purpose: bool, +} + +impl Cip36Validation { + /// Is the signature from witness registration valid? + pub fn is_valid_signature(&self) -> bool { + self.is_valid_signature + } + + /// Is the payment address network valid? + pub fn is_valid_payment_address_network(&self) -> bool { + self.is_valid_payment_address_network + } + + /// Are the voting keys valid? + pub fn is_valid_voting_keys(&self) -> bool { + self.is_valid_voting_keys + } + + /// Is the purpose valid? + pub fn is_valid_purpose(&self) -> bool { + self.is_valid_purpose + } } /// Validation for CIP-36 From 3b1ae1be059deb3043f2517ce3deedc2eda6136c Mon Sep 17 00:00:00 2001 From: bkioshn Date: Mon, 6 Jan 2025 09:00:24 +0700 Subject: [PATCH 16/17] fix(cardano-blockchain-types): remove validation from cip36 struct Signed-off-by: bkioshn --- .../src/metadata/cip36/mod.rs | 14 ++--------- .../src/metadata/cip36/validation.rs | 25 ++----------------- 2 files changed, 4 insertions(+), 35 deletions(-) diff --git a/rust/cardano-blockchain-types/src/metadata/cip36/mod.rs b/rust/cardano-blockchain-types/src/metadata/cip36/mod.rs index dcdc4de6ad7..4a8df55afe2 100644 --- a/rust/cardano-blockchain-types/src/metadata/cip36/mod.rs +++ b/rust/cardano-blockchain-types/src/metadata/cip36/mod.rs @@ -11,7 +11,7 @@ use key_registration::Cip36KeyRegistration; use minicbor::{Decode, Decoder}; use pallas::ledger::addresses::ShelleyAddress; use registration_witness::Cip36RegistrationWitness; -use validation::{validate_cip36, Cip36Validation}; +use validation::validate_cip36; use voting_pk::VotingPubKey; use crate::{MetadatumLabel, MultiEraBlock, TxnIndex}; @@ -25,9 +25,6 @@ pub struct Cip36 { registration_witness: Cip36RegistrationWitness, /// Is this a Catalyst strict registration? is_catalyst_strict: bool, - /// CIP36 validation. - #[allow(dead_code)] - validation: Cip36Validation, } impl Cip36 { @@ -101,14 +98,13 @@ impl Cip36 { key_registration, registration_witness, is_catalyst_strict, - validation, }; if validation_report.is_empty() { Ok(cip36) } else { // If there are validation errors, the CIP36 is invalid - bail!("CIP-36 validation failed: {cip36:?}, Reports: {validation_report:?}") + bail!("CIP-36 validation failed: {cip36:?}, Validation: {validation:?}, Reports: {validation_report:?}") } } @@ -172,10 +168,4 @@ impl Cip36 { pub fn is_strict_catalyst(&self) -> bool { self.is_catalyst_strict } - - /// Get the CIP-36 validation. - #[must_use] - pub fn validation(&self) -> &Cip36Validation { - &self.validation - } } diff --git a/rust/cardano-blockchain-types/src/metadata/cip36/validation.rs b/rust/cardano-blockchain-types/src/metadata/cip36/validation.rs index 4a46fb24591..22a5d90500a 100644 --- a/rust/cardano-blockchain-types/src/metadata/cip36/validation.rs +++ b/rust/cardano-blockchain-types/src/metadata/cip36/validation.rs @@ -15,7 +15,8 @@ pub const SIGNDATA_PREAMBLE: [u8; 4] = [0xA1, 0x19, 0xEF, 0x64]; /// Validation value for CIP-36. #[allow(clippy::struct_excessive_bools, clippy::module_name_repetitions)] #[derive(Clone, Default, Debug)] -pub struct Cip36Validation { +#[allow(dead_code)] +pub(crate) struct Cip36Validation { /// Is the signature valid? (signature in 61285) is_valid_signature: bool, /// Is the payment address on the correct network? @@ -26,28 +27,6 @@ pub struct Cip36Validation { is_valid_purpose: bool, } -impl Cip36Validation { - /// Is the signature from witness registration valid? - pub fn is_valid_signature(&self) -> bool { - self.is_valid_signature - } - - /// Is the payment address network valid? - pub fn is_valid_payment_address_network(&self) -> bool { - self.is_valid_payment_address_network - } - - /// Are the voting keys valid? - pub fn is_valid_voting_keys(&self) -> bool { - self.is_valid_voting_keys - } - - /// Is the purpose valid? - pub fn is_valid_purpose(&self) -> bool { - self.is_valid_purpose - } -} - /// Validation for CIP-36 /// The validation include the following: /// * Signature validation of the registration witness 61285 against the stake public key From 7543a5dec89020a4bdd96c64aff35187c1a10608 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Mon, 6 Jan 2025 09:55:59 +0700 Subject: [PATCH 17/17] fix(cardano-blockchain-types): improve verifying key error log Signed-off-by: bkioshn --- .../src/metadata/cip36/key_registration.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/rust/cardano-blockchain-types/src/metadata/cip36/key_registration.rs b/rust/cardano-blockchain-types/src/metadata/cip36/key_registration.rs index a3171d919f8..b6e47ff52ed 100644 --- a/rust/cardano-blockchain-types/src/metadata/cip36/key_registration.rs +++ b/rust/cardano-blockchain-types/src/metadata/cip36/key_registration.rs @@ -302,8 +302,12 @@ fn decode_voting_key( /// Helper function for converting `&[u8]` to `VerifyingKey`. fn voting_pk_vec_to_verifying_key(pub_key: &[u8]) -> anyhow::Result { - let bytes = pub_key.try_into().context("Invalid verifying key length")?; - VerifyingKey::from_bytes(bytes).context("Failed to convert to VerifyingKey") + let bytes = pub_key.try_into().context(format!( + "Invalid verifying key length got {}", + pub_key.len() + ))?; + VerifyingKey::from_bytes(bytes) + .map_err(|e| anyhow::anyhow!("Failed to convert to VerifyingKey: {:?}", e)) } /// Helper function for decoding the stake public key.