22
33use std:: collections:: { HashMap , HashSet } ;
44
5- use anyhow:: { Context , Result } ;
6- use cardano_blockchain_types:: { hashes :: TransactionId , StakeAddress } ;
5+ use anyhow:: Context ;
6+ use cardano_blockchain_types:: StakeAddress ;
77use catalyst_types:: {
88 catalyst_id:: { role_index:: RoleId , CatalystId } ,
99 problem_report:: ProblemReport ,
1010 uuid:: UuidV4 ,
1111} ;
12- use ed25519_dalek:: VerifyingKey ;
1312
1413use crate :: {
15- cardano:: cip509:: { Cip0134UriSet , Cip509 } ,
16- providers:: RbacRegistrationProvider ,
14+ cardano:: cip509:: Cip509 , providers:: RbacRegistrationProvider ,
1715 registration:: cardano:: RegistrationChain ,
1816} ;
1917
20- /// A return value of the `validate_rbac_registration` method.
21- pub type RbacValidationResult = Result < RbacValidationSuccess , RbacValidationError > ;
22-
2318/// An error returned from the `validate_rbac_registration` method.
2419#[ allow( clippy:: large_enum_variant) ]
2520pub enum RbacValidationError {
@@ -53,109 +48,6 @@ impl From<anyhow::Error> for RbacValidationError {
5348 }
5449}
5550
56- /// Represents the result yielded by `update_chain` or `start_new_chain` upon successful
57- /// execution.
58- pub struct RbacValidationSuccess {
59- /// A Catalyst ID of the chain this registration belongs to.
60- pub catalyst_id : CatalystId ,
61- /// A list of stake addresses that were added to the chain.
62- pub stake_addresses : HashSet < StakeAddress > ,
63- /// A list of role public keys used in this registration.
64- pub public_keys : HashSet < VerifyingKey > ,
65- /// A list of updates to other chains containing Catalyst IDs and removed stake
66- /// addresses.
67- ///
68- /// A new RBAC registration can take ownership of stake addresses of other chains.
69- pub modified_chains : Vec < ( CatalystId , HashSet < StakeAddress > ) > ,
70- /// An updated registration chain.
71- pub chain : RegistrationChain ,
72- }
73-
74- /// Attempts to update an existing RBAC registration chain
75- /// with a new CIP-509 registration, validating address and key usage consistency.
76- ///
77- /// # Returns
78- /// - `Ok((new_chain, validation_result))` if the chain was successfully updated and
79- /// validated.
80- ///
81- /// # Errors
82- /// - Returns [`RbacValidationError::UnknownCatalystId`] if no Catalyst chain is found for
83- /// `previous_txn`.
84- /// - Returns [`RbacValidationError::InvalidRegistration`] if address/key duplication or
85- /// validation inconsistencies are detected.
86- pub async fn update_chain < Provider > (
87- reg : Cip509 ,
88- previous_txn : TransactionId ,
89- provider : & Provider ,
90- ) -> RbacValidationResult
91- where
92- Provider : RbacRegistrationProvider ,
93- {
94- let purpose = reg. purpose ( ) ;
95- let report = reg. report ( ) . to_owned ( ) ;
96-
97- // Find a chain this registration belongs to.
98- let Some ( catalyst_id) = provider. catalyst_id_from_txn_id ( previous_txn) . await ? else {
99- // We are unable to determine a Catalyst ID, so there is no sense to update the problem
100- // report because we would be unable to store this registration anyway.
101- return Err ( RbacValidationError :: UnknownCatalystId ) ;
102- } ;
103- let chain = provider. chain ( catalyst_id. clone ( ) ) . await ?
104- . context ( "{catalyst_id} is present in 'catalyst_id_for_txn_id' table, but not in 'rbac_registration'" ) ?;
105-
106- // Check that addresses from the new registration aren't used in other chains.
107- let previous_addresses = chain. stake_addresses ( ) ;
108- let reg_addresses = cip509_stake_addresses ( & reg) ;
109- let new_addresses: Vec < _ > = reg_addresses. difference ( & previous_addresses) . collect ( ) ;
110- for address in & new_addresses {
111- match provider. catalyst_id_from_stake_address ( address) . await ? {
112- None => {
113- // All good: the address wasn't used before.
114- } ,
115- Some ( _) => {
116- report. functional_validation (
117- & format ! ( "{address} stake addresses is already used" ) ,
118- "It isn't allowed to use same stake address in multiple registration chains" ,
119- ) ;
120- } ,
121- }
122- }
123-
124- // Store values before consuming the registration.
125- let stake_addresses = cip509_stake_addresses ( & reg) ;
126-
127- // Try to add a new registration to the chain.
128- let new_chain = chain. update ( reg. clone ( ) ) . ok_or_else ( || {
129- RbacValidationError :: InvalidRegistration {
130- catalyst_id : catalyst_id. clone ( ) ,
131- purpose,
132- report : report. clone ( ) ,
133- }
134- } ) ?;
135-
136- // Check that new public keys aren't used by other chains.
137- let public_keys = validate_public_keys ( & new_chain, & report, provider) . await ?;
138-
139- // Return an error if any issues were recorded in the report.
140- if report. is_problematic ( ) {
141- return Err ( RbacValidationError :: InvalidRegistration {
142- catalyst_id,
143- purpose,
144- report,
145- } ) ;
146- }
147-
148- Ok ( RbacValidationSuccess {
149- catalyst_id,
150- stake_addresses,
151- public_keys,
152- // Only new chains can take ownership of stake addresses of existing chains, so in this
153- // case other chains aren't affected.
154- modified_chains : Vec :: new ( ) ,
155- chain : new_chain,
156- } )
157- }
158-
15951/// Attempts to initialize a new RBAC registration chain
16052/// from a given CIP-509 registration, ensuring uniqueness of Catalyst ID, stake
16153/// addresses, and associated public keys.
@@ -239,7 +131,7 @@ where
239131 }
240132
241133 // Check that new public keys aren't used by other chains.
242- let public_keys = validate_public_keys ( & new_chain , & report, provider) . await ?;
134+ let public_keys = new_chain . validate_public_keys ( & report, provider) . await ?;
243135
244136 if report. is_problematic ( ) {
245137 return Err ( RbacValidationError :: InvalidRegistration {
@@ -257,52 +149,3 @@ where
257149 chain : new_chain,
258150 } )
259151}
260-
261- /// Validates that none of the signing keys in a given RBAC registration chain
262- /// have been used by any other existing chain, ensuring global key uniqueness
263- /// across all Catalyst registrations.
264- ///
265- /// # Returns
266- /// Returns a [`Result<HashSet<VerifyingKey>>`] containing all unique public keys
267- /// extracted from the registration chain if validation passes successfully.
268- ///
269- /// # Errors
270- /// - Propagates any I/O or provider-level errors encountered while checking key ownership
271- /// (e.g., database lookup failures).
272- pub async fn validate_public_keys < Provider > (
273- chain : & RegistrationChain ,
274- report : & ProblemReport ,
275- provider : & Provider ,
276- ) -> Result < HashSet < VerifyingKey > >
277- where
278- Provider : RbacRegistrationProvider ,
279- {
280- let mut keys = HashSet :: new ( ) ;
281-
282- let roles: Vec < _ > = chain. role_data_history ( ) . keys ( ) . collect ( ) ;
283- let catalyst_id = chain. catalyst_id ( ) . as_short_id ( ) ;
284-
285- for role in roles {
286- if let Some ( ( key, _) ) = chain. get_latest_signing_pk_for_role ( role) {
287- keys. insert ( key) ;
288- if let Some ( previous) = provider. catalyst_id_from_public_key ( key) . await ? {
289- if previous != catalyst_id {
290- report. functional_validation (
291- & format ! ( "An update to {catalyst_id} registration chain uses the same public key ({key:?}) as {previous} chain" ) ,
292- "It isn't allowed to use role 0 signing (certificate subject public) key in different chains" ,
293- ) ;
294- }
295- }
296- }
297- }
298-
299- Ok ( keys)
300- }
301-
302- /// Returns a set of stake addresses in the given registration.
303- pub fn cip509_stake_addresses ( cip509 : & Cip509 ) -> HashSet < StakeAddress > {
304- cip509
305- . certificate_uris ( )
306- . map ( Cip0134UriSet :: stake_addresses)
307- . unwrap_or_default ( )
308- }
0 commit comments