@@ -50,8 +50,8 @@ impl RegistrationChain {
5050 ///
5151 /// Returns an error if data is invalid
5252 #[ must_use]
53- pub fn new ( cip509 : Cip509 ) -> Option < Self > {
54- let inner = RegistrationChainInner :: new ( cip509) ?;
53+ pub fn new_stateless ( cip509 : Cip509 ) -> Option < Self > {
54+ let inner = RegistrationChainInner :: new_stateless ( cip509) ?;
5555
5656 Some ( Self {
5757 inner : Arc :: new ( inner) ,
@@ -67,13 +67,13 @@ impl RegistrationChain {
6767 ///
6868 /// Returns an error if data is invalid
6969 #[ must_use]
70- pub fn update (
70+ pub fn update_stateless (
7171 & self ,
7272 cip509 : Cip509 ,
7373 ) -> Option < Self > {
7474 let latest_signing_pk = self . get_latest_signing_pk_for_role ( & RoleId :: Role0 ) ;
7575 let new_inner = if let Some ( ( signing_pk, _) ) = latest_signing_pk {
76- self . inner . update ( cip509, signing_pk) ?
76+ self . inner . update_stateless ( cip509, signing_pk) ?
7777 } else {
7878 cip509. report ( ) . missing_field (
7979 "latest signing key for role 0" ,
@@ -91,32 +91,17 @@ impl RegistrationChain {
9191 /// If the given registration references a previous transaction, it attempts
9292 /// to update the existing chain using that previous transaction.
9393 /// Otherwise, it starts a new chain from the provided registration.
94- ///
95- /// # Returns
96- /// - `Ok(Self)` if the chain was successfully initialized or updated and validated.
97- ///
98- /// # Errors
99- /// - [`RbacValidationError::UnknownCatalystId`] if no Catalyst chain can be found or
100- /// inferred.
101- /// - [`RbacValidationError::InvalidRegistration`] if any validation, address, or key
102- /// duplication inconsistencies are detected.
103- pub async fn update_from_previous_txn < Provider > (
94+ pub async fn update < Provider > (
10495 reg : Cip509 ,
10596 provider : & Provider ,
106- ) -> Result < ( Self , Vec < ( CatalystId , HashSet < StakeAddress > ) > ) , RbacValidationError >
97+ ) -> Option < Self >
10798 where
10899 Provider : RbacRegistrationProvider ,
109100 {
110- if let Some ( previous_txn) = reg. previous_transaction ( ) {
111- let result =
112- RegistrationChainInner :: update_from_previous_txn ( reg, previous_txn, provider)
113- . await ?;
114-
115- // Only new chains can take ownership of stake addresses of existing chains, so in this
116- // case other chains aren't affected.
117- Ok ( ( result, Vec :: new ( ) ) )
101+ if reg. previous_transaction ( ) . is_some ( ) {
102+ RegistrationChainInner :: update ( reg, provider) . await
118103 } else {
119- RegistrationChainInner :: start_from_provider ( reg, provider) . await
104+ RegistrationChainInner :: new ( reg, provider) . await
120105 }
121106 }
122107
@@ -135,13 +120,14 @@ impl RegistrationChain {
135120 & self ,
136121 report : & ProblemReport ,
137122 provider : & Provider ,
138- ) -> Result < ( ) , RbacValidationError >
123+ ) -> anyhow :: Result < bool >
139124 where
140125 Provider : RbacRegistrationProvider ,
141126 {
142127 let roles: Vec < _ > = self . role_data_history ( ) . keys ( ) . collect ( ) ;
143128 let catalyst_id = self . catalyst_id ( ) . as_short_id ( ) ;
144129
130+ let mut result = true ;
145131 for role in roles {
146132 if let Some ( ( key, _) ) = self . get_latest_signing_pk_for_role ( role) {
147133 if let Some ( previous) = provider. catalyst_id_from_public_key ( key) . await ? {
@@ -150,12 +136,14 @@ impl RegistrationChain {
150136 & format ! ( "An update to {catalyst_id} registration chain uses the same public key ({key:?}) as {previous} chain" ) ,
151137 "It isn't allowed to use role 0 signing (certificate subject public) key in different chains" ,
152138 ) ;
139+
140+ result = false
153141 }
154142 }
155143 }
156144 }
157145
158- Ok ( ( ) )
146+ Ok ( result )
159147 }
160148
161149 /// Returns a Catalyst ID.
@@ -331,39 +319,6 @@ impl RegistrationChain {
331319 }
332320}
333321
334- /// An error returned from the `validate_rbac_registration` method.
335- #[ allow( clippy:: large_enum_variant) ]
336- pub enum RbacValidationError {
337- /// A registration is invalid (`report.is_problematic()` returns `true`).
338- ///
339- /// This variant is inserted to the `rbac_invalid_registration` table.
340- InvalidRegistration {
341- /// A Catalyst ID.
342- catalyst_id : CatalystId ,
343- /// A registration purpose.
344- purpose : Option < UuidV4 > ,
345- /// A problem report.
346- report : ProblemReport ,
347- } ,
348- /// Unable to determine a Catalyst ID of the registration.
349- ///
350- /// This can happen if a previous transaction ID in the registration is incorrect.
351- UnknownCatalystId ,
352- /// A "fatal" error occurred during validation.
353- ///
354- /// This means that the validation wasn't performed properly (usually because of a
355- /// database failure) and we cannot process the given registration. This error is
356- /// propagated on a higher level, so there will be another attempt to index that
357- /// block.
358- Fatal ( anyhow:: Error ) ,
359- }
360-
361- impl From < anyhow:: Error > for RbacValidationError {
362- fn from ( e : anyhow:: Error ) -> Self {
363- RbacValidationError :: Fatal ( e)
364- }
365- }
366-
367322/// Inner structure of registration chain.
368323#[ derive( Debug , Clone ) ]
369324struct RegistrationChainInner {
@@ -411,7 +366,7 @@ impl RegistrationChainInner {
411366 ///
412367 /// Returns an error if data is invalid
413368 #[ must_use]
414- fn new ( cip509 : Cip509 ) -> Option < Self > {
369+ fn new_stateless ( cip509 : Cip509 ) -> Option < Self > {
415370 let context = "Registration Chain new" ;
416371 // Should be chain root, return immediately if not
417372 if cip509. previous_transaction ( ) . is_some ( ) {
@@ -517,7 +472,7 @@ impl RegistrationChainInner {
517472 ///
518473 /// Returns an error if data is invalid
519474 #[ must_use]
520- fn update (
475+ fn update_stateless (
521476 & self ,
522477 cip509 : Cip509 ,
523478 signing_pk : VerifyingKey ,
@@ -601,172 +556,45 @@ impl RegistrationChainInner {
601556
602557 /// Attempts to update an existing RBAC registration chain
603558 /// with a new CIP-509 registration, validating address and key usage consistency.
604- ///
605- /// # Returns
606- /// - `Ok((new_chain, validation_result))` if the chain was successfully updated and
607- /// validated.
608- ///
609- /// # Errors
610- /// - Returns [`RbacValidationError::UnknownCatalystId`] if no Catalyst chain is found
611- /// for `previous_txn`.
612- /// - Returns [`RbacValidationError::InvalidRegistration`] if address/key duplication
613- /// or validation inconsistencies are detected.
614- pub async fn update_from_previous_txn < Provider > (
559+ pub async fn update < Provider > (
615560 reg : Cip509 ,
616- previous_txn : TransactionId ,
617561 provider : & Provider ,
618- ) -> Result < RegistrationChain , RbacValidationError >
562+ ) -> Option < RegistrationChain >
619563 where
620564 Provider : RbacRegistrationProvider ,
621565 {
622- let purpose = reg. purpose ( ) ;
623- let report = reg. report ( ) . to_owned ( ) ;
624-
625- // Find a chain this registration belongs to.
626- let Some ( catalyst_id) = provider. catalyst_id_from_txn_id ( previous_txn) . await ? else {
627- // We are unable to determine a Catalyst ID, so there is no sense to update the problem
628- // report because we would be unable to store this registration anyway.
629- return Err ( RbacValidationError :: UnknownCatalystId ) ;
630- } ;
631- let chain = provider. chain ( catalyst_id. clone ( ) ) . await ?
632- . context ( "{catalyst_id} is present in 'catalyst_id_for_txn_id' table, but not in 'rbac_registration'" ) ?;
633-
634- // Check that addresses from the new registration aren't used in other chains.
635- let previous_addresses = chain. stake_addresses ( ) ;
636- let reg_addresses = reg. stake_addresses ( ) ;
637- let new_addresses: Vec < _ > = reg_addresses. difference ( & previous_addresses) . collect ( ) ;
638- for address in & new_addresses {
639- match provider. catalyst_id_from_stake_address ( address) . await ? {
640- None => {
641- // All good: the address wasn't used before.
642- } ,
643- Some ( _) => {
644- report. functional_validation (
645- & format ! ( "{address} stake addresses is already used" ) ,
646- "It isn't allowed to use same stake address in multiple registration chains" ,
647- ) ;
648- } ,
649- }
650- }
651-
652- // Try to add a new registration to the chain.
653- let new_chain = chain. update ( reg. clone ( ) ) . ok_or_else ( || {
654- RbacValidationError :: InvalidRegistration {
655- catalyst_id : catalyst_id. clone ( ) ,
656- purpose,
657- report : report. clone ( ) ,
658- }
659- } ) ?;
660-
661- // Check that new public keys aren't used by other chains.
662- new_chain. validate_public_keys ( & report, provider) . await ?;
663-
664- // Return an error if any issues were recorded in the report.
665- if report. is_problematic ( ) {
666- return Err ( RbacValidationError :: InvalidRegistration {
667- catalyst_id,
668- purpose,
669- report,
670- } ) ;
671- }
672-
673- Ok ( new_chain)
566+ // perform provider lookups and validations here
567+ // then build new RegistrationChain using update_stateless
568+ let previous_txn = reg. previous_transaction ( ) ?;
569+ let catalyst_id = provider
570+ . catalyst_id_from_txn_id ( previous_txn)
571+ . await
572+ . ok ( ) ??;
573+ let chain = provider. chain ( catalyst_id. clone ( ) ) . await . ok ( ) ??;
574+
575+ let latest_signing_pk = chain. get_latest_signing_pk_for_role ( & RoleId :: Role0 ) ?;
576+ let ( signing_pk, _) = latest_signing_pk;
577+
578+ let new_inner = chain. inner . update_stateless ( reg. clone ( ) , signing_pk) ?;
579+ Some ( RegistrationChain {
580+ inner : Arc :: new ( new_inner) ,
581+ } )
674582 }
675583
676584 /// Attempts to initialize a new RBAC registration chain
677585 /// from a given CIP-509 registration, ensuring uniqueness of Catalyst ID, stake
678586 /// addresses, and associated public keys.
679- ///
680- /// # Returns
681- /// - `Ok((new_chain, validation_result))` if the chain was successfully initialized
682- /// and validated.
683- ///
684- /// # Errors
685- /// - [`RbacValidationError::UnknownCatalystId`]: if `reg` lacks a valid Catalyst ID.
686- /// - [`RbacValidationError::InvalidRegistration`]: if any functional validation,
687- /// stake address conflict, or public key duplication occurs.
688- pub async fn start_from_provider < Provider > (
587+ pub async fn new < Provider > (
689588 reg : Cip509 ,
690- provider : & Provider ,
691- ) -> Result < ( RegistrationChain , Vec < ( CatalystId , HashSet < StakeAddress > ) > ) , RbacValidationError >
589+ _provider : & Provider ,
590+ ) -> Option < RegistrationChain >
692591 where
693592 Provider : RbacRegistrationProvider ,
694593 {
695- let catalyst_id = reg. catalyst_id ( ) . map ( CatalystId :: as_short_id) ;
696- let purpose = reg. purpose ( ) ;
697- let report = reg. report ( ) . to_owned ( ) ;
698-
699- // Try to start a new chain.
700- let new_chain = RegistrationChain :: new ( reg) . ok_or_else ( || {
701- if let Some ( catalyst_id) = catalyst_id {
702- RbacValidationError :: InvalidRegistration {
703- catalyst_id,
704- purpose,
705- report : report. clone ( ) ,
706- }
707- } else {
708- RbacValidationError :: UnknownCatalystId
709- }
710- } ) ?;
711-
712- // Verify that a Catalyst ID of this chain is unique.
713- let catalyst_id = new_chain. catalyst_id ( ) . as_short_id ( ) ;
714- if provider. is_chain_known ( catalyst_id. clone ( ) ) . await ? {
715- report. functional_validation (
716- & format ! ( "{catalyst_id} is already used" ) ,
717- "It isn't allowed to use same Catalyst ID (certificate subject public key) in multiple registration chains" ,
718- ) ;
719- return Err ( RbacValidationError :: InvalidRegistration {
720- catalyst_id,
721- purpose,
722- report,
723- } ) ;
724- }
725-
726- // Validate stake addresses.
727- let new_addresses = new_chain. stake_addresses ( ) ;
728- let mut updated_chains: HashMap < _ , HashSet < StakeAddress > > = HashMap :: new ( ) ;
729- for address in & new_addresses {
730- if let Some ( id) = provider. catalyst_id_from_stake_address ( address) . await ? {
731- // If an address is used in existing chain then a new chain must have different role
732- // 0 signing key.
733- let previous_chain = provider. chain ( id. clone ( ) )
734- . await ?
735- . context ( "{id} is present in 'catalyst_id_for_stake_address', but not in 'rbac_registration'" ) ?;
736- if previous_chain. get_latest_signing_pk_for_role ( & RoleId :: Role0 )
737- == new_chain. get_latest_signing_pk_for_role ( & RoleId :: Role0 )
738- {
739- report. functional_validation (
740- & format ! ( "A new registration ({catalyst_id}) uses the same public key as the previous one ({})" ,
741- previous_chain. catalyst_id( ) . as_short_id( )
742- ) ,
743- "It is only allowed to override the existing chain by using different public key" ,
744- ) ;
745- } else {
746- // The new root registration "takes" an address(es) from the existing chain, so
747- // that chain needs to be updated.
748- updated_chains
749- . entry ( id)
750- . and_modify ( |e| {
751- e. insert ( address. clone ( ) ) ;
752- } )
753- . or_insert ( [ address. clone ( ) ] . into_iter ( ) . collect ( ) ) ;
754- }
755- }
756- }
757-
758- // Check that new public keys aren't used by other chains.
759- new_chain. validate_public_keys ( & report, provider) . await ?;
760-
761- if report. is_problematic ( ) {
762- return Err ( RbacValidationError :: InvalidRegistration {
763- catalyst_id,
764- purpose,
765- report,
766- } ) ;
767- }
768-
769- Ok ( ( new_chain, updated_chains. into_iter ( ) . collect ( ) ) )
594+ let inner = RegistrationChainInner :: new_stateless ( reg) ?;
595+ Some ( RegistrationChain {
596+ inner : Arc :: new ( inner) ,
597+ } )
770598 }
771599}
772600
@@ -827,7 +655,7 @@ mod test {
827655 data. assert_valid ( & registration) ;
828656
829657 // Create a chain with the first registration.
830- let chain = RegistrationChain :: new ( registration) . unwrap ( ) ;
658+ let chain = RegistrationChain :: new_stateless ( registration) . unwrap ( ) ;
831659 assert_eq ! ( chain. purpose( ) , & [ data. purpose] ) ;
832660 assert_eq ! ( 1 , chain. x509_certs( ) . len( ) ) ;
833661 let origin = & chain. x509_certs ( ) . get ( & 0 ) . unwrap ( ) . first ( ) . unwrap ( ) ;
@@ -854,7 +682,7 @@ mod test {
854682 assert ! ( registration. report( ) . is_problematic( ) ) ;
855683
856684 let report = registration. report ( ) . to_owned ( ) ;
857- assert ! ( chain. update ( registration) . is_none( ) ) ;
685+ assert ! ( chain. update_stateless ( registration) . is_none( ) ) ;
858686 let report = format ! ( "{report:?}" ) ;
859687 assert ! (
860688 report. contains( "kind: InvalidValue { field: \" previous transaction ID\" " ) ,
@@ -868,7 +696,7 @@ mod test {
868696 . unwrap ( )
869697 . unwrap ( ) ;
870698 data. assert_valid ( & registration) ;
871- let update = chain. update ( registration) . unwrap ( ) ;
699+ let update = chain. update_stateless ( registration) . unwrap ( ) ;
872700 // Current tx hash should be equal to the hash from block 4.
873701 assert_eq ! ( update. current_tx_id_hash( ) , data. txn_hash) ;
874702 assert ! ( update. role_data_record( ) . contains_key( & data. role) ) ;
0 commit comments