@@ -82,6 +82,20 @@ impl<'a> CertificateChainBuilderContext<'a> {
8282 }
8383}
8484
85+ /// Chaining method to use when building a certificate chain with the [CertificateChainBuilder]. For tests only.
86+ #[ derive( Debug , Clone , Copy , PartialEq , Default ) ]
87+ pub enum CertificateChainingMethod {
88+ /// `default` Chain certificates to the 'master' certificate of the epoch or if it's the 'master'
89+ /// certificate, chain it to the 'master' certificate of the previous epoch.
90+ ///
91+ /// The 'master' certificate of an epoch is the first certificate of the epoch.
92+ #[ default]
93+ ToMasterCertificate ,
94+
95+ /// Chain certificates to their direct predecessor.
96+ ToDirectPredecessor ,
97+ }
98+
8599/// A builder for creating a certificate chain. For tests only.
86100///
87101/// # Simple example usage for building a fully valid certificate chain
@@ -149,6 +163,7 @@ pub struct CertificateChainBuilder<'a> {
149163 total_signers_per_epoch_processor : & ' a TotalSignersPerEpochProcessorFunc ,
150164 genesis_certificate_processor : & ' a GenesisCertificateProcessorFunc ,
151165 standard_certificate_processor : & ' a StandardCertificateProcessorFunc ,
166+ certificate_chaining_method : CertificateChainingMethod ,
152167}
153168
154169impl < ' a > CertificateChainBuilder < ' a > {
@@ -166,6 +181,7 @@ impl<'a> CertificateChainBuilder<'a> {
166181 total_signers_per_epoch_processor : & |epoch| min ( 2 + * epoch as usize , 5 ) ,
167182 genesis_certificate_processor : & |certificate, _, _| certificate,
168183 standard_certificate_processor : & |certificate, _| certificate,
184+ certificate_chaining_method : Default :: default ( ) ,
169185 }
170186 }
171187
@@ -220,6 +236,16 @@ impl<'a> CertificateChainBuilder<'a> {
220236 self
221237 }
222238
239+ /// Set the chaining method to use when building the certificate chain.
240+ pub fn with_certificate_chaining_method (
241+ mut self ,
242+ certificate_chaining_method : CertificateChainingMethod ,
243+ ) -> Self {
244+ self . certificate_chaining_method = certificate_chaining_method;
245+
246+ self
247+ }
248+
223249 /// Build the certificate chain.
224250 pub fn build ( self ) -> ( Vec < Certificate > , ProtocolGenesisVerifier ) {
225251 let ( genesis_signer, genesis_verifier) = CertificateChainBuilder :: setup_genesis ( ) ;
@@ -438,26 +464,31 @@ impl<'a> CertificateChainBuilder<'a> {
438464 certificate : & Certificate ,
439465 certificates_chained : & ' b [ Certificate ] ,
440466 ) -> Option < & ' b Certificate > {
441- let is_certificate_first_of_epoch = certificates_chained
442- . last ( )
443- . map ( |c| c. epoch != certificate. epoch )
444- . unwrap_or ( true ) ;
445-
446- certificates_chained
447- . iter ( )
448- . rev ( )
449- . filter ( |c| {
450- if is_certificate_first_of_epoch {
451- // The previous certificate of the first certificate of an epoch
452- // is the first certificate of the previous epoch
453- c. epoch == certificate. epoch . previous ( ) . unwrap ( )
454- } else {
455- // The previous certificate of not the first certificate of an epoch
456- // is the first certificate of the epoch
457- c. epoch == certificate. epoch
458- }
459- } )
460- . last ( )
467+ match self . certificate_chaining_method {
468+ CertificateChainingMethod :: ToMasterCertificate => {
469+ let is_certificate_first_of_epoch = certificates_chained
470+ . last ( )
471+ . map ( |c| c. epoch != certificate. epoch )
472+ . unwrap_or ( true ) ;
473+
474+ certificates_chained
475+ . iter ( )
476+ . rev ( )
477+ . filter ( |c| {
478+ if is_certificate_first_of_epoch {
479+ // The previous certificate of the first certificate of an epoch
480+ // is the first certificate of the previous epoch
481+ c. epoch == certificate. epoch . previous ( ) . unwrap ( )
482+ } else {
483+ // The previous certificate of not the first certificate of an epoch
484+ // is the first certificate of the epoch
485+ c. epoch == certificate. epoch
486+ }
487+ } )
488+ . last ( )
489+ }
490+ CertificateChainingMethod :: ToDirectPredecessor => certificates_chained. last ( ) ,
491+ }
461492 }
462493
463494 // Returns the chained certificates in reverse order
@@ -788,7 +819,7 @@ mod test {
788819 }
789820
790821 #[ test]
791- fn builds_certificate_chain_correctly_chained ( ) {
822+ fn builds_certificate_chain_chained_by_default_to_master_certificates ( ) {
792823 fn create_fake_certificate ( epoch : Epoch , index_in_epoch : u64 ) -> Certificate {
793824 Certificate {
794825 epoch,
@@ -845,6 +876,65 @@ mod test {
845876 ) ;
846877 }
847878
879+ #[ test]
880+ fn builds_certificate_chain_chained_to_direct_previous_if_option_set ( ) {
881+ fn create_fake_certificate ( epoch : Epoch , index_in_epoch : u64 ) -> Certificate {
882+ Certificate {
883+ epoch,
884+ signed_message : format ! ( "certificate-{}-{index_in_epoch}" , * epoch) ,
885+ ..fake_data:: certificate ( "cert-fake" . to_string ( ) )
886+ }
887+ }
888+
889+ let certificates = vec ! [
890+ create_fake_certificate( Epoch ( 1 ) , 1 ) ,
891+ create_fake_certificate( Epoch ( 2 ) , 1 ) ,
892+ create_fake_certificate( Epoch ( 2 ) , 2 ) ,
893+ create_fake_certificate( Epoch ( 3 ) , 1 ) ,
894+ create_fake_certificate( Epoch ( 4 ) , 1 ) ,
895+ create_fake_certificate( Epoch ( 4 ) , 2 ) ,
896+ create_fake_certificate( Epoch ( 4 ) , 3 ) ,
897+ ] ;
898+
899+ let mut certificates_chained = CertificateChainBuilder :: default ( )
900+ . with_certificate_chaining_method ( CertificateChainingMethod :: ToDirectPredecessor )
901+ . compute_chained_certificates ( certificates) ;
902+ certificates_chained. reverse ( ) ;
903+
904+ let certificate_chained_1_1 = & certificates_chained[ 0 ] ;
905+ let certificate_chained_2_1 = & certificates_chained[ 1 ] ;
906+ let certificate_chained_2_2 = & certificates_chained[ 2 ] ;
907+ let certificate_chained_3_1 = & certificates_chained[ 3 ] ;
908+ let certificate_chained_4_1 = & certificates_chained[ 4 ] ;
909+ let certificate_chained_4_2 = & certificates_chained[ 5 ] ;
910+ let certificate_chained_4_3 = & certificates_chained[ 6 ] ;
911+ assert_eq ! ( "" , certificate_chained_1_1. previous_hash) ;
912+ assert_eq ! (
913+ certificate_chained_2_1. previous_hash,
914+ certificate_chained_1_1. hash
915+ ) ;
916+ assert_eq ! (
917+ certificate_chained_2_2. previous_hash,
918+ certificate_chained_2_1. hash
919+ ) ;
920+ assert_eq ! (
921+ certificate_chained_3_1. previous_hash,
922+ certificate_chained_2_2. hash
923+ ) ;
924+ assert_eq ! (
925+ certificate_chained_4_1. previous_hash,
926+ certificate_chained_3_1. hash
927+ ) ;
928+ assert_eq ! (
929+ certificate_chained_4_2. previous_hash,
930+ certificate_chained_4_1. hash
931+ ) ;
932+ assert_eq ! (
933+ certificate_chained_4_3. previous_hash,
934+ certificate_chained_4_2. hash
935+ ) ;
936+ }
937+
848938 #[ test]
849939 fn builds_certificate_chain_with_alteration_on_genesis_certificate ( ) {
850940 let ( certificates, _) = CertificateChainBuilder :: new ( )
0 commit comments