11//! Utilities for RBAC registrations validation.
22
3- use std:: collections:: HashSet ;
3+ use std:: collections:: { HashMap , HashSet } ;
44
55use anyhow:: { Context , Result } ;
66use cardano_chain_follower:: { hashes:: TransactionId , StakeAddress } ;
7- use catalyst_types:: problem_report:: ProblemReport ;
7+ use catalyst_types:: {
8+ catalyst_id:: { role_index:: RoleId , CatalystId } ,
9+ problem_report:: ProblemReport ,
10+ } ;
811use ed25519_dalek:: VerifyingKey ;
912
1013use crate :: {
1114 cardano:: cip509:: { Cip0134UriSet , Cip509 } ,
12- providers:: { RbacCacheProvider , RbacRegistrationProvider } ,
15+ providers:: RbacRegistrationProvider ,
1316 registration:: cardano:: {
1417 validation_result:: { RbacValidationError , RbacValidationResult , RbacValidationSuccess } ,
1518 RegistrationChain ,
@@ -24,7 +27,7 @@ async fn update_chain<Provider>(
2427 provider : & Provider ,
2528) -> RbacValidationResult
2629where
27- Provider : RbacRegistrationProvider + RbacCacheProvider ,
30+ Provider : RbacRegistrationProvider ,
2831{
2932 let purpose = reg. purpose ( ) ;
3033 let report = reg. report ( ) . to_owned ( ) ;
8689 } ) ;
8790 }
8891
89- if is_persistent {
90- provider. cache_persistent_rbac_chain ( catalyst_id. clone ( ) , new_chain) ;
91- }
92-
9392 Ok ( RbacValidationSuccess {
9493 catalyst_id,
9594 stake_addresses,
@@ -101,6 +100,104 @@ where
101100 } )
102101}
103102
103+ /// Tries to start a new RBAC chain.
104+ async fn start_new_chain < Provider > (
105+ reg : Cip509 ,
106+ is_persistent : bool ,
107+ provider : & Provider ,
108+ ) -> RbacValidationResult
109+ where
110+ Provider : RbacRegistrationProvider ,
111+ {
112+ let catalyst_id = reg. catalyst_id ( ) . map ( CatalystId :: as_short_id) ;
113+ let purpose = reg. purpose ( ) ;
114+ let report = reg. report ( ) . to_owned ( ) ;
115+
116+ // Try to start a new chain.
117+ let new_chain = RegistrationChain :: new ( reg) . ok_or_else ( || {
118+ if let Some ( catalyst_id) = catalyst_id {
119+ RbacValidationError :: InvalidRegistration {
120+ catalyst_id,
121+ purpose,
122+ report : report. clone ( ) ,
123+ }
124+ } else {
125+ RbacValidationError :: UnknownCatalystId
126+ }
127+ } ) ?;
128+
129+ // Verify that a Catalyst ID of this chain is unique.
130+ let catalyst_id = new_chain. catalyst_id ( ) . as_short_id ( ) ;
131+ if provider
132+ . is_chain_known ( catalyst_id. clone ( ) , is_persistent)
133+ . await ?
134+ {
135+ report. functional_validation (
136+ & format ! ( "{catalyst_id} is already used" ) ,
137+ "It isn't allowed to use same Catalyst ID (certificate subject public key) in multiple registration chains" ,
138+ ) ;
139+ return Err ( RbacValidationError :: InvalidRegistration {
140+ catalyst_id,
141+ purpose,
142+ report,
143+ } ) ;
144+ }
145+
146+ // Validate stake addresses.
147+ let new_addresses = new_chain. stake_addresses ( ) ;
148+ let mut updated_chains: HashMap < _ , HashSet < StakeAddress > > = HashMap :: new ( ) ;
149+ for address in & new_addresses {
150+ if let Some ( id) = provider
151+ . catalyst_id_from_stake_address ( address, is_persistent)
152+ . await ?
153+ {
154+ // If an address is used in existing chain then a new chain must have different role 0
155+ // signing key.
156+ let previous_chain = provider. chain ( id. clone ( ) , is_persistent)
157+ . await ?
158+ . context ( "{id} is present in 'catalyst_id_for_stake_address', but not in 'rbac_registration'" ) ?;
159+ if previous_chain. get_latest_signing_pk_for_role ( & RoleId :: Role0 )
160+ == new_chain. get_latest_signing_pk_for_role ( & RoleId :: Role0 )
161+ {
162+ report. functional_validation (
163+ & format ! ( "A new registration ({catalyst_id}) uses the same public key as the previous one ({})" ,
164+ previous_chain. catalyst_id( ) . as_short_id( )
165+ ) ,
166+ "It is only allowed to override the existing chain by using different public key" ,
167+ ) ;
168+ } else {
169+ // The new root registration "takes" an address(es) from the existing chain, so that
170+ // chain needs to be updated.
171+ updated_chains
172+ . entry ( id)
173+ . and_modify ( |e| {
174+ e. insert ( address. clone ( ) ) ;
175+ } )
176+ . or_insert ( [ address. clone ( ) ] . into_iter ( ) . collect ( ) ) ;
177+ }
178+ }
179+ }
180+
181+ // Check that new public keys aren't used by other chains.
182+ let public_keys = validate_public_keys ( & new_chain, is_persistent, & report, provider) . await ?;
183+
184+ if report. is_problematic ( ) {
185+ return Err ( RbacValidationError :: InvalidRegistration {
186+ catalyst_id,
187+ purpose,
188+ report,
189+ } ) ;
190+ }
191+
192+ Ok ( RbacValidationSuccess {
193+ catalyst_id,
194+ stake_addresses : new_addresses,
195+ public_keys,
196+ modified_chains : updated_chains. into_iter ( ) . collect ( ) ,
197+ purpose,
198+ } )
199+ }
200+
104201/// Checks that a new registration doesn't contain a signing key that was used by any
105202/// other chain. Returns a list of public keys in the registration.
106203async fn validate_public_keys < Provider > (
0 commit comments