From 534e3f5f6044131be930b358ec22ba17d967a3aa Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Fri, 7 Nov 2025 16:56:19 +0700 Subject: [PATCH 01/30] wip --- .../src/cardano/cip509/cip509.rs | 44 ++- rust/rbac-registration/src/providers.rs | 15 +- .../src/registration/cardano/mod.rs | 327 +++++++----------- 3 files changed, 171 insertions(+), 215 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/cip509.rs b/rust/rbac-registration/src/cardano/cip509/cip509.rs index 18d21213ff3..0dbaeaae592 100644 --- a/rust/rbac-registration/src/cardano/cip509/cip509.rs +++ b/rust/rbac-registration/src/cardano/cip509/cip509.rs @@ -22,6 +22,7 @@ use catalyst_types::{ uuid::UuidV4, }; use cbork_utils::decode_helper::{decode_bytes, decode_helper, decode_map_len}; +use ed25519_dalek::VerifyingKey; use minicbor::{ decode::{self}, Decode, Decoder, @@ -32,6 +33,7 @@ use uuid::Uuid; use crate::cardano::cip509::{ decode_context::DecodeContext, + extract_key, rbac::Cip509RbacMetadata, types::{PaymentHistory, TxInputHash, ValidationSignature}, utils::Cip0134UriSet, @@ -40,7 +42,7 @@ use crate::cardano::cip509::{ validate_txn_inputs_hash, }, x509_chunks::X509Chunks, - Payment, PointTxnIdx, RoleData, + C509Cert, LocalRefInt, Payment, PointTxnIdx, RoleData, SimplePublicKeyType, X509DerCert, }; /// A x509 metadata envelope. @@ -227,6 +229,46 @@ impl Cip509 { self.metadata.as_ref().and_then(|m| m.role_data.get(&role)) } + /// Returns signing public key for a role. + #[must_use] + pub fn signing_pk_for_role( + &self, + role: RoleId, + ) -> Option { + self.metadata.as_ref().and_then(|m| { + let key_ref = m.role_data.get(&role).and_then(|d| d.signing_key())?; + match key_ref.local_ref { + LocalRefInt::X509Certs => { + m.x509_certs.get(key_ref.key_offset).and_then(|c| { + if let X509DerCert::X509Cert(c) = c { + extract_key::x509_key(&c).ok() + } else { + None + } + }) + }, + LocalRefInt::C509Certs => { + m.c509_certs.get(key_ref.key_offset).and_then(|c| { + if let C509Cert::C509Certificate(c) = c { + extract_key::c509_key(&c).ok() + } else { + None + } + }) + }, + LocalRefInt::PubKeys => { + m.pub_keys.get(key_ref.key_offset).and_then(|c| { + if let SimplePublicKeyType::Ed25519(c) = c { + Some(c.clone()) + } else { + None + } + }) + }, + } + }) + } + /// Returns a purpose of this registration. #[must_use] pub fn purpose(&self) -> Option { diff --git a/rust/rbac-registration/src/providers.rs b/rust/rbac-registration/src/providers.rs index 271f7ccd75c..754a1f8d191 100644 --- a/rust/rbac-registration/src/providers.rs +++ b/rust/rbac-registration/src/providers.rs @@ -14,7 +14,7 @@ pub trait RbacRegistrationProvider { /// for the given Catalyst ID. fn chain( &self, - id: CatalystId, + id: &CatalystId, ) -> impl Future>> + Send; /// Returns `true` if a chain with the given Catalyst ID already exists. @@ -24,24 +24,25 @@ pub trait RbacRegistrationProvider { /// chain. fn is_chain_known( &self, - id: CatalystId, + id: &CatalystId, ) -> impl Future> + Send; - /// Returns a Catalyst ID corresponding to the given stake address. - fn catalyst_id_from_stake_address( + /// Returns a current valid registration chain corresponding to the given stake + /// address. + fn chain_from_stake_address( &self, address: &StakeAddress, - ) -> impl Future>> + Send; + ) -> impl Future>> + Send; /// Returns a Catalyst ID corresponding to the given public key. fn catalyst_id_from_public_key( &self, - key: VerifyingKey, + key: &VerifyingKey, ) -> impl Future>> + Send; /// Returns a Catalyst ID corresponding to the given transaction hash. fn catalyst_id_from_txn_id( &self, - txn_id: TransactionId, + txn_id: &TransactionId, ) -> impl Future>> + Send; } diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs index 4c9d1df1721..2796ef3ed99 100644 --- a/rust/rbac-registration/src/registration/cardano/mod.rs +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -40,20 +40,124 @@ pub struct RegistrationChain { } impl RegistrationChain { - /// Create a new instance of registration chain. - /// The first new value should be the chain root. + /// Attempts to initialize a new RBAC registration chain + /// from a given CIP-509 registration, ensuring uniqueness of Catalyst ID, stake + /// addresses, and associated public keys. /// - /// # Arguments - /// - `cip509` - The CIP509. - #[must_use] - pub fn new_stateless(cip509: Cip509) -> Option { - let inner = RegistrationChainInner::new_stateless(cip509)?; + /// # Errors + /// - Propagates any I/O or provider-level errors encountered while checking key + /// ownership (e.g., database lookup failures). + pub async fn new( + reg: Cip509, + provider: &Provider, + ) -> anyhow::Result> + where + Provider: RbacRegistrationProvider, + { + // Validate stake addresses. + let new_addresses = reg.stake_addresses(); + for _address in &new_addresses { + // TODO: The new root registration "takes" an address(es) from the + // existing chain, so that chain needs to be + // updated. + } - Some(Self { - inner: Arc::new(inner), - }) + // Verify that a Catalyst ID of this chain is unique. + // An absense of the Catalyst ID already handled and processed by the + // `RegistrationChainInner::new`. + if let Some(cat_id) = reg.catalyst_id() { + if provider.is_chain_known(cat_id).await? { + reg.report().functional_validation( + &format!("{} is already used", cat_id.as_short_id()), + "It isn't allowed to use same Catalyst ID (certificate subject public key) in multiple registration chains", + ); + } + + for role in reg.all_roles() { + if let Some(key) = reg.signing_pk_for_role(role) { + if let Some(previous) = provider.catalyst_id_from_public_key(&key).await? { + if &previous != cat_id { + reg.report().functional_validation( + &format!("An update to {cat_id} registration chain uses the same public key ({key:?}) as {previous} chain"), + "It isn't allowed to use role 0 signing (certificate subject public) key in different chains", + ); + } + } + } + } + } + + // Try to start a new chain. + let Some(new_chain) = RegistrationChainInner::new(reg) else { + return Ok(None); + }; + + Ok(Some(Self { + inner: new_chain.into(), + })) } + // /// Attempts to update an existing RBAC registration chain + // /// with a new CIP-509 registration, validating address and key usage consistency. + // /// + // /// # Errors + // /// - Propagates any I/O or provider-level errors encountered while checking key + // /// ownership (e.g., database lookup failures). + // pub async fn update( + // &self, + // reg: Cip509, + // provider: &Provider, + // ) -> anyhow::Result> + // where + // Provider: RbacRegistrationProvider, + // { + // // Check that addresses from the new registration aren't used in other chains. + // let previous_addresses = self.stake_addresses(); + // let reg_addresses = reg.stake_addresses(); + // let new_addresses: Vec<_> = + // reg_addresses.difference(&previous_addresses).collect(); for address in + // &new_addresses { match provider.chain_from_stake_address(address).await? { + // None => { + // // All good: the address wasn't used before. + // }, + // Some(_) => { + // reg.report().functional_validation( + // &format!("{address} stake addresses is already used"), + // "It isn't allowed to use same stake address in multiple + // registration chains", ); + // }, + // } + // } + + // let latest_signing_pk = self.get_latest_signing_pk_for_role(RoleId::Role0); + // let new_inner = if let Some((signing_pk, _)) = latest_signing_pk { + // self.inner.update_stateless(reg, signing_pk)? + // } else { + // reg.report().missing_field( + // "latest signing key for role 0", + // "cannot perform signature validation during Registration Chain update", + // ); + // return None; + // }; + + // // Try to add a new registration to the chain. + // let (signing_pk, _) = self.get_latest_signing_pk_for_role(RoleId::Role0)?; + // let new_chain = chain.inner.update_stateless(reg.clone(), signing_pk)?; + + // // Check that new public keys aren't used by other chains. + // new_chain + // .validate_public_keys(&report, provider) + // .await + // .ok()?; + + // // Return an error if any issues were recorded in the report. + // if report.is_problematic() { + // return None; + // } + + // Some(new_chain) + // } + /// Update the registration chain. /// /// # Arguments @@ -78,29 +182,6 @@ impl RegistrationChain { }) } - /// Creates or updates an RBAC registration chain from a CIP-509 registration. - /// - /// If the given registration references a previous transaction, it attempts - /// to update the existing chain using that previous transaction. - /// Otherwise, it starts a new chain from the provided registration. - pub async fn update( - &self, - reg: Cip509, - provider: &Provider, - ) -> Option - where - Provider: RbacRegistrationProvider, - { - let new_inner = if reg.previous_transaction().is_some() { - self.inner.update(reg, provider).await? - } else { - RegistrationChainInner::new(reg, provider).await? - }; - Some(Self { - inner: Arc::new(new_inner), - }) - } - /// Returns a Catalyst ID. #[must_use] pub fn catalyst_id(&self) -> &CatalystId { @@ -305,7 +386,7 @@ impl RegistrationChainInner { /// # Arguments /// - `cip509` - The CIP509. #[must_use] - fn new_stateless(cip509: Cip509) -> Option { + fn new(cip509: Cip509) -> Option { let context = "Registration Chain new"; // Should be chain root, return immediately if not if cip509.previous_transaction().is_some() { @@ -489,177 +570,6 @@ impl RegistrationChainInner { Some(new_inner) } - /// Attempts to initialize a new RBAC registration chain - /// from a given CIP-509 registration, ensuring uniqueness of Catalyst ID, stake - /// addresses, and associated public keys. - pub async fn new( - reg: Cip509, - provider: &Provider, - ) -> Option - where - Provider: RbacRegistrationProvider, - { - let report = reg.report().to_owned(); - - // Try to start a new chain. - let new_chain = Self::new_stateless(reg)?; - // Verify that a Catalyst ID of this chain is unique. - let catalyst_id = new_chain.catalyst_id.as_short_id(); - if provider.is_chain_known(catalyst_id.clone()).await.ok()? { - report.functional_validation( - &format!("{catalyst_id} is already used"), - "It isn't allowed to use same Catalyst ID (certificate subject public key) in multiple registration chains", - ); - return None; - } - - // Validate stake addresses. - let new_addresses = new_chain.certificate_uris.stake_addresses(); - let mut updated_chains: HashMap<_, HashSet> = HashMap::new(); - for address in &new_addresses { - if let Some(id) = provider - .catalyst_id_from_stake_address(address) - .await - .ok()? - { - // If an address is used in existing chain then a new chain must have different role - // 0 signing key. - let previous_chain = provider.chain(id.clone()).await.ok()??; - if previous_chain.get_latest_signing_pk_for_role(RoleId::Role0) - == new_chain.get_latest_signing_pk_for_role(RoleId::Role0) - { - report.functional_validation( - &format!("A new registration ({catalyst_id}) uses the same public key as the previous one ({})", - previous_chain.catalyst_id().as_short_id() - ), - "It is only allowed to override the existing chain by using different public key", - ); - } else { - // The new root registration "takes" an address(es) from the existing chain, so - // that chain needs to be updated. - updated_chains - .entry(id) - .and_modify(|e| { - e.insert(address.clone()); - }) - .or_insert([address.clone()].into_iter().collect()); - } - } - } - - // Check that new public keys aren't used by other chains. - new_chain - .validate_public_keys(&report, provider) - .await - .ok()?; - - if report.is_problematic() { - return None; - } - - Some(new_chain) - } - - /// Attempts to update an existing RBAC registration chain - /// with a new CIP-509 registration, validating address and key usage consistency. - pub async fn update( - &self, - reg: Cip509, - provider: &Provider, - ) -> Option - where - Provider: RbacRegistrationProvider, - { - let previous_txn = reg.previous_transaction()?; - let report = reg.report().to_owned(); - - // Find a chain this registration belongs to. - let Some(catalyst_id) = provider.catalyst_id_from_txn_id(previous_txn).await.ok()? else { - // We are unable to determine a Catalyst ID, so there is no sense to update the problem - // report because we would be unable to store this registration anyway. - return None; - }; - let chain = provider.chain(catalyst_id.clone()).await.ok()??; - - // Check that addresses from the new registration aren't used in other chains. - let previous_addresses = chain.stake_addresses(); - let reg_addresses = reg.stake_addresses(); - let new_addresses: Vec<_> = reg_addresses.difference(&previous_addresses).collect(); - for address in &new_addresses { - match provider - .catalyst_id_from_stake_address(address) - .await - .ok()? - { - None => { - // All good: the address wasn't used before. - }, - Some(_) => { - report.functional_validation( - &format!("{address} stake addresses is already used"), - "It isn't allowed to use same stake address in multiple registration chains", - ); - }, - } - } - - // Try to add a new registration to the chain. - let (signing_pk, _) = self.get_latest_signing_pk_for_role(RoleId::Role0)?; - let new_chain = chain.inner.update_stateless(reg.clone(), signing_pk)?; - - // Check that new public keys aren't used by other chains. - new_chain - .validate_public_keys(&report, provider) - .await - .ok()?; - - // Return an error if any issues were recorded in the report. - if report.is_problematic() { - return None; - } - - Some(new_chain) - } - - /// Validates that none of the signing keys in a given RBAC registration chain - /// have been used by any other existing chain, ensuring global key uniqueness - /// across all Catalyst registrations. - /// - /// # Returns - /// Returns `Ok(true)` if all signing keys are unique and validation passes - /// successfully. Returns `Ok(false)` if any key conflict is detected, with the - /// issue recorded in the provided [`ProblemReport`]. - /// - /// # Errors - /// - Propagates any I/O or provider-level errors encountered while checking key - /// ownership (e.g., database lookup failures). - async fn validate_public_keys( - &self, - report: &ProblemReport, - provider: &Provider, - ) -> anyhow::Result<()> - where - Provider: RbacRegistrationProvider, - { - let roles: Vec<_> = self.role_data_history.keys().collect(); - let catalyst_id = self.catalyst_id.as_short_id(); - - for role in roles { - if let Some((key, _)) = self.get_latest_signing_pk_for_role(*role) { - if let Some(previous) = provider.catalyst_id_from_public_key(key).await? { - if previous != catalyst_id { - report.functional_validation( - &format!("An update to {catalyst_id} registration chain uses the same public key ({key:?}) as {previous} chain"), - "It isn't allowed to use role 0 signing (certificate subject public) key in different chains", - ); - } - } - } - } - - Ok(()) - } - /// Get the latest signing public key for a role. /// Returns the public key and the rotation,`None` if not found. #[must_use] @@ -684,7 +594,7 @@ impl RegistrationChainInner { role: RoleId, ) -> Option<(VerifyingKey, KeyRotation)> { self.role_data_record.get(&role).and_then(|rdr| { - rdr.encryption_keys().last().and_then(|key| { + rdr.encryption_keys().last().and_then(|key| { let rotation = KeyRotation::from_latest_rotation(rdr.encryption_keys()); key.data().extract_pk().map(|pk| (pk, rotation)) @@ -749,8 +659,11 @@ mod test { .unwrap(); data.assert_valid(®istration); + // Performs only stateless validations // Create a chain with the first registration. - let chain = RegistrationChain::new_stateless(registration).unwrap(); + let chain = RegistrationChainInner::new(registration) + .map(|r| RegistrationChain { inner: r.into() }) + .unwrap(); assert_eq!(chain.purpose(), &[data.purpose]); assert_eq!(1, chain.x509_certs().len()); let origin = &chain.x509_certs().get(&0).unwrap().first().unwrap(); From 3a7ae9b7e781f685eca7edd83fd8ecf9c5533711 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Fri, 7 Nov 2025 17:11:04 +0700 Subject: [PATCH 02/30] wip --- .../src/registration/cardano/mod.rs | 130 +++++++++--------- 1 file changed, 67 insertions(+), 63 deletions(-) diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs index 2796ef3ed99..01a57fc9822 100644 --- a/rust/rbac-registration/src/registration/cardano/mod.rs +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -73,6 +73,8 @@ impl RegistrationChain { ); } + // Checks that a new registration doesn't contain a signing key that was used by any + // other chain. Returns a list of public keys in the registration. for role in reg.all_roles() { if let Some(key) = reg.signing_pk_for_role(role) { if let Some(previous) = provider.catalyst_id_from_public_key(&key).await? { @@ -97,66 +99,68 @@ impl RegistrationChain { })) } - // /// Attempts to update an existing RBAC registration chain - // /// with a new CIP-509 registration, validating address and key usage consistency. - // /// - // /// # Errors - // /// - Propagates any I/O or provider-level errors encountered while checking key - // /// ownership (e.g., database lookup failures). - // pub async fn update( - // &self, - // reg: Cip509, - // provider: &Provider, - // ) -> anyhow::Result> - // where - // Provider: RbacRegistrationProvider, - // { - // // Check that addresses from the new registration aren't used in other chains. - // let previous_addresses = self.stake_addresses(); - // let reg_addresses = reg.stake_addresses(); - // let new_addresses: Vec<_> = - // reg_addresses.difference(&previous_addresses).collect(); for address in - // &new_addresses { match provider.chain_from_stake_address(address).await? { - // None => { - // // All good: the address wasn't used before. - // }, - // Some(_) => { - // reg.report().functional_validation( - // &format!("{address} stake addresses is already used"), - // "It isn't allowed to use same stake address in multiple - // registration chains", ); - // }, - // } - // } - - // let latest_signing_pk = self.get_latest_signing_pk_for_role(RoleId::Role0); - // let new_inner = if let Some((signing_pk, _)) = latest_signing_pk { - // self.inner.update_stateless(reg, signing_pk)? - // } else { - // reg.report().missing_field( - // "latest signing key for role 0", - // "cannot perform signature validation during Registration Chain update", - // ); - // return None; - // }; - - // // Try to add a new registration to the chain. - // let (signing_pk, _) = self.get_latest_signing_pk_for_role(RoleId::Role0)?; - // let new_chain = chain.inner.update_stateless(reg.clone(), signing_pk)?; - - // // Check that new public keys aren't used by other chains. - // new_chain - // .validate_public_keys(&report, provider) - // .await - // .ok()?; - - // // Return an error if any issues were recorded in the report. - // if report.is_problematic() { - // return None; - // } - - // Some(new_chain) - // } + /// Attempts to update an existing RBAC registration chain + /// with a new CIP-509 registration, validating address and key usage consistency. + /// + /// # Errors + /// - Propagates any I/O or provider-level errors encountered while checking key + /// ownership (e.g., database lookup failures). + pub async fn update( + &self, + reg: Cip509, + provider: &Provider, + ) -> anyhow::Result> + where + Provider: RbacRegistrationProvider, + { + // Check that addresses from the new registration aren't used in other chains. + let previous_addresses = self.stake_addresses(); + let reg_addresses = reg.stake_addresses(); + let new_addresses: Vec<_> = reg_addresses.difference(&previous_addresses).collect(); + for address in &new_addresses { + if provider.chain_from_stake_address(address).await?.is_some() { + reg.report().functional_validation( + &format!("{address} stake addresses is already used"), + "It isn't allowed to use same stake address in multiple registration chains, if its not a new chain", + ); + } + } + + // Checks that a new registration doesn't contain a signing key that was used by any + // other chain. Returns a list of public keys in the registration. + { + let cat_id = self.catalyst_id(); + for role in reg.all_roles() { + if let Some(key) = reg.signing_pk_for_role(role) { + if let Some(previous) = provider.catalyst_id_from_public_key(&key).await? { + if &previous != cat_id { + reg.report().functional_validation( + &format!("An update to {cat_id} registration chain uses the same public key ({key:?}) as {previous} chain"), + "It isn't allowed to use role 0 signing (certificate subject public) key in different chains", + ); + } + } + } + } + } + + let Some((latest_signing_pk, _)) = self.get_latest_signing_pk_for_role(RoleId::Role0) + else { + reg.report().missing_field( + "latest signing key for role 0", + "cannot perform signature validation during Registration Chain update", + ); + return Ok(None); + }; + + let Some(new_inner) = self.inner.update(reg, latest_signing_pk) else { + return Ok(None); + }; + + Ok(Some(Self { + inner: new_inner.into(), + })) + } /// Update the registration chain. /// @@ -169,7 +173,7 @@ impl RegistrationChain { ) -> Option { let latest_signing_pk = self.get_latest_signing_pk_for_role(RoleId::Role0); let new_inner = if let Some((signing_pk, _)) = latest_signing_pk { - self.inner.update_stateless(cip509, signing_pk)? + self.inner.update(cip509, signing_pk)? } else { cip509.report().missing_field( "latest signing key for role 0", @@ -488,7 +492,7 @@ impl RegistrationChainInner { /// # Arguments /// - `cip509` - The CIP509. #[must_use] - fn update_stateless( + fn update( &self, cip509: Cip509, signing_pk: VerifyingKey, @@ -594,7 +598,7 @@ impl RegistrationChainInner { role: RoleId, ) -> Option<(VerifyingKey, KeyRotation)> { self.role_data_record.get(&role).and_then(|rdr| { - rdr.encryption_keys().last().and_then(|key| { + rdr.encryption_keys().last().and_then(|key| { let rotation = KeyRotation::from_latest_rotation(rdr.encryption_keys()); key.data().extract_pk().map(|pk| (pk, rotation)) From ae05abd7ef9b849474668ca02fdab8d7efbce437 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 10 Nov 2025 15:56:55 +0700 Subject: [PATCH 03/30] wip --- .../src/cardano/cip509/cip509.rs | 68 +------- .../src/cardano/cip509/validation.rs | 8 +- .../src/registration/cardano/mod.rs | 153 ++++++++++-------- rust/rbac-registration/src/utils/test.rs | 3 +- 4 files changed, 99 insertions(+), 133 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/cip509.rs b/rust/rbac-registration/src/cardano/cip509/cip509.rs index 0dbaeaae592..b66aa01f7ac 100644 --- a/rust/rbac-registration/src/cardano/cip509/cip509.rs +++ b/rust/rbac-registration/src/cardano/cip509/cip509.rs @@ -22,7 +22,6 @@ use catalyst_types::{ uuid::UuidV4, }; use cbork_utils::decode_helper::{decode_bytes, decode_helper, decode_map_len}; -use ed25519_dalek::VerifyingKey; use minicbor::{ decode::{self}, Decode, Decoder, @@ -33,7 +32,6 @@ use uuid::Uuid; use crate::cardano::cip509::{ decode_context::DecodeContext, - extract_key, rbac::Cip509RbacMetadata, types::{PaymentHistory, TxInputHash, ValidationSignature}, utils::Cip0134UriSet, @@ -42,7 +40,7 @@ use crate::cardano::cip509::{ validate_txn_inputs_hash, }, x509_chunks::X509Chunks, - C509Cert, LocalRefInt, Payment, PointTxnIdx, RoleData, SimplePublicKeyType, X509DerCert, + Payment, PointTxnIdx, RoleData, }; /// A x509 metadata envelope. @@ -229,46 +227,6 @@ impl Cip509 { self.metadata.as_ref().and_then(|m| m.role_data.get(&role)) } - /// Returns signing public key for a role. - #[must_use] - pub fn signing_pk_for_role( - &self, - role: RoleId, - ) -> Option { - self.metadata.as_ref().and_then(|m| { - let key_ref = m.role_data.get(&role).and_then(|d| d.signing_key())?; - match key_ref.local_ref { - LocalRefInt::X509Certs => { - m.x509_certs.get(key_ref.key_offset).and_then(|c| { - if let X509DerCert::X509Cert(c) = c { - extract_key::x509_key(&c).ok() - } else { - None - } - }) - }, - LocalRefInt::C509Certs => { - m.c509_certs.get(key_ref.key_offset).and_then(|c| { - if let C509Cert::C509Certificate(c) = c { - extract_key::c509_key(&c).ok() - } else { - None - } - }) - }, - LocalRefInt::PubKeys => { - m.pub_keys.get(key_ref.key_offset).and_then(|c| { - if let SimplePublicKeyType::Ed25519(c) = c { - Some(c.clone()) - } else { - None - } - }) - }, - } - }) - } - /// Returns a purpose of this registration. #[must_use] pub fn purpose(&self) -> Option { @@ -355,26 +313,10 @@ impl Cip509 { .unwrap_or_default() } - /// Returns `Cip509` fields consuming the structure if it was successfully decoded and - /// validated otherwise return the problem report that contains all the encountered - /// issues. - /// - /// # Errors - /// - /// - `Err(ProblemReport)` - pub fn consume(self) -> Result<(UuidV4, Cip509RbacMetadata, PaymentHistory), ProblemReport> { - match ( - self.purpose, - self.txn_inputs_hash, - self.metadata, - self.validation_signature, - ) { - (Some(purpose), Some(_), Some(metadata), Some(_)) if !self.report.is_problematic() => { - Ok((purpose, metadata, self.payment_history)) - }, - - _ => Err(self.report), - } + /// Returns a payment history map. + #[must_use] + pub fn payment_history(&self) -> &PaymentHistory { + &self.payment_history } } diff --git a/rust/rbac-registration/src/cardano/cip509/validation.rs b/rust/rbac-registration/src/cardano/cip509/validation.rs index 158a889fefd..1502c7f4816 100644 --- a/rust/rbac-registration/src/cardano/cip509/validation.rs +++ b/rust/rbac-registration/src/cardano/cip509/validation.rs @@ -593,8 +593,7 @@ mod tests { assert_eq!(origin.txn_index(), data.txn_index); assert_eq!(origin.point().slot_or_default(), data.slot); - // The consume function must return the problem report contained within the registration. - let report = registration.consume().unwrap_err(); + let report = registration.report(); assert!(report.is_problematic()); let report = format!("{report:?}"); assert!(report.contains("is not present in the transaction witness set, and can not be verified as owned and spendable")); @@ -616,7 +615,7 @@ mod tests { assert_eq!(origin.txn_index(), data.txn_index); assert_eq!(origin.point().slot_or_default(), data.slot); - let report = registration.consume().unwrap_err(); + let report = registration.report(); assert!(report.is_problematic()); let report = format!("{report:?}"); assert!(report @@ -637,8 +636,7 @@ mod tests { assert_eq!(origin.txn_index(), data.txn_index); assert_eq!(origin.point().slot_or_default(), data.slot); - // The consume function must return the problem report contained within the registration. - let report = registration.consume().unwrap_err(); + let report = registration.report(); assert!(report.is_problematic()); let report = format!("{report:?}"); assert!(report.contains("Unknown role found: 4")); diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs index 01a57fc9822..7fd7b497f19 100644 --- a/rust/rbac-registration/src/registration/cardano/mod.rs +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -48,14 +48,18 @@ impl RegistrationChain { /// - Propagates any I/O or provider-level errors encountered while checking key /// ownership (e.g., database lookup failures). pub async fn new( - reg: Cip509, + cip509: &Cip509, provider: &Provider, ) -> anyhow::Result> where Provider: RbacRegistrationProvider, { + let Some(new_chain) = Self::new_stateless(cip509) else { + return Ok(None); + }; + // Validate stake addresses. - let new_addresses = reg.stake_addresses(); + let new_addresses = cip509.stake_addresses(); for _address in &new_addresses { // TODO: The new root registration "takes" an address(es) from the // existing chain, so that chain needs to be @@ -63,11 +67,10 @@ impl RegistrationChain { } // Verify that a Catalyst ID of this chain is unique. - // An absense of the Catalyst ID already handled and processed by the - // `RegistrationChainInner::new`. - if let Some(cat_id) = reg.catalyst_id() { + { + let cat_id = new_chain.catalyst_id(); if provider.is_chain_known(cat_id).await? { - reg.report().functional_validation( + cip509.report().functional_validation( &format!("{} is already used", cat_id.as_short_id()), "It isn't allowed to use same Catalyst ID (certificate subject public key) in multiple registration chains", ); @@ -75,11 +78,11 @@ impl RegistrationChain { // Checks that a new registration doesn't contain a signing key that was used by any // other chain. Returns a list of public keys in the registration. - for role in reg.all_roles() { - if let Some(key) = reg.signing_pk_for_role(role) { + for role in cip509.all_roles() { + if let Some((key, _)) = new_chain.get_latest_signing_pk_for_role(role) { if let Some(previous) = provider.catalyst_id_from_public_key(&key).await? { if &previous != cat_id { - reg.report().functional_validation( + cip509.report().functional_validation( &format!("An update to {cat_id} registration chain uses the same public key ({key:?}) as {previous} chain"), "It isn't allowed to use role 0 signing (certificate subject public) key in different chains", ); @@ -89,14 +92,22 @@ impl RegistrationChain { } } - // Try to start a new chain. - let Some(new_chain) = RegistrationChainInner::new(reg) else { - return Ok(None); - }; + if reg.report().is_problematic() { + Ok(None) + } else { + Ok(Some(new_chain)) + } + } + + /// Create a new instance of registration chain. + /// The first new value should be the chain root. + #[must_use] + pub fn new_stateless(cip509: &Cip509) -> Option { + let inner = RegistrationChainInner::new(cip509)?; - Ok(Some(Self { - inner: new_chain.into(), - })) + Some(Self { + inner: Arc::new(inner), + }) } /// Attempts to update an existing RBAC registration chain @@ -107,12 +118,16 @@ impl RegistrationChain { /// ownership (e.g., database lookup failures). pub async fn update( &self, - reg: Cip509, + reg: &Cip509, provider: &Provider, ) -> anyhow::Result> where Provider: RbacRegistrationProvider, { + let Some(new_chain) = self.update_stateless(reg) else { + return Ok(None); + }; + // Check that addresses from the new registration aren't used in other chains. let previous_addresses = self.stake_addresses(); let reg_addresses = reg.stake_addresses(); @@ -131,7 +146,7 @@ impl RegistrationChain { { let cat_id = self.catalyst_id(); for role in reg.all_roles() { - if let Some(key) = reg.signing_pk_for_role(role) { + if let Some((key, _)) = new_chain.get_latest_signing_pk_for_role(role) { if let Some(previous) = provider.catalyst_id_from_public_key(&key).await? { if &previous != cat_id { reg.report().functional_validation( @@ -144,22 +159,11 @@ impl RegistrationChain { } } - let Some((latest_signing_pk, _)) = self.get_latest_signing_pk_for_role(RoleId::Role0) - else { - reg.report().missing_field( - "latest signing key for role 0", - "cannot perform signature validation during Registration Chain update", - ); - return Ok(None); - }; - - let Some(new_inner) = self.inner.update(reg, latest_signing_pk) else { - return Ok(None); - }; - - Ok(Some(Self { - inner: new_inner.into(), - })) + if reg.report().is_problematic() { + Ok(None) + } else { + Ok(Some(new_chain)) + } } /// Update the registration chain. @@ -169,7 +173,7 @@ impl RegistrationChain { #[must_use] pub fn update_stateless( &self, - cip509: Cip509, + cip509: &Cip509, ) -> Option { let latest_signing_pk = self.get_latest_signing_pk_for_role(RoleId::Role0); let new_inner = if let Some((signing_pk, _)) = latest_signing_pk { @@ -390,7 +394,7 @@ impl RegistrationChainInner { /// # Arguments /// - `cip509` - The CIP509. #[must_use] - fn new(cip509: Cip509) -> Option { + fn new(cip509: &Cip509) -> Option { let context = "Registration Chain new"; // Should be chain root, return immediately if not if cip509.previous_transaction().is_some() { @@ -403,23 +407,21 @@ impl RegistrationChainInner { return None; }; - let point_tx_idx = cip509.origin().clone(); - let current_tx_id_hash = PointData::new(point_tx_idx.clone(), cip509.txn_hash()); - let validation_signature = cip509.validation_signature().cloned(); - let raw_aux_data = cip509.raw_aux_data().to_vec(); + let Some(registration) = cip509.metadata().cloned() else { + cip509.report().missing_field("metadata", context); + return None; + }; // Role data let mut role_data_history = HashMap::new(); let mut role_data_record = HashMap::new(); - - if let Some(registration) = cip509.metadata() { - update_role_data( - registration, - &mut role_data_history, - &mut role_data_record, - &point_tx_idx, - ); - } + let point_tx_idx = cip509.origin().clone(); + update_role_data( + ®istration, + &mut role_data_history, + &mut role_data_record, + &point_tx_idx, + ); // There should be role 0 since we already check that the chain root (no previous tx id) // must contain role 0 @@ -439,17 +441,26 @@ impl RegistrationChainInner { }; check_validation_signature( - validation_signature, - &raw_aux_data, + cip509.validation_signature(), + &cip509.raw_aux_data(), signing_pk, cip509.report(), context, ); - let Ok((purpose, registration, payment_history)) = cip509.consume() else { + if cip509.txn_inputs_hash().is_none() { + cip509.report().missing_field("txn inputs hash", context); + }; + + let Some(purpose) = cip509.purpose() else { + cip509.report().missing_field("purpose", context); return None; }; + if cip509.report().is_problematic() { + return None; + } + let purpose = vec![purpose]; let certificate_uris = registration.certificate_uris.clone(); let mut x509_certs = HashMap::new(); @@ -471,6 +482,8 @@ impl RegistrationChainInner { &point_tx_idx, ); let revocations = revocations_list(registration.revocation_list.clone(), &point_tx_idx); + let current_tx_id_hash = PointData::new(point_tx_idx, cip509.txn_hash()); + let payment_history = cip509.payment_history().clone(); Some(Self { catalyst_id, @@ -494,7 +507,7 @@ impl RegistrationChainInner { #[must_use] fn update( &self, - cip509: Cip509, + cip509: &Cip509, signing_pk: VerifyingKey, ) -> Option { let context = "Registration Chain update"; @@ -512,7 +525,7 @@ impl RegistrationChainInner { // Perform signature validation // This should be done before updating the signing key check_validation_signature( - cip509.validation_signature().cloned(), + cip509.validation_signature(), cip509.raw_aux_data(), signing_pk, cip509.report(), @@ -532,18 +545,34 @@ impl RegistrationChainInner { return None; } - let point_tx_idx = cip509.origin().clone(); - let Ok((purpose, registration, payment_history)) = cip509.consume() else { + if cip509.txn_inputs_hash().is_none() { + cip509.report().missing_field("txn inputs hash", context); + }; + + let Some(purpose) = cip509.purpose() else { + cip509.report().missing_field("purpose", context); return None; }; + let Some(registration) = cip509.metadata().cloned() else { + cip509.report().missing_field("metadata", context); + return None; + }; + + if cip509.report().is_problematic() { + return None; + } // Add purpose to the chain, if not already exist if !self.purpose.contains(&purpose) { new_inner.purpose.push(purpose); } + let point_tx_idx = cip509.origin().clone(); + new_inner.certificate_uris = new_inner.certificate_uris.update(®istration); - new_inner.payment_history.extend(payment_history); + new_inner + .payment_history + .extend(cip509.payment_history().clone()); update_x509_certs( &mut new_inner.x509_certs, registration.x509_certs.clone(), @@ -610,7 +639,7 @@ impl RegistrationChainInner { /// Perform a check on the validation signature. /// The auxiliary data should be sign with the latest signing public key. fn check_validation_signature( - validation_signature: Option, + validation_signature: Option<&ValidationSignature>, raw_aux_data: &[u8], signing_pk: VerifyingKey, report: &ProblemReport, @@ -665,9 +694,7 @@ mod test { // Performs only stateless validations // Create a chain with the first registration. - let chain = RegistrationChainInner::new(registration) - .map(|r| RegistrationChain { inner: r.into() }) - .unwrap(); + let chain = RegistrationChain::new_stateless(®istration).unwrap(); assert_eq!(chain.purpose(), &[data.purpose]); assert_eq!(1, chain.x509_certs().len()); let origin = &chain.x509_certs().get(&0).unwrap().first().unwrap(); @@ -694,7 +721,7 @@ mod test { assert!(registration.report().is_problematic()); let report = registration.report().to_owned(); - assert!(chain.update_stateless(registration).is_none()); + assert!(chain.update_stateless(®istration).is_none()); let report = format!("{report:?}"); assert!( report.contains("kind: InvalidValue { field: \"previous transaction ID\""), @@ -708,7 +735,7 @@ mod test { .unwrap() .unwrap(); data.assert_valid(®istration); - let update = chain.update_stateless(registration).unwrap(); + let update = chain.update_stateless(®istration).unwrap(); // Current tx hash should be equal to the hash from block 4. assert_eq!(update.current_tx_id_hash(), data.txn_hash); assert!(update.role_data_record().contains_key(&data.role)); diff --git a/rust/rbac-registration/src/utils/test.rs b/rust/rbac-registration/src/utils/test.rs index 3d8f0e86111..345a019b555 100644 --- a/rust/rbac-registration/src/utils/test.rs +++ b/rust/rbac-registration/src/utils/test.rs @@ -48,8 +48,7 @@ impl BlockTestData { assert!(cip509.role_data(self.role).is_some()); assert_eq!(cip509.txn_hash(), self.txn_hash); assert_eq!(cip509.previous_transaction(), self.prv_hash); - let (purpose, ..) = cip509.clone().consume().unwrap(); - assert_eq!(purpose, self.purpose); + assert_eq!(cip509.purpose().unwrap(), self.purpose); } } From ec48143a5baf0a3232da1ab1cebc42b16dabe711 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 10 Nov 2025 15:58:27 +0700 Subject: [PATCH 04/30] wip --- .../src/registration/cardano/mod.rs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs index 7fd7b497f19..2b3ccf84163 100644 --- a/rust/rbac-registration/src/registration/cardano/mod.rs +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -92,7 +92,7 @@ impl RegistrationChain { } } - if reg.report().is_problematic() { + if cip509.report().is_problematic() { Ok(None) } else { Ok(Some(new_chain)) @@ -102,7 +102,7 @@ impl RegistrationChain { /// Create a new instance of registration chain. /// The first new value should be the chain root. #[must_use] - pub fn new_stateless(cip509: &Cip509) -> Option { + fn new_stateless(cip509: &Cip509) -> Option { let inner = RegistrationChainInner::new(cip509)?; Some(Self { @@ -118,23 +118,23 @@ impl RegistrationChain { /// ownership (e.g., database lookup failures). pub async fn update( &self, - reg: &Cip509, + cip509: &Cip509, provider: &Provider, ) -> anyhow::Result> where Provider: RbacRegistrationProvider, { - let Some(new_chain) = self.update_stateless(reg) else { + let Some(new_chain) = self.update_stateless(cip509) else { return Ok(None); }; // Check that addresses from the new registration aren't used in other chains. let previous_addresses = self.stake_addresses(); - let reg_addresses = reg.stake_addresses(); + let reg_addresses = cip509.stake_addresses(); let new_addresses: Vec<_> = reg_addresses.difference(&previous_addresses).collect(); for address in &new_addresses { if provider.chain_from_stake_address(address).await?.is_some() { - reg.report().functional_validation( + cip509.report().functional_validation( &format!("{address} stake addresses is already used"), "It isn't allowed to use same stake address in multiple registration chains, if its not a new chain", ); @@ -145,11 +145,11 @@ impl RegistrationChain { // other chain. Returns a list of public keys in the registration. { let cat_id = self.catalyst_id(); - for role in reg.all_roles() { + for role in cip509.all_roles() { if let Some((key, _)) = new_chain.get_latest_signing_pk_for_role(role) { if let Some(previous) = provider.catalyst_id_from_public_key(&key).await? { if &previous != cat_id { - reg.report().functional_validation( + cip509.report().functional_validation( &format!("An update to {cat_id} registration chain uses the same public key ({key:?}) as {previous} chain"), "It isn't allowed to use role 0 signing (certificate subject public) key in different chains", ); @@ -159,7 +159,7 @@ impl RegistrationChain { } } - if reg.report().is_problematic() { + if cip509.report().is_problematic() { Ok(None) } else { Ok(Some(new_chain)) @@ -171,7 +171,7 @@ impl RegistrationChain { /// # Arguments /// - `cip509` - The CIP509. #[must_use] - pub fn update_stateless( + fn update_stateless( &self, cip509: &Cip509, ) -> Option { From 58a8cc2d4820a7dc51293269060bcd1d3f010c86 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 10 Nov 2025 16:07:07 +0700 Subject: [PATCH 05/30] revert --- .../src/cardano/cip509/cip509.rs | 44 ++++++++++++++++++- .../src/registration/cardano/mod.rs | 4 +- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/cip509.rs b/rust/rbac-registration/src/cardano/cip509/cip509.rs index b66aa01f7ac..86ccb9a583a 100644 --- a/rust/rbac-registration/src/cardano/cip509/cip509.rs +++ b/rust/rbac-registration/src/cardano/cip509/cip509.rs @@ -22,6 +22,7 @@ use catalyst_types::{ uuid::UuidV4, }; use cbork_utils::decode_helper::{decode_bytes, decode_helper, decode_map_len}; +use ed25519_dalek::VerifyingKey; use minicbor::{ decode::{self}, Decode, Decoder, @@ -32,6 +33,7 @@ use uuid::Uuid; use crate::cardano::cip509::{ decode_context::DecodeContext, + extract_key, rbac::Cip509RbacMetadata, types::{PaymentHistory, TxInputHash, ValidationSignature}, utils::Cip0134UriSet, @@ -40,7 +42,7 @@ use crate::cardano::cip509::{ validate_txn_inputs_hash, }, x509_chunks::X509Chunks, - Payment, PointTxnIdx, RoleData, + C509Cert, LocalRefInt, Payment, PointTxnIdx, RoleData, SimplePublicKeyType, X509DerCert, }; /// A x509 metadata envelope. @@ -227,6 +229,46 @@ impl Cip509 { self.metadata.as_ref().and_then(|m| m.role_data.get(&role)) } + /// Returns signing public key for a role. + #[must_use] + pub fn signing_pk_for_role( + &self, + role: RoleId, + ) -> Option { + self.metadata.as_ref().and_then(|m| { + let key_ref = m.role_data.get(&role).and_then(|d| d.signing_key())?; + match key_ref.local_ref { + LocalRefInt::X509Certs => { + m.x509_certs.get(key_ref.key_offset).and_then(|c| { + if let X509DerCert::X509Cert(c) = c { + extract_key::x509_key(&c).ok() + } else { + None + } + }) + }, + LocalRefInt::C509Certs => { + m.c509_certs.get(key_ref.key_offset).and_then(|c| { + if let C509Cert::C509Certificate(c) = c { + extract_key::c509_key(&c).ok() + } else { + None + } + }) + }, + LocalRefInt::PubKeys => { + m.pub_keys.get(key_ref.key_offset).and_then(|c| { + if let SimplePublicKeyType::Ed25519(c) = c { + Some(c.clone()) + } else { + None + } + }) + }, + } + }) + } + /// Returns a purpose of this registration. #[must_use] pub fn purpose(&self) -> Option { diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs index 2b3ccf84163..28fb39a5ff3 100644 --- a/rust/rbac-registration/src/registration/cardano/mod.rs +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -79,7 +79,7 @@ impl RegistrationChain { // Checks that a new registration doesn't contain a signing key that was used by any // other chain. Returns a list of public keys in the registration. for role in cip509.all_roles() { - if let Some((key, _)) = new_chain.get_latest_signing_pk_for_role(role) { + if let Some(key) = cip509.signing_pk_for_role(role) { if let Some(previous) = provider.catalyst_id_from_public_key(&key).await? { if &previous != cat_id { cip509.report().functional_validation( @@ -146,7 +146,7 @@ impl RegistrationChain { { let cat_id = self.catalyst_id(); for role in cip509.all_roles() { - if let Some((key, _)) = new_chain.get_latest_signing_pk_for_role(role) { + if let Some(key) = cip509.signing_pk_for_role(role) { if let Some(previous) = provider.catalyst_id_from_public_key(&key).await? { if &previous != cat_id { cip509.report().functional_validation( From ef6fb132ab975d0ce4c94f1eb2b2cc534d17acae Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Tue, 11 Nov 2025 14:09:51 +0700 Subject: [PATCH 06/30] wip --- rust/rbac-registration/src/cardano/cip509/cip509.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rust/rbac-registration/src/cardano/cip509/cip509.rs b/rust/rbac-registration/src/cardano/cip509/cip509.rs index 86ccb9a583a..d708bbb6c17 100644 --- a/rust/rbac-registration/src/cardano/cip509/cip509.rs +++ b/rust/rbac-registration/src/cardano/cip509/cip509.rs @@ -230,8 +230,10 @@ impl Cip509 { } /// Returns signing public key for a role. + /// Would return only signing public keys for the present ceritificates, + /// if certificate marked as deleted or undefined it would be skipped. #[must_use] - pub fn signing_pk_for_role( + pub(crate) fn signing_pk_for_role( &self, role: RoleId, ) -> Option { From 91a9a8cb7ad77586e831b60991881bdf76dc4a38 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Wed, 12 Nov 2025 19:30:42 +0700 Subject: [PATCH 07/30] add stolen_uris field --- .../cardano/cip509/utils/cip134_uri_set.rs | 17 ++-- .../src/registration/cardano/mod.rs | 80 +++++++++++++++++-- 2 files changed, 84 insertions(+), 13 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs b/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs index 44ba89af91a..fb950a47912 100644 --- a/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs +++ b/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs @@ -66,6 +66,14 @@ impl Cip0134UriSet { &self.0.c_uris } + /// Returns an iterator over of `Cip0134Uri`. + pub fn values(&self) -> impl Iterator { + self.x_uris() + .values() + .chain(self.c_uris().values()) + .flat_map(|uris| uris.iter()) + } + /// Returns `true` if both x509 and c509 certificate maps are empty. #[must_use] pub fn is_empty(&self) -> bool { @@ -90,7 +98,7 @@ impl Cip0134UriSet { result } - /// Returns a list of stake addresses by the given role. + /// Returns a set of stake addresses by the given role. #[must_use] pub fn role_stake_addresses( &self, @@ -107,13 +115,10 @@ impl Cip0134UriSet { .collect() } - /// Returns a list of all stake addresses. + /// Returns a set of all stake addresses. #[must_use] pub fn stake_addresses(&self) -> HashSet { - self.x_uris() - .values() - .chain(self.c_uris().values()) - .flat_map(|uris| uris.iter()) + self.values() .filter_map(|uri| { match uri.address() { Address::Stake(a) => Some(a.clone().into()), diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs index 28fb39a5ff3..aba179e8dbd 100644 --- a/rust/rbac-registration/src/registration/cardano/mod.rs +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -9,7 +9,9 @@ use std::{ use anyhow::Context; use c509_certificate::c509::C509; -use cardano_blockchain_types::{hashes::TransactionId, Point, StakeAddress, TxnIndex}; +use cardano_blockchain_types::{ + hashes::TransactionId, pallas_addresses::Address, Cip0134Uri, Point, StakeAddress, TxnIndex, +}; use catalyst_types::{ catalyst_id::{key_rotation::KeyRotation, role_index::RoleId, CatalystId}, conversion::zero_out_last_n_bytes, @@ -344,10 +346,29 @@ impl RegistrationChain { .and_then(|rdr| rdr.encryption_key_from_rotation(rotation)) } - /// Returns all stake addresses associated to this registration. + /// Returns all stake addresses associated to this chain. #[must_use] pub fn stake_addresses(&self) -> HashSet { - self.inner.certificate_uris.stake_addresses() + let stolen_stake_addresses = self + .inner + .stolen_uris + .iter() + .flat_map(|v| { + v.data().iter().filter_map(|uri| { + match uri.address() { + Address::Stake(a) => Some(a.clone().into()), + _ => None, + } + }) + }) + .collect(); + + self.inner + .certificate_uris + .stake_addresses() + .difference(&stolen_stake_addresses) + .cloned() + .collect() } } @@ -376,6 +397,9 @@ struct RegistrationChainInner { /// List of point + transaction index, and certificate key hash. revocations: Vec>, + /// URIs which are stolen by another registration chains. + stolen_uris: Vec>>, + // Role /// Map of role number to list point + transaction index, and role data. /// Record history of the whole role data in point in time. @@ -494,6 +518,7 @@ impl RegistrationChainInner { certificate_uris, simple_keys, revocations, + stolen_uris: vec![], role_data_history, role_data_record, payment_history, @@ -514,10 +539,25 @@ impl RegistrationChainInner { let mut new_inner = self.clone(); let Some(prv_tx_id) = cip509.previous_transaction() else { - cip509 - .report() - .missing_field("previous transaction ID", context); - return None; + if let Some(cat_id) = cip509.catalyst_id() { + if cat_id == &self.catalyst_id { + cip509.report().functional_validation( + &format!( + "Trying to apply the first registration to the assosiated {} again", + cat_id.as_short_id() + ), + "It isn't allowed to submit first registration twice", + ); + return None; + } + + return new_inner.update_cause_another_chain(cip509); + } else { + cip509 + .report() + .missing_field("previous transaction ID", context); + return None; + } }; // Previous transaction ID in the CIP509 should equal to the current transaction ID @@ -603,6 +643,32 @@ impl RegistrationChainInner { Some(new_inner) } + /// Update the registration chain with the `cip509` assosiated to another chain. + /// This is the case when registration for different chain affecting the current one, + /// by invalidating some data for the current registration chain (stoling stake + /// addresses etc.). + /// + /// The provided `cip509` should be fully validated by another chain before trying to + /// submit to the current one. + #[must_use] + fn update_cause_another_chain( + mut self, + cip509: &Cip509, + ) -> Option { + if let Some(uri_set) = cip509.certificate_uris() { + let origin = cip509.origin().clone(); + self.stolen_uris.push(PointData::new( + origin, + uri_set + .values() + .cloned() + .collect::>() + .into_boxed_slice(), + )); + } + Some(self) + } + /// Get the latest signing public key for a role. /// Returns the public key and the rotation,`None` if not found. #[must_use] From 6d52775ca88ce902636820f09ee1de35f7c72969 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Thu, 13 Nov 2025 15:59:06 +0700 Subject: [PATCH 08/30] wip --- .../cardano/cip509/utils/cip134_uri_set.rs | 43 +++++++++- .../src/cardano/cip509/validation.rs | 10 +-- .../src/registration/cardano/mod.rs | 84 ++++++++++--------- 3 files changed, 86 insertions(+), 51 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs b/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs index fb950a47912..410089b677e 100644 --- a/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs +++ b/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs @@ -39,6 +39,8 @@ struct Cip0134UriSetInner { x_uris: UrisMap, /// URIs from c509 certificates. c_uris: UrisMap, + /// URIs which are taken by another certificates. + taken_uris: HashSet, } impl Cip0134UriSet { @@ -51,7 +53,12 @@ impl Cip0134UriSet { ) -> Self { let x_uris = extract_x509_uris(x509_certs, report); let c_uris = extract_c509_uris(c509_certs, report); - Self(Arc::new(Cip0134UriSetInner { x_uris, c_uris })) + let taken_uris = HashSet::new(); + Self(Arc::new(Cip0134UriSetInner { + x_uris, + c_uris, + taken_uris, + })) } /// Returns a mapping from the x509 certificate index to URIs contained within. @@ -67,7 +74,7 @@ impl Cip0134UriSet { } /// Returns an iterator over of `Cip0134Uri`. - pub fn values(&self) -> impl Iterator { + pub(crate) fn values(&self) -> impl Iterator { self.x_uris() .values() .chain(self.c_uris().values()) @@ -159,6 +166,7 @@ impl Cip0134UriSet { let Cip0134UriSetInner { mut x_uris, mut c_uris, + mut taken_uris, } = Arc::unwrap_or_clone(self.0); for (index, cert) in metadata.x509_certs.iter().enumerate() { @@ -171,6 +179,7 @@ impl Cip0134UriSet { }, X509DerCert::X509Cert(_) => { if let Some(uris) = metadata.certificate_uris.x_uris().get(&index) { + taken_uris.remove(&uris); x_uris.insert(index, uris.clone()); } }, @@ -190,13 +199,41 @@ impl Cip0134UriSet { }, C509Cert::C509Certificate(_) => { if let Some(uris) = metadata.certificate_uris.c_uris().get(&index) { + taken_uris.remove(&uris); c_uris.insert(index, uris.clone()); } }, } } - Self(Arc::new(Cip0134UriSetInner { x_uris, c_uris })) + Self(Arc::new(Cip0134UriSetInner { + x_uris, + c_uris, + taken_uris, + })) + } + + /// Return the updated URIs set where the provided URIs were taken by other + /// registration chains. + /// + /// Updates the current URI set by removing the taken URIs from it. + #[must_use] + pub fn update_taken_uris( + self, + metadata: &Cip509RbacMetadata, + ) -> Self { + let taken_uri_set = metadata.certificate_uris.values().collect::>(); + let current_uris_set = self.values().collect::>(); + let taken_uris = current_uris_set.intersection(&taken_uri_set); + + let Cip0134UriSetInner { x_uris, c_uris, .. } = Arc::unwrap_or_clone(self.0); + let taken_uris = taken_uris.cloned().cloned().collect(); + + Self(Arc::new(Cip0134UriSetInner { + x_uris, + c_uris, + taken_uris, + })) } } diff --git a/rust/rbac-registration/src/cardano/cip509/validation.rs b/rust/rbac-registration/src/cardano/cip509/validation.rs index 1502c7f4816..8484f5bf514 100644 --- a/rust/rbac-registration/src/cardano/cip509/validation.rs +++ b/rust/rbac-registration/src/cardano/cip509/validation.rs @@ -157,10 +157,7 @@ fn extract_stake_addresses(uris: Option<&Cip0134UriSet>) -> Vec<(VKeyHash, Strin return Vec::new(); }; - uris.x_uris() - .iter() - .chain(uris.c_uris()) - .flat_map(|(_index, uris)| uris.iter()) + uris.values() .filter_map(|uri| { if let Address::Stake(a) = uri.address() { let bech32 = uri.address().to_string(); @@ -185,10 +182,7 @@ fn extract_payment_addresses(uris: Option<&Cip0134UriSet>) -> Vec<(VKeyHash, Str return Vec::new(); }; - uris.x_uris() - .iter() - .chain(uris.c_uris()) - .flat_map(|(_index, uris)| uris.iter()) + uris.values() .filter_map(|uri| { if let Address::Shelley(a) = uri.address() { match a.payment() { diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs index aba179e8dbd..9904d6d6312 100644 --- a/rust/rbac-registration/src/registration/cardano/mod.rs +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -9,9 +9,7 @@ use std::{ use anyhow::Context; use c509_certificate::c509::C509; -use cardano_blockchain_types::{ - hashes::TransactionId, pallas_addresses::Address, Cip0134Uri, Point, StakeAddress, TxnIndex, -}; +use cardano_blockchain_types::{hashes::TransactionId, Point, StakeAddress, TxnIndex}; use catalyst_types::{ catalyst_id::{key_rotation::KeyRotation, role_index::RoleId, CatalystId}, conversion::zero_out_last_n_bytes, @@ -26,8 +24,8 @@ use x509_cert::certificate::Certificate as X509Certificate; use crate::{ cardano::cip509::{ - CertKeyHash, CertOrPk, Cip0134UriSet, Cip509, PaymentHistory, PointData, RoleData, - RoleDataRecord, ValidationSignature, + CertKeyHash, CertOrPk, Cip0134UriSet, Cip509, PaymentHistory, PointData, PointTxnIdx, + RoleData, RoleDataRecord, ValidationSignature, }, providers::RbacRegistrationProvider, }; @@ -349,26 +347,13 @@ impl RegistrationChain { /// Returns all stake addresses associated to this chain. #[must_use] pub fn stake_addresses(&self) -> HashSet { - let stolen_stake_addresses = self - .inner - .stolen_uris - .iter() - .flat_map(|v| { - v.data().iter().filter_map(|uri| { - match uri.address() { - Address::Stake(a) => Some(a.clone().into()), - _ => None, - } - }) - }) - .collect(); + self.inner.certificate_uris.stake_addresses(); + } - self.inner - .certificate_uris - .stake_addresses() - .difference(&stolen_stake_addresses) - .cloned() - .collect() + /// Returns the latest know applied registration's `PointTxnIdx`. + #[must_use] + pub fn latest_applied(&self) -> PointTxnIdx { + self.inner.latest_applied() } } @@ -379,6 +364,9 @@ struct RegistrationChainInner { catalyst_id: CatalystId, /// The current transaction ID hash (32 bytes) current_tx_id_hash: PointData, + /// The latest `PointTxnIdx` of the stolen taken URIs by another registration chains. + latest_taken_uris_point: Option, + /// List of purpose for this registration chain purpose: Vec, @@ -397,9 +385,6 @@ struct RegistrationChainInner { /// List of point + transaction index, and certificate key hash. revocations: Vec>, - /// URIs which are stolen by another registration chains. - stolen_uris: Vec>>, - // Role /// Map of role number to list point + transaction index, and role data. /// Record history of the whole role data in point in time. @@ -516,9 +501,9 @@ impl RegistrationChainInner { x509_certs, c509_certs, certificate_uris, + latest_taken_uris_point: None, simple_keys, revocations, - stolen_uris: vec![], role_data_history, role_data_record, payment_history, @@ -536,6 +521,18 @@ impl RegistrationChainInner { signing_pk: VerifyingKey, ) -> Option { let context = "Registration Chain update"; + if self.latest_applied().point() >= cip509.origin().point() { + cip509.report().functional_validation( + &format!( + "The provided registration is earlier {} than the current one {}", + cip509.origin().point(), + self.current_tx_id_hash.point() + ), + "Provided registrations must be applied in the correct order.", + ); + return None; + } + let mut new_inner = self.clone(); let Some(prv_tx_id) = cip509.previous_transaction() else { @@ -654,19 +651,12 @@ impl RegistrationChainInner { fn update_cause_another_chain( mut self, cip509: &Cip509, - ) -> Option { - if let Some(uri_set) = cip509.certificate_uris() { - let origin = cip509.origin().clone(); - self.stolen_uris.push(PointData::new( - origin, - uri_set - .values() - .cloned() - .collect::>() - .into_boxed_slice(), - )); + ) -> Self { + if let Some(reg) = cip509.metadata() { + self.certificate_uris = self.certificate_uris.update_taken_uris(reg); } - Some(self) + self.latest_taken_uris_point = Some(cip509.origin().clone()); + self } /// Get the latest signing public key for a role. @@ -700,6 +690,20 @@ impl RegistrationChainInner { }) }) } + + /// Returns the latest know applied registration's `PointTxnIdx`. + #[must_use] + fn latest_applied(&self) -> PointTxnIdx { + if let Some(latest_taken_uris_point) = &self.latest_taken_uris_point { + if latest_taken_uris_point.point() > self.current_tx_id_hash.point() { + return latest_taken_uris_point.clone(); + } + } + PointTxnIdx::new( + self.current_tx_id_hash.point().clone(), + self.current_tx_id_hash.txn_index(), + ) + } } /// Perform a check on the validation signature. From c949a126d04854269d3a0a6984c4e1088020af2c Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Thu, 13 Nov 2025 16:23:31 +0700 Subject: [PATCH 09/30] wip --- rust/rbac-registration/Cargo.toml | 2 +- .../cardano/cip509/utils/cip134_uri_set.rs | 20 ++++++++++++++----- .../src/registration/cardano/mod.rs | 4 ++-- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/rust/rbac-registration/Cargo.toml b/rust/rbac-registration/Cargo.toml index c238f89a4f2..cf54fa327fc 100644 --- a/rust/rbac-registration/Cargo.toml +++ b/rust/rbac-registration/Cargo.toml @@ -34,5 +34,5 @@ thiserror = "2.0.11" c509-certificate = { version = "0.0.3", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "c509-certificate-v0.0.3" } cbork-utils = { version = "0.0.2", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "cbork-utils-v0.0.2" } -cardano-blockchain-types = { version = "0.0.8", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "cardano-blockchain-types/v0.0.8" } +cardano-blockchain-types = { version = "0.0.9", git = "https://github.com/input-output-hk/catalyst-libs.git", branch = "feat/cip-134-uri-hash" } catalyst-types = { version = "0.0.10", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "catalyst-types/v0.0.10" } diff --git a/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs b/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs index 410089b677e..ef89154b63d 100644 --- a/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs +++ b/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs @@ -179,7 +179,9 @@ impl Cip0134UriSet { }, X509DerCert::X509Cert(_) => { if let Some(uris) = metadata.certificate_uris.x_uris().get(&index) { - taken_uris.remove(&uris); + uris.iter().for_each(|v| { + taken_uris.remove(v); + }); x_uris.insert(index, uris.clone()); } }, @@ -199,7 +201,9 @@ impl Cip0134UriSet { }, C509Cert::C509Certificate(_) => { if let Some(uris) = metadata.certificate_uris.c_uris().get(&index) { - taken_uris.remove(&uris); + uris.iter().for_each(|v| { + taken_uris.remove(v); + }); c_uris.insert(index, uris.clone()); } }, @@ -224,10 +228,16 @@ impl Cip0134UriSet { ) -> Self { let taken_uri_set = metadata.certificate_uris.values().collect::>(); let current_uris_set = self.values().collect::>(); - let taken_uris = current_uris_set.intersection(&taken_uri_set); + let latest_taken_uris = current_uris_set.intersection(&taken_uri_set); + let latest_taken_uris = latest_taken_uris.cloned().cloned().collect::>(); - let Cip0134UriSetInner { x_uris, c_uris, .. } = Arc::unwrap_or_clone(self.0); - let taken_uris = taken_uris.cloned().cloned().collect(); + let Cip0134UriSetInner { + x_uris, + c_uris, + mut taken_uris, + } = Arc::unwrap_or_clone(self.0); + + taken_uris.extend(latest_taken_uris); Self(Arc::new(Cip0134UriSetInner { x_uris, diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs index 9904d6d6312..728fb5db6c8 100644 --- a/rust/rbac-registration/src/registration/cardano/mod.rs +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -347,7 +347,7 @@ impl RegistrationChain { /// Returns all stake addresses associated to this chain. #[must_use] pub fn stake_addresses(&self) -> HashSet { - self.inner.certificate_uris.stake_addresses(); + self.inner.certificate_uris.stake_addresses() } /// Returns the latest know applied registration's `PointTxnIdx`. @@ -548,7 +548,7 @@ impl RegistrationChainInner { return None; } - return new_inner.update_cause_another_chain(cip509); + return Some(new_inner.update_cause_another_chain(cip509)); } else { cip509 .report() From 7e596590eb3f10cd660cf6e6ef84be796b95b07b Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Thu, 13 Nov 2025 17:11:33 +0700 Subject: [PATCH 10/30] wip --- .../src/cardano/cip509/utils/cip134_uri_set.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs b/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs index ef89154b63d..9d02b04a358 100644 --- a/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs +++ b/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs @@ -73,12 +73,13 @@ impl Cip0134UriSet { &self.0.c_uris } - /// Returns an iterator over of `Cip0134Uri`. + /// Returns an iterator over of active (without taken) `Cip0134Uri`. pub(crate) fn values(&self) -> impl Iterator { self.x_uris() .values() .chain(self.c_uris().values()) .flat_map(|uris| uris.iter()) + .filter(|v| !self.0.taken_uris.contains(v)) } /// Returns `true` if both x509 and c509 certificate maps are empty. @@ -224,12 +225,16 @@ impl Cip0134UriSet { #[must_use] pub fn update_taken_uris( self, - metadata: &Cip509RbacMetadata, + reg: &Cip509RbacMetadata, ) -> Self { - let taken_uri_set = metadata.certificate_uris.values().collect::>(); let current_uris_set = self.values().collect::>(); - let latest_taken_uris = current_uris_set.intersection(&taken_uri_set); - let latest_taken_uris = latest_taken_uris.cloned().cloned().collect::>(); + + let latest_taken_uris = reg + .certificate_uris + .values() + .filter(|v| current_uris_set.contains(v)) + .cloned() + .collect::>(); let Cip0134UriSetInner { x_uris, From 323f2bad812830c111f9af45e8473c8b1f94458d Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Thu, 13 Nov 2025 17:53:21 +0700 Subject: [PATCH 11/30] fix --- rust/rbac-registration/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/rbac-registration/Cargo.toml b/rust/rbac-registration/Cargo.toml index cf54fa327fc..33b04640358 100644 --- a/rust/rbac-registration/Cargo.toml +++ b/rust/rbac-registration/Cargo.toml @@ -34,5 +34,5 @@ thiserror = "2.0.11" c509-certificate = { version = "0.0.3", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "c509-certificate-v0.0.3" } cbork-utils = { version = "0.0.2", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "cbork-utils-v0.0.2" } -cardano-blockchain-types = { version = "0.0.9", git = "https://github.com/input-output-hk/catalyst-libs.git", branch = "feat/cip-134-uri-hash" } +cardano-blockchain-types = { version = "0.0.9", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "cardano-blockchain-types/v0.0.9" } catalyst-types = { version = "0.0.10", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "catalyst-types/v0.0.10" } From 307ad4dd54a4db441a3fa370eac654bd0f55726a Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Thu, 13 Nov 2025 18:04:12 +0700 Subject: [PATCH 12/30] wip --- rust/rbac-registration/src/cardano/mod.rs | 1 + .../src/{providers.rs => cardano/state.rs} | 18 +++++-------- rust/rbac-registration/src/lib.rs | 1 - .../src/registration/cardano/mod.rs | 26 +++++++++---------- 4 files changed, 21 insertions(+), 25 deletions(-) rename rust/rbac-registration/src/{providers.rs => cardano/state.rs} (65%) diff --git a/rust/rbac-registration/src/cardano/mod.rs b/rust/rbac-registration/src/cardano/mod.rs index 8af2182b83a..11784792f49 100644 --- a/rust/rbac-registration/src/cardano/mod.rs +++ b/rust/rbac-registration/src/cardano/mod.rs @@ -1,3 +1,4 @@ //! Cardano module pub mod cip509; +pub mod state; diff --git a/rust/rbac-registration/src/providers.rs b/rust/rbac-registration/src/cardano/state.rs similarity index 65% rename from rust/rbac-registration/src/providers.rs rename to rust/rbac-registration/src/cardano/state.rs index 754a1f8d191..705622f8494 100644 --- a/rust/rbac-registration/src/providers.rs +++ b/rust/rbac-registration/src/cardano/state.rs @@ -1,4 +1,5 @@ -//! Providers traits, which are used during different validation procedures. +//! Cardano RBAC state traits, which are used during different statefull validation +//! procedures. use std::future::Future; @@ -8,26 +9,21 @@ use ed25519_dalek::VerifyingKey; use crate::registration::cardano::RegistrationChain; -/// `RegistrationChain` Provider trait -pub trait RbacRegistrationProvider { - /// Returns registration chain - /// for the given Catalyst ID. +/// RBAC chains state trait +pub trait RBACState { + /// Returns RBAC chain for the given Catalyst ID. fn chain( &self, id: &CatalystId, ) -> impl Future>> + Send; - /// Returns `true` if a chain with the given Catalyst ID already exists. - /// - /// This function behaves in the same way as `latest_rbac_chain(...).is_some()` but - /// the implementation is more optimized because we don't need to build the whole - /// chain. + /// Returns `true` if a RBAC chain with the given Catalyst ID already exists. fn is_chain_known( &self, id: &CatalystId, ) -> impl Future> + Send; - /// Returns a current valid registration chain corresponding to the given stake + /// Returns a current valid RBAC chain corresponding to the given stake /// address. fn chain_from_stake_address( &self, diff --git a/rust/rbac-registration/src/lib.rs b/rust/rbac-registration/src/lib.rs index b140584362f..6e35f516568 100644 --- a/rust/rbac-registration/src/lib.rs +++ b/rust/rbac-registration/src/lib.rs @@ -1,7 +1,6 @@ //! This crate provides functionalities for RBAC registration. pub mod cardano; -pub mod providers; pub mod registration; mod utils; diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs index 728fb5db6c8..6c41db6d1a7 100644 --- a/rust/rbac-registration/src/registration/cardano/mod.rs +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -22,12 +22,12 @@ use update_rbac::{ }; use x509_cert::certificate::Certificate as X509Certificate; -use crate::{ - cardano::cip509::{ +use crate::cardano::{ + cip509::{ CertKeyHash, CertOrPk, Cip0134UriSet, Cip509, PaymentHistory, PointData, PointTxnIdx, RoleData, RoleDataRecord, ValidationSignature, }, - providers::RbacRegistrationProvider, + state::RBACState, }; /// Registration chains. @@ -47,12 +47,12 @@ impl RegistrationChain { /// # Errors /// - Propagates any I/O or provider-level errors encountered while checking key /// ownership (e.g., database lookup failures). - pub async fn new( + pub async fn new( cip509: &Cip509, - provider: &Provider, + state: &State, ) -> anyhow::Result> where - Provider: RbacRegistrationProvider, + State: RBACState, { let Some(new_chain) = Self::new_stateless(cip509) else { return Ok(None); @@ -69,7 +69,7 @@ impl RegistrationChain { // Verify that a Catalyst ID of this chain is unique. { let cat_id = new_chain.catalyst_id(); - if provider.is_chain_known(cat_id).await? { + if state.is_chain_known(cat_id).await? { cip509.report().functional_validation( &format!("{} is already used", cat_id.as_short_id()), "It isn't allowed to use same Catalyst ID (certificate subject public key) in multiple registration chains", @@ -80,7 +80,7 @@ impl RegistrationChain { // other chain. Returns a list of public keys in the registration. for role in cip509.all_roles() { if let Some(key) = cip509.signing_pk_for_role(role) { - if let Some(previous) = provider.catalyst_id_from_public_key(&key).await? { + if let Some(previous) = state.catalyst_id_from_public_key(&key).await? { if &previous != cat_id { cip509.report().functional_validation( &format!("An update to {cat_id} registration chain uses the same public key ({key:?}) as {previous} chain"), @@ -116,13 +116,13 @@ impl RegistrationChain { /// # Errors /// - Propagates any I/O or provider-level errors encountered while checking key /// ownership (e.g., database lookup failures). - pub async fn update( + pub async fn update( &self, cip509: &Cip509, - provider: &Provider, + state: &State, ) -> anyhow::Result> where - Provider: RbacRegistrationProvider, + State: RBACState, { let Some(new_chain) = self.update_stateless(cip509) else { return Ok(None); @@ -133,7 +133,7 @@ impl RegistrationChain { let reg_addresses = cip509.stake_addresses(); let new_addresses: Vec<_> = reg_addresses.difference(&previous_addresses).collect(); for address in &new_addresses { - if provider.chain_from_stake_address(address).await?.is_some() { + if state.chain_from_stake_address(address).await?.is_some() { cip509.report().functional_validation( &format!("{address} stake addresses is already used"), "It isn't allowed to use same stake address in multiple registration chains, if its not a new chain", @@ -147,7 +147,7 @@ impl RegistrationChain { let cat_id = self.catalyst_id(); for role in cip509.all_roles() { if let Some(key) = cip509.signing_pk_for_role(role) { - if let Some(previous) = provider.catalyst_id_from_public_key(&key).await? { + if let Some(previous) = state.catalyst_id_from_public_key(&key).await? { if &previous != cat_id { cip509.report().functional_validation( &format!("An update to {cat_id} registration chain uses the same public key ({key:?}) as {previous} chain"), From 6548e4a84a105271526ae3e0123a429c3357d3c4 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Thu, 13 Nov 2025 18:27:22 +0700 Subject: [PATCH 13/30] wip --- rust/rbac-registration/src/cardano/state.rs | 24 ++++++++++---- .../src/registration/cardano/mod.rs | 32 ++++++++++--------- 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/rust/rbac-registration/src/cardano/state.rs b/rust/rbac-registration/src/cardano/state.rs index 705622f8494..ff6613bc540 100644 --- a/rust/rbac-registration/src/cardano/state.rs +++ b/rust/rbac-registration/src/cardano/state.rs @@ -23,22 +23,32 @@ pub trait RBACState { id: &CatalystId, ) -> impl Future> + Send; - /// Returns a current valid RBAC chain corresponding to the given stake + /// Returns a current valid RBAC chain Catalyst ID corresponding to the given stake /// address. - fn chain_from_stake_address( + fn chain_catalyst_id_from_stake_address( &self, address: &StakeAddress, - ) -> impl Future>> + Send; + ) -> impl Future>> + Send; - /// Returns a Catalyst ID corresponding to the given public key. - fn catalyst_id_from_public_key( + /// Returns a corresponding to the RBAC chain's Catalyst ID corresponding by the given + /// public key. + fn chain_catalyst_id_from_public_key( &self, key: &VerifyingKey, ) -> impl Future>> + Send; - /// Returns a Catalyst ID corresponding to the given transaction hash. - fn catalyst_id_from_txn_id( + /// Returns a corresponding to the RBAC chain's Catalyst ID corresponding by the given + /// transaction hash. + fn chain_catalyst_id_from_txn_id( &self, txn_id: &TransactionId, ) -> impl Future>> + Send; + + /// Update the update by "taking" the given `StakeAddress` for the correspoding RBAC + /// chain's by the given `CatalystId`. + fn take_stake_address_from_chain( + &mut self, + id: &CatalystId, + address: &StakeAddress, + ) -> impl Future> + Send; } diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs index 6c41db6d1a7..3bf0c04ea4e 100644 --- a/rust/rbac-registration/src/registration/cardano/mod.rs +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -49,7 +49,7 @@ impl RegistrationChain { /// ownership (e.g., database lookup failures). pub async fn new( cip509: &Cip509, - state: &State, + state: &mut State, ) -> anyhow::Result> where State: RBACState, @@ -58,14 +58,6 @@ impl RegistrationChain { return Ok(None); }; - // Validate stake addresses. - let new_addresses = cip509.stake_addresses(); - for _address in &new_addresses { - // TODO: The new root registration "takes" an address(es) from the - // existing chain, so that chain needs to be - // updated. - } - // Verify that a Catalyst ID of this chain is unique. { let cat_id = new_chain.catalyst_id(); @@ -80,7 +72,7 @@ impl RegistrationChain { // other chain. Returns a list of public keys in the registration. for role in cip509.all_roles() { if let Some(key) = cip509.signing_pk_for_role(role) { - if let Some(previous) = state.catalyst_id_from_public_key(&key).await? { + if let Some(previous) = state.chain_catalyst_id_from_public_key(&key).await? { if &previous != cat_id { cip509.report().functional_validation( &format!("An update to {cat_id} registration chain uses the same public key ({key:?}) as {previous} chain"), @@ -93,10 +85,16 @@ impl RegistrationChain { } if cip509.report().is_problematic() { - Ok(None) - } else { - Ok(Some(new_chain)) + return Ok(None); } + + for address in &cip509.stake_addresses() { + if let Some(id) = state.chain_catalyst_id_from_stake_address(address).await? { + state.take_stake_address_from_chain(&id, address).await?; + } + } + + Ok(Some(new_chain)) } /// Create a new instance of registration chain. @@ -133,7 +131,11 @@ impl RegistrationChain { let reg_addresses = cip509.stake_addresses(); let new_addresses: Vec<_> = reg_addresses.difference(&previous_addresses).collect(); for address in &new_addresses { - if state.chain_from_stake_address(address).await?.is_some() { + if state + .chain_catalyst_id_from_stake_address(address) + .await? + .is_some() + { cip509.report().functional_validation( &format!("{address} stake addresses is already used"), "It isn't allowed to use same stake address in multiple registration chains, if its not a new chain", @@ -147,7 +149,7 @@ impl RegistrationChain { let cat_id = self.catalyst_id(); for role in cip509.all_roles() { if let Some(key) = cip509.signing_pk_for_role(role) { - if let Some(previous) = state.catalyst_id_from_public_key(&key).await? { + if let Some(previous) = state.chain_catalyst_id_from_public_key(&key).await? { if &previous != cat_id { cip509.report().functional_validation( &format!("An update to {cat_id} registration chain uses the same public key ({key:?}) as {previous} chain"), From 167327b2fd0506f10d08e834d4ad5fbdc8b0ef5a Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Fri, 14 Nov 2025 11:07:26 +0700 Subject: [PATCH 14/30] fix spelling --- rust/rbac-registration/src/cardano/cip509/cip509.rs | 2 +- rust/rbac-registration/src/cardano/state.rs | 4 ++-- rust/rbac-registration/src/registration/cardano/mod.rs | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/cip509.rs b/rust/rbac-registration/src/cardano/cip509/cip509.rs index d708bbb6c17..a72dee2ca59 100644 --- a/rust/rbac-registration/src/cardano/cip509/cip509.rs +++ b/rust/rbac-registration/src/cardano/cip509/cip509.rs @@ -230,7 +230,7 @@ impl Cip509 { } /// Returns signing public key for a role. - /// Would return only signing public keys for the present ceritificates, + /// Would return only signing public keys for the present certificates, /// if certificate marked as deleted or undefined it would be skipped. #[must_use] pub(crate) fn signing_pk_for_role( diff --git a/rust/rbac-registration/src/cardano/state.rs b/rust/rbac-registration/src/cardano/state.rs index ff6613bc540..42d4e29020e 100644 --- a/rust/rbac-registration/src/cardano/state.rs +++ b/rust/rbac-registration/src/cardano/state.rs @@ -1,4 +1,4 @@ -//! Cardano RBAC state traits, which are used during different statefull validation +//! Cardano RBAC state traits, which are used during different stateful validation //! procedures. use std::future::Future; @@ -44,7 +44,7 @@ pub trait RBACState { txn_id: &TransactionId, ) -> impl Future>> + Send; - /// Update the update by "taking" the given `StakeAddress` for the correspoding RBAC + /// Update the update by "taking" the given `StakeAddress` for the corresponding RBAC /// chain's by the given `CatalystId`. fn take_stake_address_from_chain( &mut self, diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs index 3bf0c04ea4e..e312bfa7e9c 100644 --- a/rust/rbac-registration/src/registration/cardano/mod.rs +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -542,7 +542,7 @@ impl RegistrationChainInner { if cat_id == &self.catalyst_id { cip509.report().functional_validation( &format!( - "Trying to apply the first registration to the assosiated {} again", + "Trying to apply the first registration to the associated {} again", cat_id.as_short_id() ), "It isn't allowed to submit first registration twice", @@ -642,9 +642,9 @@ impl RegistrationChainInner { Some(new_inner) } - /// Update the registration chain with the `cip509` assosiated to another chain. + /// Update the registration chain with the `cip509` associated to another chain. /// This is the case when registration for different chain affecting the current one, - /// by invalidating some data for the current registration chain (stoling stake + /// by invalidating some data for the current registration chain (stealing stake /// addresses etc.). /// /// The provided `cip509` should be fully validated by another chain before trying to From b5830c01d3bcf52ec0bc7e83c88a62c12b561f53 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Fri, 14 Nov 2025 14:17:30 +0700 Subject: [PATCH 15/30] fix --- .../src/cardano/cip509/cip509.rs | 6 +- .../cardano/cip509/utils/cip134_uri_set.rs | 14 ++-- .../src/registration/cardano/mod.rs | 78 +++++++++---------- 3 files changed, 47 insertions(+), 51 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/cip509.rs b/rust/rbac-registration/src/cardano/cip509/cip509.rs index a72dee2ca59..840de2c7482 100644 --- a/rust/rbac-registration/src/cardano/cip509/cip509.rs +++ b/rust/rbac-registration/src/cardano/cip509/cip509.rs @@ -243,7 +243,7 @@ impl Cip509 { LocalRefInt::X509Certs => { m.x509_certs.get(key_ref.key_offset).and_then(|c| { if let X509DerCert::X509Cert(c) = c { - extract_key::x509_key(&c).ok() + extract_key::x509_key(c).ok() } else { None } @@ -252,7 +252,7 @@ impl Cip509 { LocalRefInt::C509Certs => { m.c509_certs.get(key_ref.key_offset).and_then(|c| { if let C509Cert::C509Certificate(c) = c { - extract_key::c509_key(&c).ok() + extract_key::c509_key(c).ok() } else { None } @@ -261,7 +261,7 @@ impl Cip509 { LocalRefInt::PubKeys => { m.pub_keys.get(key_ref.key_offset).and_then(|c| { if let SimplePublicKeyType::Ed25519(c) = c { - Some(c.clone()) + Some(*c) } else { None } diff --git a/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs b/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs index 9d02b04a358..1a4f5c4786a 100644 --- a/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs +++ b/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs @@ -40,7 +40,7 @@ struct Cip0134UriSetInner { /// URIs from c509 certificates. c_uris: UrisMap, /// URIs which are taken by another certificates. - taken_uris: HashSet, + taken: HashSet, } impl Cip0134UriSet { @@ -57,7 +57,7 @@ impl Cip0134UriSet { Self(Arc::new(Cip0134UriSetInner { x_uris, c_uris, - taken_uris, + taken: taken_uris, })) } @@ -79,7 +79,7 @@ impl Cip0134UriSet { .values() .chain(self.c_uris().values()) .flat_map(|uris| uris.iter()) - .filter(|v| !self.0.taken_uris.contains(v)) + .filter(|v| !self.0.taken.contains(v)) } /// Returns `true` if both x509 and c509 certificate maps are empty. @@ -167,7 +167,7 @@ impl Cip0134UriSet { let Cip0134UriSetInner { mut x_uris, mut c_uris, - mut taken_uris, + taken: mut taken_uris, } = Arc::unwrap_or_clone(self.0); for (index, cert) in metadata.x509_certs.iter().enumerate() { @@ -214,7 +214,7 @@ impl Cip0134UriSet { Self(Arc::new(Cip0134UriSetInner { x_uris, c_uris, - taken_uris, + taken: taken_uris, })) } @@ -239,7 +239,7 @@ impl Cip0134UriSet { let Cip0134UriSetInner { x_uris, c_uris, - mut taken_uris, + taken: mut taken_uris, } = Arc::unwrap_or_clone(self.0); taken_uris.extend(latest_taken_uris); @@ -247,7 +247,7 @@ impl Cip0134UriSet { Self(Arc::new(Cip0134UriSetInner { x_uris, c_uris, - taken_uris, + taken: taken_uris, })) } } diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs index e312bfa7e9c..45e784a98fb 100644 --- a/rust/rbac-registration/src/registration/cardano/mod.rs +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -46,7 +46,7 @@ impl RegistrationChain { /// /// # Errors /// - Propagates any I/O or provider-level errors encountered while checking key - /// ownership (e.g., database lookup failures). + /// ownership (e.g., database lookup failures). pub async fn new( cip509: &Cip509, state: &mut State, @@ -68,20 +68,7 @@ impl RegistrationChain { ); } - // Checks that a new registration doesn't contain a signing key that was used by any - // other chain. Returns a list of public keys in the registration. - for role in cip509.all_roles() { - if let Some(key) = cip509.signing_pk_for_role(role) { - if let Some(previous) = state.chain_catalyst_id_from_public_key(&key).await? { - if &previous != cat_id { - cip509.report().functional_validation( - &format!("An update to {cat_id} registration chain uses the same public key ({key:?}) as {previous} chain"), - "It isn't allowed to use role 0 signing (certificate subject public) key in different chains", - ); - } - } - } - } + check_signing_pk(cat_id, cip509, state).await?; } if cip509.report().is_problematic() { @@ -113,7 +100,7 @@ impl RegistrationChain { /// /// # Errors /// - Propagates any I/O or provider-level errors encountered while checking key - /// ownership (e.g., database lookup failures). + /// ownership (e.g., database lookup failures). pub async fn update( &self, cip509: &Cip509, @@ -143,23 +130,7 @@ impl RegistrationChain { } } - // Checks that a new registration doesn't contain a signing key that was used by any - // other chain. Returns a list of public keys in the registration. - { - let cat_id = self.catalyst_id(); - for role in cip509.all_roles() { - if let Some(key) = cip509.signing_pk_for_role(role) { - if let Some(previous) = state.chain_catalyst_id_from_public_key(&key).await? { - if &previous != cat_id { - cip509.report().functional_validation( - &format!("An update to {cat_id} registration chain uses the same public key ({key:?}) as {previous} chain"), - "It isn't allowed to use role 0 signing (certificate subject public) key in different chains", - ); - } - } - } - } - } + check_signing_pk(self.catalyst_id(), cip509, state).await?; if cip509.report().is_problematic() { Ok(None) @@ -453,7 +424,7 @@ impl RegistrationChainInner { check_validation_signature( cip509.validation_signature(), - &cip509.raw_aux_data(), + cip509.raw_aux_data(), signing_pk, cip509.report(), context, @@ -461,7 +432,7 @@ impl RegistrationChainInner { if cip509.txn_inputs_hash().is_none() { cip509.report().missing_field("txn inputs hash", context); - }; + } let Some(purpose) = cip509.purpose() else { cip509.report().missing_field("purpose", context); @@ -551,12 +522,11 @@ impl RegistrationChainInner { } return Some(new_inner.update_cause_another_chain(cip509)); - } else { - cip509 - .report() - .missing_field("previous transaction ID", context); - return None; } + cip509 + .report() + .missing_field("previous transaction ID", context); + return None; }; // Previous transaction ID in the CIP509 should equal to the current transaction ID @@ -586,7 +556,7 @@ impl RegistrationChainInner { if cip509.txn_inputs_hash().is_none() { cip509.report().missing_field("txn inputs hash", context); - }; + } let Some(purpose) = cip509.purpose() else { cip509.report().missing_field("purpose", context); @@ -749,6 +719,32 @@ fn check_validation_signature( } } +/// Checks that a new registration doesn't contain a signing key that was used by any +/// other chain. +async fn check_signing_pk( + cat_id: &CatalystId, + cip509: &Cip509, + state: &State, +) -> anyhow::Result<()> +where + State: RBACState, +{ + for role in cip509.all_roles() { + if let Some(key) = cip509.signing_pk_for_role(role) { + if let Some(previous) = state.chain_catalyst_id_from_public_key(&key).await? { + if &previous != cat_id { + cip509.report().functional_validation( + &format!("An update to {cat_id} registration chain uses the same public key ({key:?}) as {previous} chain"), + "It isn't allowed to use role 0 signing (certificate subject public) key in different chains", + ); + } + } + } + } + + Ok(()) +} + #[cfg(test)] mod test { use catalyst_types::catalyst_id::role_index::RoleId; From ebcbf32095c887c6e22dac39d8c9fb547f241e68 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 17 Nov 2025 11:01:51 +0700 Subject: [PATCH 16/30] wip --- .../src/cardano/cip509/utils/cip134_uri_set.rs | 4 ++-- rust/rbac-registration/src/cardano/state.rs | 13 +++---------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs b/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs index 1a4f5c4786a..deb80894593 100644 --- a/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs +++ b/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs @@ -39,7 +39,7 @@ struct Cip0134UriSetInner { x_uris: UrisMap, /// URIs from c509 certificates. c_uris: UrisMap, - /// URIs which are taken by another certificates. + /// URIs which are taken by another chains. taken: HashSet, } @@ -221,7 +221,7 @@ impl Cip0134UriSet { /// Return the updated URIs set where the provided URIs were taken by other /// registration chains. /// - /// Updates the current URI set by removing the taken URIs from it. + /// Updates the current URI set by marking URIs as taken. #[must_use] pub fn update_taken_uris( self, diff --git a/rust/rbac-registration/src/cardano/state.rs b/rust/rbac-registration/src/cardano/state.rs index 42d4e29020e..7cd960c3eda 100644 --- a/rust/rbac-registration/src/cardano/state.rs +++ b/rust/rbac-registration/src/cardano/state.rs @@ -3,7 +3,7 @@ use std::future::Future; -use cardano_blockchain_types::{hashes::TransactionId, StakeAddress}; +use cardano_blockchain_types::StakeAddress; use catalyst_types::catalyst_id::CatalystId; use ed25519_dalek::VerifyingKey; @@ -37,15 +37,8 @@ pub trait RBACState { key: &VerifyingKey, ) -> impl Future>> + Send; - /// Returns a corresponding to the RBAC chain's Catalyst ID corresponding by the given - /// transaction hash. - fn chain_catalyst_id_from_txn_id( - &self, - txn_id: &TransactionId, - ) -> impl Future>> + Send; - - /// Update the update by "taking" the given `StakeAddress` for the corresponding RBAC - /// chain's by the given `CatalystId`. + /// Update the chain by "taking" the given `StakeAddress` for the corresponding + /// RBAC chain's by the given `CatalystId`. fn take_stake_address_from_chain( &mut self, id: &CatalystId, From 39817659b01eb2f8a4dc40fc5d69dc355fecf362 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 17 Nov 2025 11:17:42 +0700 Subject: [PATCH 17/30] wip --- rust/rbac-registration/src/registration/cardano/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs index 45e784a98fb..489b0d55a8b 100644 --- a/rust/rbac-registration/src/registration/cardano/mod.rs +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -497,11 +497,11 @@ impl RegistrationChainInner { if self.latest_applied().point() >= cip509.origin().point() { cip509.report().functional_validation( &format!( - "The provided registration is earlier {} than the current one {}", + "The provided registration is earlier {} than the latest applied one {}", cip509.origin().point(), self.current_tx_id_hash.point() ), - "Provided registrations must be applied in the correct order.", + "Registrations must be applied in the correct ascending order.", ); return None; } From 00eacd827082966c052991521724e4371565e8c8 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 17 Nov 2025 11:46:12 +0700 Subject: [PATCH 18/30] wip --- rust/rbac-registration/src/cardano/state.rs | 2 +- rust/rbac-registration/src/registration/cardano/mod.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/rust/rbac-registration/src/cardano/state.rs b/rust/rbac-registration/src/cardano/state.rs index 7cd960c3eda..f1d8e6ca2c5 100644 --- a/rust/rbac-registration/src/cardano/state.rs +++ b/rust/rbac-registration/src/cardano/state.rs @@ -10,7 +10,7 @@ use ed25519_dalek::VerifyingKey; use crate::registration::cardano::RegistrationChain; /// RBAC chains state trait -pub trait RBACState { +pub trait RbacChainsState { /// Returns RBAC chain for the given Catalyst ID. fn chain( &self, diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs index 489b0d55a8b..76fee7b1850 100644 --- a/rust/rbac-registration/src/registration/cardano/mod.rs +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -27,7 +27,7 @@ use crate::cardano::{ CertKeyHash, CertOrPk, Cip0134UriSet, Cip509, PaymentHistory, PointData, PointTxnIdx, RoleData, RoleDataRecord, ValidationSignature, }, - state::RBACState, + state::RbacChainsState, }; /// Registration chains. @@ -52,7 +52,7 @@ impl RegistrationChain { state: &mut State, ) -> anyhow::Result> where - State: RBACState, + State: RbacChainsState, { let Some(new_chain) = Self::new_stateless(cip509) else { return Ok(None); @@ -107,7 +107,7 @@ impl RegistrationChain { state: &State, ) -> anyhow::Result> where - State: RBACState, + State: RbacChainsState, { let Some(new_chain) = self.update_stateless(cip509) else { return Ok(None); @@ -727,7 +727,7 @@ async fn check_signing_pk( state: &State, ) -> anyhow::Result<()> where - State: RBACState, + State: RbacChainsState, { for role in cip509.all_roles() { if let Some(key) = cip509.signing_pk_for_role(role) { From 2c8157c3272f39ecd9400d709e68fe04ae096fc2 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 17 Nov 2025 12:08:34 +0700 Subject: [PATCH 19/30] wip --- rust/rbac-registration/src/cardano/state.rs | 4 ++-- rust/rbac-registration/src/registration/cardano/mod.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/rbac-registration/src/cardano/state.rs b/rust/rbac-registration/src/cardano/state.rs index f1d8e6ca2c5..fe007e73999 100644 --- a/rust/rbac-registration/src/cardano/state.rs +++ b/rust/rbac-registration/src/cardano/state.rs @@ -31,8 +31,8 @@ pub trait RbacChainsState { ) -> impl Future>> + Send; /// Returns a corresponding to the RBAC chain's Catalyst ID corresponding by the given - /// public key. - fn chain_catalyst_id_from_public_key( + /// signing public key. + fn chain_catalyst_id_from_signing_pk( &self, key: &VerifyingKey, ) -> impl Future>> + Send; diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs index 76fee7b1850..47c31d9573f 100644 --- a/rust/rbac-registration/src/registration/cardano/mod.rs +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -731,7 +731,7 @@ where { for role in cip509.all_roles() { if let Some(key) = cip509.signing_pk_for_role(role) { - if let Some(previous) = state.chain_catalyst_id_from_public_key(&key).await? { + if let Some(previous) = state.chain_catalyst_id_from_signing_pk(&key).await? { if &previous != cat_id { cip509.report().functional_validation( &format!("An update to {cat_id} registration chain uses the same public key ({key:?}) as {previous} chain"), From 9cc9bbe93a6725ebc38a60b22a37a84e542be027 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 17 Nov 2025 13:07:50 +0700 Subject: [PATCH 20/30] cleanup --- .../src/cardano/cip509/cip509.rs | 20 +++++------ .../cardano/cip509/utils/cip134_uri_set.rs | 34 +++++++++++-------- rust/rbac-registration/src/cardano/state.rs | 18 +++++----- .../src/registration/cardano/mod.rs | 32 +++++++++-------- 4 files changed, 53 insertions(+), 51 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/cip509.rs b/rust/rbac-registration/src/cardano/cip509/cip509.rs index 840de2c7482..64acb538411 100644 --- a/rust/rbac-registration/src/cardano/cip509/cip509.rs +++ b/rust/rbac-registration/src/cardano/cip509/cip509.rs @@ -319,18 +319,6 @@ impl Cip509 { self.catalyst_id.as_ref() } - /// Returns a list of addresses extracted from certificate URIs of a specific role. - #[must_use] - pub fn certificate_addresses( - &self, - role: usize, - ) -> HashSet
{ - self.metadata - .as_ref() - .map(|m| m.certificate_uris.role_addresses(role)) - .unwrap_or_default() - } - /// Return validation signature. #[must_use] pub fn validation_signature(&self) -> Option<&ValidationSignature> { @@ -349,6 +337,14 @@ impl Cip509 { self.metadata.as_ref() } + /// Returns a set of addresses. + #[must_use] + pub fn addresses(&self) -> HashSet
{ + self.certificate_uris() + .map(Cip0134UriSet::addresses) + .unwrap_or_default() + } + /// Returns a set of stake addresses. #[must_use] pub fn stake_addresses(&self) -> HashSet { diff --git a/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs b/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs index deb80894593..ff6dfda9528 100644 --- a/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs +++ b/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs @@ -63,13 +63,13 @@ impl Cip0134UriSet { /// Returns a mapping from the x509 certificate index to URIs contained within. #[must_use] - pub fn x_uris(&self) -> &UrisMap { + pub(crate) fn x_uris(&self) -> &UrisMap { &self.0.x_uris } /// Returns a mapping from the c509 certificate index to URIs contained within. #[must_use] - pub fn c_uris(&self) -> &UrisMap { + pub(crate) fn c_uris(&self) -> &UrisMap { &self.0.c_uris } @@ -88,34 +88,34 @@ impl Cip0134UriSet { self.x_uris().is_empty() && self.c_uris().is_empty() } - /// Returns a list of addresses by the given role. + /// Returns a list of active (without taken) URIs by the given role. #[must_use] - pub fn role_addresses( + pub(crate) fn role_uris( &self, role: usize, - ) -> HashSet
{ + ) -> HashSet { let mut result = HashSet::new(); if let Some(uris) = self.x_uris().get(&role) { - result.extend(uris.iter().map(|uri| uri.address().clone())); + result.extend(uris.iter().filter(|v| !self.0.taken.contains(v)).cloned()); } if let Some(uris) = self.c_uris().get(&role) { - result.extend(uris.iter().map(|uri| uri.address().clone())); + result.extend(uris.iter().filter(|v| !self.0.taken.contains(v)).cloned()); } result } - /// Returns a set of stake addresses by the given role. + /// Returns a set of active (without taken) stake addresses by the given role. #[must_use] - pub fn role_stake_addresses( + pub(crate) fn role_stake_addresses( &self, role: usize, ) -> HashSet { - self.role_addresses(role) + self.role_uris(role) .iter() - .filter_map(|address| { - match address { + .filter_map(|uri| { + match uri.address() { Address::Stake(a) => Some(a.clone().into()), _ => None, } @@ -123,7 +123,13 @@ impl Cip0134UriSet { .collect() } - /// Returns a set of all stake addresses. + /// Returns a set of all active (without taken) addresses. + #[must_use] + pub fn addresses(&self) -> HashSet
{ + self.values().map(Cip0134Uri::address).cloned().collect() + } + + /// Returns a set of all active (without taken) stake addresses. #[must_use] pub fn stake_addresses(&self) -> HashSet { self.values() @@ -401,7 +407,7 @@ mod tests { let set = cip509.certificate_uris().unwrap(); assert!(!set.is_empty()); assert!(set.c_uris().is_empty()); - assert_eq!(set.role_addresses(0).len(), 1); + assert_eq!(set.role_uris(0).len(), 1); assert_eq!(set.role_stake_addresses(0).len(), 1); assert_eq!(set.stake_addresses().len(), 1); diff --git a/rust/rbac-registration/src/cardano/state.rs b/rust/rbac-registration/src/cardano/state.rs index fe007e73999..9853adca380 100644 --- a/rust/rbac-registration/src/cardano/state.rs +++ b/rust/rbac-registration/src/cardano/state.rs @@ -3,7 +3,7 @@ use std::future::Future; -use cardano_blockchain_types::StakeAddress; +use cardano_blockchain_types::pallas_addresses::Address; use catalyst_types::catalyst_id::CatalystId; use ed25519_dalek::VerifyingKey; @@ -23,12 +23,11 @@ pub trait RbacChainsState { id: &CatalystId, ) -> impl Future> + Send; - /// Returns a current valid RBAC chain Catalyst ID corresponding to the given stake - /// address. - fn chain_catalyst_id_from_stake_address( + /// Returns `true` if a provided address already used by any RBAC chain. + fn is_addressed_used( &self, - address: &StakeAddress, - ) -> impl Future>> + Send; + address: &Address, + ) -> impl Future> + Send; /// Returns a corresponding to the RBAC chain's Catalyst ID corresponding by the given /// signing public key. @@ -37,11 +36,10 @@ pub trait RbacChainsState { key: &VerifyingKey, ) -> impl Future>> + Send; - /// Update the chain by "taking" the given `StakeAddress` for the corresponding + /// Update the chain by "taking" the given `Address` for the corresponding /// RBAC chain's by the given `CatalystId`. - fn take_stake_address_from_chain( + fn take_address_from_chains( &mut self, - id: &CatalystId, - address: &StakeAddress, + addresses: impl Iterator, ) -> impl Future> + Send; } diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs index 47c31d9573f..b3df72b74be 100644 --- a/rust/rbac-registration/src/registration/cardano/mod.rs +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -9,7 +9,9 @@ use std::{ use anyhow::Context; use c509_certificate::c509::C509; -use cardano_blockchain_types::{hashes::TransactionId, Point, StakeAddress, TxnIndex}; +use cardano_blockchain_types::{ + hashes::TransactionId, pallas_addresses::Address, Point, StakeAddress, TxnIndex, +}; use catalyst_types::{ catalyst_id::{key_rotation::KeyRotation, role_index::RoleId, CatalystId}, conversion::zero_out_last_n_bytes, @@ -75,11 +77,9 @@ impl RegistrationChain { return Ok(None); } - for address in &cip509.stake_addresses() { - if let Some(id) = state.chain_catalyst_id_from_stake_address(address).await? { - state.take_stake_address_from_chain(&id, address).await?; - } - } + state + .take_address_from_chains(cip509.addresses().into_iter()) + .await?; Ok(Some(new_chain)) } @@ -114,18 +114,14 @@ impl RegistrationChain { }; // Check that addresses from the new registration aren't used in other chains. - let previous_addresses = self.stake_addresses(); - let reg_addresses = cip509.stake_addresses(); + let previous_addresses = self.addresses(); + let reg_addresses = cip509.addresses(); let new_addresses: Vec<_> = reg_addresses.difference(&previous_addresses).collect(); for address in &new_addresses { - if state - .chain_catalyst_id_from_stake_address(address) - .await? - .is_some() - { + if state.is_addressed_used(address).await? { cip509.report().functional_validation( - &format!("{address} stake addresses is already used"), - "It isn't allowed to use same stake address in multiple registration chains, if its not a new chain", + &format!("{address} addresses is already used"), + "It isn't allowed to use same address in multiple registration chains, if its not a new chain", ); } } @@ -317,6 +313,12 @@ impl RegistrationChain { .and_then(|rdr| rdr.encryption_key_from_rotation(rotation)) } + /// Returns all addresses associated to this chain. + #[must_use] + pub fn addresses(&self) -> HashSet
{ + self.inner.certificate_uris.addresses() + } + /// Returns all stake addresses associated to this chain. #[must_use] pub fn stake_addresses(&self) -> HashSet { From ca4044bc2dadc60e80139588ecaa4f87fc134a37 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 17 Nov 2025 13:22:31 +0700 Subject: [PATCH 21/30] fix test --- rust/rbac-registration/src/registration/cardano/mod.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs index b3df72b74be..e0968cd6e37 100644 --- a/rust/rbac-registration/src/registration/cardano/mod.rs +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -792,12 +792,7 @@ mod test { let report = registration.report().to_owned(); assert!(chain.update_stateless(®istration).is_none()); - let report = format!("{report:?}"); - assert!( - report.contains("kind: InvalidValue { field: \"previous transaction ID\""), - "{}", - report - ); + assert!(report.is_problematic(), "{:?}", report); // Add the second registration. let data = test::block_6(); From e76385ba2b7134621db61893fc783b585cc29d26 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 17 Nov 2025 13:23:13 +0700 Subject: [PATCH 22/30] fix clippy --- rust/rbac-registration/src/registration/cardano/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs index e0968cd6e37..a26e1514de3 100644 --- a/rust/rbac-registration/src/registration/cardano/mod.rs +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -792,7 +792,7 @@ mod test { let report = registration.report().to_owned(); assert!(chain.update_stateless(®istration).is_none()); - assert!(report.is_problematic(), "{:?}", report); + assert!(report.is_problematic(), "{report:?}"); // Add the second registration. let data = test::block_6(); From c0918036e657b6de4eae80d436169aa834596985 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 17 Nov 2025 15:37:07 +0700 Subject: [PATCH 23/30] wip --- .../src/cardano/cip509/cip509.rs | 8 --- .../cardano/cip509/utils/cip134_uri_set.rs | 61 ++++++++----------- rust/rbac-registration/src/cardano/state.rs | 12 ++-- .../src/registration/cardano/mod.rs | 22 +++---- 4 files changed, 40 insertions(+), 63 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/cip509.rs b/rust/rbac-registration/src/cardano/cip509/cip509.rs index 64acb538411..07bebd6f763 100644 --- a/rust/rbac-registration/src/cardano/cip509/cip509.rs +++ b/rust/rbac-registration/src/cardano/cip509/cip509.rs @@ -337,14 +337,6 @@ impl Cip509 { self.metadata.as_ref() } - /// Returns a set of addresses. - #[must_use] - pub fn addresses(&self) -> HashSet
{ - self.certificate_uris() - .map(Cip0134UriSet::addresses) - .unwrap_or_default() - } - /// Returns a set of stake addresses. #[must_use] pub fn stake_addresses(&self) -> HashSet { diff --git a/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs b/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs index ff6dfda9528..b59a5bd0d1f 100644 --- a/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs +++ b/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs @@ -39,8 +39,8 @@ struct Cip0134UriSetInner { x_uris: UrisMap, /// URIs from c509 certificates. c_uris: UrisMap, - /// URIs which are taken by another chains. - taken: HashSet, + /// `StakeAddress` which are taken by another chains. + taken_stake_addresses: HashSet, } impl Cip0134UriSet { @@ -53,11 +53,11 @@ impl Cip0134UriSet { ) -> Self { let x_uris = extract_x509_uris(x509_certs, report); let c_uris = extract_c509_uris(c509_certs, report); - let taken_uris = HashSet::new(); + let taken_stake_addresses = HashSet::new(); Self(Arc::new(Cip0134UriSetInner { x_uris, c_uris, - taken: taken_uris, + taken_stake_addresses, })) } @@ -73,13 +73,12 @@ impl Cip0134UriSet { &self.0.c_uris } - /// Returns an iterator over of active (without taken) `Cip0134Uri`. + /// Returns an iterator over of `Cip0134Uri`. pub(crate) fn values(&self) -> impl Iterator { self.x_uris() .values() .chain(self.c_uris().values()) .flat_map(|uris| uris.iter()) - .filter(|v| !self.0.taken.contains(v)) } /// Returns `true` if both x509 and c509 certificate maps are empty. @@ -88,7 +87,7 @@ impl Cip0134UriSet { self.x_uris().is_empty() && self.c_uris().is_empty() } - /// Returns a list of active (without taken) URIs by the given role. + /// Returns a list of URIs by the given role. #[must_use] pub(crate) fn role_uris( &self, @@ -97,16 +96,16 @@ impl Cip0134UriSet { let mut result = HashSet::new(); if let Some(uris) = self.x_uris().get(&role) { - result.extend(uris.iter().filter(|v| !self.0.taken.contains(v)).cloned()); + result.extend(uris.iter().cloned()); } if let Some(uris) = self.c_uris().get(&role) { - result.extend(uris.iter().filter(|v| !self.0.taken.contains(v)).cloned()); + result.extend(uris.iter().cloned()); } result } - /// Returns a set of active (without taken) stake addresses by the given role. + /// Returns a set of stake addresses by the given role. #[must_use] pub(crate) fn role_stake_addresses( &self, @@ -123,12 +122,6 @@ impl Cip0134UriSet { .collect() } - /// Returns a set of all active (without taken) addresses. - #[must_use] - pub fn addresses(&self) -> HashSet
{ - self.values().map(Cip0134Uri::address).cloned().collect() - } - /// Returns a set of all active (without taken) stake addresses. #[must_use] pub fn stake_addresses(&self) -> HashSet { @@ -173,7 +166,7 @@ impl Cip0134UriSet { let Cip0134UriSetInner { mut x_uris, mut c_uris, - taken: mut taken_uris, + mut taken_stake_addresses, } = Arc::unwrap_or_clone(self.0); for (index, cert) in metadata.x509_certs.iter().enumerate() { @@ -186,9 +179,6 @@ impl Cip0134UriSet { }, X509DerCert::X509Cert(_) => { if let Some(uris) = metadata.certificate_uris.x_uris().get(&index) { - uris.iter().for_each(|v| { - taken_uris.remove(v); - }); x_uris.insert(index, uris.clone()); } }, @@ -208,19 +198,24 @@ impl Cip0134UriSet { }, C509Cert::C509Certificate(_) => { if let Some(uris) = metadata.certificate_uris.c_uris().get(&index) { - uris.iter().for_each(|v| { - taken_uris.remove(v); - }); c_uris.insert(index, uris.clone()); } }, } } + metadata + .certificate_uris + .stake_addresses() + .iter() + .for_each(|v| { + taken_stake_addresses.remove(v); + }); + Self(Arc::new(Cip0134UriSetInner { x_uris, c_uris, - taken: taken_uris, + taken_stake_addresses, })) } @@ -233,27 +228,25 @@ impl Cip0134UriSet { self, reg: &Cip509RbacMetadata, ) -> Self { - let current_uris_set = self.values().collect::>(); - - let latest_taken_uris = reg + let current_stake_addresses = self.stake_addresses(); + let latest_taken_stake_addresses = reg .certificate_uris - .values() - .filter(|v| current_uris_set.contains(v)) - .cloned() - .collect::>(); + .stake_addresses() + .into_iter() + .filter(|v| current_stake_addresses.contains(&v)); let Cip0134UriSetInner { x_uris, c_uris, - taken: mut taken_uris, + mut taken_stake_addresses, } = Arc::unwrap_or_clone(self.0); - taken_uris.extend(latest_taken_uris); + taken_stake_addresses.extend(latest_taken_stake_addresses); Self(Arc::new(Cip0134UriSetInner { x_uris, c_uris, - taken: taken_uris, + taken_stake_addresses, })) } } diff --git a/rust/rbac-registration/src/cardano/state.rs b/rust/rbac-registration/src/cardano/state.rs index 9853adca380..55822868adb 100644 --- a/rust/rbac-registration/src/cardano/state.rs +++ b/rust/rbac-registration/src/cardano/state.rs @@ -3,7 +3,7 @@ use std::future::Future; -use cardano_blockchain_types::pallas_addresses::Address; +use cardano_blockchain_types::StakeAddress; use catalyst_types::catalyst_id::CatalystId; use ed25519_dalek::VerifyingKey; @@ -24,9 +24,9 @@ pub trait RbacChainsState { ) -> impl Future> + Send; /// Returns `true` if a provided address already used by any RBAC chain. - fn is_addressed_used( + fn is_stake_address_used( &self, - address: &Address, + addr: &StakeAddress, ) -> impl Future> + Send; /// Returns a corresponding to the RBAC chain's Catalyst ID corresponding by the given @@ -36,10 +36,10 @@ pub trait RbacChainsState { key: &VerifyingKey, ) -> impl Future>> + Send; - /// Update the chain by "taking" the given `Address` for the corresponding + /// Update the chain by "taking" the given `StakeAddress` for the corresponding /// RBAC chain's by the given `CatalystId`. - fn take_address_from_chains( + fn take_stake_address_from_chains( &mut self, - addresses: impl Iterator, + addresses: impl Iterator, ) -> impl Future> + Send; } diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs index a26e1514de3..bed726762ff 100644 --- a/rust/rbac-registration/src/registration/cardano/mod.rs +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -9,9 +9,7 @@ use std::{ use anyhow::Context; use c509_certificate::c509::C509; -use cardano_blockchain_types::{ - hashes::TransactionId, pallas_addresses::Address, Point, StakeAddress, TxnIndex, -}; +use cardano_blockchain_types::{hashes::TransactionId, Point, StakeAddress, TxnIndex}; use catalyst_types::{ catalyst_id::{key_rotation::KeyRotation, role_index::RoleId, CatalystId}, conversion::zero_out_last_n_bytes, @@ -78,7 +76,7 @@ impl RegistrationChain { } state - .take_address_from_chains(cip509.addresses().into_iter()) + .take_stake_address_from_chains(cip509.stake_addresses().into_iter()) .await?; Ok(Some(new_chain)) @@ -114,14 +112,14 @@ impl RegistrationChain { }; // Check that addresses from the new registration aren't used in other chains. - let previous_addresses = self.addresses(); - let reg_addresses = cip509.addresses(); + let previous_addresses = self.stake_addresses(); + let reg_addresses = cip509.stake_addresses(); let new_addresses: Vec<_> = reg_addresses.difference(&previous_addresses).collect(); for address in &new_addresses { - if state.is_addressed_used(address).await? { + if state.is_stake_address_used(address).await? { cip509.report().functional_validation( - &format!("{address} addresses is already used"), - "It isn't allowed to use same address in multiple registration chains, if its not a new chain", + &format!("{address} stake address is already used"), + "It isn't allowed to use same stake address in multiple registration chains, if its not a new chain", ); } } @@ -313,12 +311,6 @@ impl RegistrationChain { .and_then(|rdr| rdr.encryption_key_from_rotation(rotation)) } - /// Returns all addresses associated to this chain. - #[must_use] - pub fn addresses(&self) -> HashSet
{ - self.inner.certificate_uris.addresses() - } - /// Returns all stake addresses associated to this chain. #[must_use] pub fn stake_addresses(&self) -> HashSet { From 92a9612f1b54d0eafca85a14e3fae225e2c1c255 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 17 Nov 2025 15:58:10 +0700 Subject: [PATCH 24/30] wip --- rust/rbac-registration/src/cardano/cip509/cip509.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/rbac-registration/src/cardano/cip509/cip509.rs b/rust/rbac-registration/src/cardano/cip509/cip509.rs index 07bebd6f763..ec7a98271a6 100644 --- a/rust/rbac-registration/src/cardano/cip509/cip509.rs +++ b/rust/rbac-registration/src/cardano/cip509/cip509.rs @@ -233,7 +233,7 @@ impl Cip509 { /// Would return only signing public keys for the present certificates, /// if certificate marked as deleted or undefined it would be skipped. #[must_use] - pub(crate) fn signing_pk_for_role( + pub fn signing_pk_for_role( &self, role: RoleId, ) -> Option { From b0acd2dacd9dca8c693ecc73500f4fd9c5f78422 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Tue, 18 Nov 2025 15:54:10 +0700 Subject: [PATCH 25/30] fix --- rust/rbac-registration/src/cardano/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/rbac-registration/src/cardano/state.rs b/rust/rbac-registration/src/cardano/state.rs index 55822868adb..c5007bff42c 100644 --- a/rust/rbac-registration/src/cardano/state.rs +++ b/rust/rbac-registration/src/cardano/state.rs @@ -40,6 +40,6 @@ pub trait RbacChainsState { /// RBAC chain's by the given `CatalystId`. fn take_stake_address_from_chains( &mut self, - addresses: impl Iterator, + addresses: impl Iterator + Send, ) -> impl Future> + Send; } From a7856ef22e13507331be631aa39b05571c049bb8 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Tue, 18 Nov 2025 16:28:58 +0700 Subject: [PATCH 26/30] wip --- rust/rbac-registration/src/registration/cardano/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs index bed726762ff..a025fdab9bb 100644 --- a/rust/rbac-registration/src/registration/cardano/mod.rs +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -85,7 +85,7 @@ impl RegistrationChain { /// Create a new instance of registration chain. /// The first new value should be the chain root. #[must_use] - fn new_stateless(cip509: &Cip509) -> Option { + pub fn new_stateless(cip509: &Cip509) -> Option { let inner = RegistrationChainInner::new(cip509)?; Some(Self { @@ -138,7 +138,7 @@ impl RegistrationChain { /// # Arguments /// - `cip509` - The CIP509. #[must_use] - fn update_stateless( + pub fn update_stateless( &self, cip509: &Cip509, ) -> Option { From 9417c0bef1e275fbf1f2972534d10cf508f74ca8 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Tue, 18 Nov 2025 16:49:43 +0700 Subject: [PATCH 27/30] fix clippy --- .../src/cardano/cip509/utils/cip134_uri_set.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs b/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs index b59a5bd0d1f..8e6210f0954 100644 --- a/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs +++ b/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs @@ -233,7 +233,7 @@ impl Cip0134UriSet { .certificate_uris .stake_addresses() .into_iter() - .filter(|v| current_stake_addresses.contains(&v)); + .filter(|v| current_stake_addresses.contains(v)); let Cip0134UriSetInner { x_uris, From b909b29062748cbe7e47c67e7b7fb4470af09629 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Wed, 19 Nov 2025 10:56:17 +0700 Subject: [PATCH 28/30] fix --- rust/rbac-registration/src/cardano/cip509/cip509.rs | 2 +- .../src/cardano/cip509/utils/cip134_uri_set.rs | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/cip509.rs b/rust/rbac-registration/src/cardano/cip509/cip509.rs index ec7a98271a6..748635828c6 100644 --- a/rust/rbac-registration/src/cardano/cip509/cip509.rs +++ b/rust/rbac-registration/src/cardano/cip509/cip509.rs @@ -303,7 +303,7 @@ impl Cip509 { /// Returns URIs contained in both x509 and c509 certificates of `Cip509` metadata. #[must_use] - pub fn certificate_uris(&self) -> Option<&Cip0134UriSet> { + pub(crate) fn certificate_uris(&self) -> Option<&Cip0134UriSet> { self.metadata.as_ref().map(|m| &m.certificate_uris) } diff --git a/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs b/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs index 8e6210f0954..b71c04f6c3e 100644 --- a/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs +++ b/rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs @@ -46,7 +46,7 @@ struct Cip0134UriSetInner { impl Cip0134UriSet { /// Creates a new `Cip0134UriSet` instance from the given certificates. #[must_use] - pub fn new( + pub(crate) fn new( x509_certs: &[X509DerCert], c509_certs: &[C509Cert], report: &ProblemReport, @@ -82,8 +82,8 @@ impl Cip0134UriSet { } /// Returns `true` if both x509 and c509 certificate maps are empty. - #[must_use] - pub fn is_empty(&self) -> bool { + #[cfg(test)] + pub(crate) fn is_empty(&self) -> bool { self.x_uris().is_empty() && self.c_uris().is_empty() } @@ -124,7 +124,7 @@ impl Cip0134UriSet { /// Returns a set of all active (without taken) stake addresses. #[must_use] - pub fn stake_addresses(&self) -> HashSet { + pub(crate) fn stake_addresses(&self) -> HashSet { self.values() .filter_map(|uri| { match uri.address() { @@ -132,6 +132,7 @@ impl Cip0134UriSet { _ => None, } }) + .filter(|v| !self.0.taken_stake_addresses.contains(v)) .collect() } @@ -154,7 +155,7 @@ impl Cip0134UriSet { /// 2: [uri_4] /// ``` #[must_use] - pub fn update( + pub(crate) fn update( self, metadata: &Cip509RbacMetadata, ) -> Self { @@ -224,7 +225,7 @@ impl Cip0134UriSet { /// /// Updates the current URI set by marking URIs as taken. #[must_use] - pub fn update_taken_uris( + pub(crate) fn update_taken_uris( self, reg: &Cip509RbacMetadata, ) -> Self { From 9d72d89cf030d8a41707fec2137e47cfa6183207 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Wed, 19 Nov 2025 11:35:38 +0700 Subject: [PATCH 29/30] wip --- .../src/registration/cardano/mod.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs index a025fdab9bb..927cbac0c1b 100644 --- a/rust/rbac-registration/src/registration/cardano/mod.rs +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -44,6 +44,9 @@ impl RegistrationChain { /// from a given CIP-509 registration, ensuring uniqueness of Catalyst ID, stake /// addresses, and associated public keys. /// + /// Returns `None` if the `cip509` is invalid by any reason, properly updating + /// `cip509.report()`. + /// /// # Errors /// - Propagates any I/O or provider-level errors encountered while checking key /// ownership (e.g., database lookup failures). @@ -84,6 +87,9 @@ impl RegistrationChain { /// Create a new instance of registration chain. /// The first new value should be the chain root. + /// + /// Returns `None` if the `cip509` is invalid by any reason, properly updating + /// `cip509.report()`. #[must_use] pub fn new_stateless(cip509: &Cip509) -> Option { let inner = RegistrationChainInner::new(cip509)?; @@ -96,6 +102,9 @@ impl RegistrationChain { /// Attempts to update an existing RBAC registration chain /// with a new CIP-509 registration, validating address and key usage consistency. /// + /// Returns `None` if the `cip509` is invalid by any reason, properly updating + /// `cip509.report()`. + /// /// # Errors /// - Propagates any I/O or provider-level errors encountered while checking key /// ownership (e.g., database lookup failures). @@ -135,8 +144,8 @@ impl RegistrationChain { /// Update the registration chain. /// - /// # Arguments - /// - `cip509` - The CIP509. + /// Returns `None` if the `cip509` is invalid by any reason, properly updating + /// `cip509.report()`. #[must_use] pub fn update_stateless( &self, From bbd9fd94c330b29226b562cf1b16b0ea30728f89 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Wed, 19 Nov 2025 18:10:35 +0700 Subject: [PATCH 30/30] wip --- rust/rbac-registration/src/cardano/cip509/cip509.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/rust/rbac-registration/src/cardano/cip509/cip509.rs b/rust/rbac-registration/src/cardano/cip509/cip509.rs index 748635828c6..f85defa5268 100644 --- a/rust/rbac-registration/src/cardano/cip509/cip509.rs +++ b/rust/rbac-registration/src/cardano/cip509/cip509.rs @@ -82,7 +82,8 @@ pub struct Cip509 { origin: PointTxnIdx, /// A catalyst ID. /// - /// This field is only present in role 0 registrations. + /// This field is only present in role 0 registrations and only for the first + /// registration, which defines a `CatalystId` for the chain. catalyst_id: Option, /// Raw aux data associated with the transaction that CIP509 is attached to, raw_aux_data: Vec, @@ -182,6 +183,12 @@ impl Cip509 { validate_self_sign_cert(metadata, &report); } + // We want to keep `catalyst_id` field only for the first registration, + // which starts a new chain + if cip509.prv_tx_id.is_some() { + cip509.catalyst_id = None; + } + Ok(Some(cip509)) } @@ -313,7 +320,8 @@ impl Cip509 { self.txn_inputs_hash.as_ref() } - /// Returns a Catalyst ID of this registration if role 0 is present. + /// Returns a Catalyst ID of this registration if role 0 is present and if its a first + /// registration, which defines a `CatalystId` for the chain. #[must_use] pub fn catalyst_id(&self) -> Option<&CatalystId> { self.catalyst_id.as_ref()