Skip to main content

solana_runtime/
validated_block_finalization.rs

1//! Validated block finalization certificates.
2//!
3//! This module provides [`ValidatedBlockFinalizationCert`], a type that represents
4//! a validated proof that a block has been finalized. The type can only be constructed
5//! through validation, ensuring type-safe guarantees about certificate validity.
6
7use {
8    crate::bank::Bank,
9    agave_votor_messages::{
10        certificate::{Certificate, CertificateType},
11        consensus_message::Block,
12        finalized_slot::FinalizedSlot,
13        unverified_vote_message::UnverifiedCertificate,
14    },
15    solana_bls_signatures::BlsError,
16    solana_clock::Slot,
17    solana_entry::block_component::{BlockFinalizationCert, VotesAggregate},
18    solana_pubkey::Pubkey,
19    solana_signer_store::{DecodeError, Decoded, decode},
20    std::collections::HashSet,
21    thiserror::Error,
22};
23
24/// Error type for block finalization certificate validation.
25#[derive(Debug, Error, PartialEq, Eq)]
26pub enum BlockFinalizationCertError {
27    /// BLS signature conversion failed
28    #[error("BLS signature conversion for {0:?} failed: {1}")]
29    BlsError(CertificateType, BlsError),
30
31    /// Certificate signature verification failed
32    #[error("Certificate verification for {0:?} failed")]
33    CertificateVerificationFailed(CertificateType),
34
35    /// Missing epoch stakes for slot
36    #[error("Missing epoch stakes for {0:?}")]
37    MissingEpochStakes(CertificateType),
38
39    /// Base 3 finalization certificate
40    #[error("Finalization certificate was base 3 for {0:?}")]
41    Base3Finalization(CertificateType),
42
43    /// Decoding error
44    #[error("Unable to decode certificate {0:?}: {1:?}")]
45    DecodeError(CertificateType, DecodeError),
46}
47
48/// There are two ways a block can be finalized:
49/// - `Finalize`: Slow finalization with both a Finalize certificate and a Notarize certificate
50/// - `FastFinalize`: Fast finalization with a single FinalizeFast certificate
51#[derive(Clone, Debug)]
52#[allow(clippy::large_enum_variant)]
53enum ValidatedBlockFinalizationCertKind {
54    /// Slow finalization: requires both a Finalize cert and a Notarize cert for the same slot
55    Finalize {
56        /// The finalize certificate
57        finalize_cert: Certificate,
58        /// The notarize certificate
59        notarize_cert: Certificate,
60    },
61    /// Fast finalization: a single FastFinalize certificate
62    FastFinalize(Certificate),
63}
64
65/// A validated proof that a block has been finalized.
66///
67/// This type can only be constructed through validation methods, providing
68/// compile-time guarantees that the contained certificates are valid.
69#[derive(Clone, Debug)]
70pub struct ValidatedBlockFinalizationCert {
71    kind: ValidatedBlockFinalizationCertKind,
72    signers: HashSet<Pubkey>,
73}
74
75impl ValidatedBlockFinalizationCert {
76    /// Creates a validated block finalization certificate from a footer's final certificate.
77    ///
78    /// This method performs both BLS signature conversion and stake threshold verification
79    /// against the provided bank's epoch stakes.
80    ///
81    /// # Errors
82    /// Returns an error if:
83    /// - BLS signature conversion fails
84    /// - Certificate signature verification fails
85    /// - The certificates don't meet the required stake thresholds
86    pub fn try_from_footer(
87        block_final_cert: BlockFinalizationCert,
88        bank: &Bank,
89        shred_version: u16,
90    ) -> Result<Self, BlockFinalizationCertError> {
91        let block = Block {
92            slot: block_final_cert.slot,
93            block_id: block_final_cert.block_id,
94        };
95
96        if let Some(notar_aggregate) = block_final_cert.notar_aggregate {
97            // Slow finalization
98            let notarize_cert_type = CertificateType::Notarize(block);
99            let finalize_cert_type = CertificateType::Finalize(block.slot);
100
101            let notarize_cert = UnverifiedCertificate {
102                cert_type: notarize_cert_type,
103                signature: notar_aggregate
104                    .uncompress_signature()
105                    .map_err(|e| BlockFinalizationCertError::BlsError(notarize_cert_type, e))?,
106                bitmap: notar_aggregate.into_bitmap(),
107                shred_version,
108            };
109            let finalize_cert = UnverifiedCertificate {
110                cert_type: finalize_cert_type,
111                signature: block_final_cert
112                    .final_aggregate
113                    .uncompress_signature()
114                    .map_err(|e| BlockFinalizationCertError::BlsError(finalize_cert_type, e))?,
115                bitmap: block_final_cert.final_aggregate.into_bitmap(),
116                shred_version,
117            };
118
119            // Verify both certificates
120            let notarize_cert = Self::verify_certificate(bank, notarize_cert)?;
121            let finalize_cert = Self::verify_certificate(bank, finalize_cert)?;
122
123            let signers = Self::extract_signers(bank, &finalize_cert)?;
124            Ok(Self {
125                kind: ValidatedBlockFinalizationCertKind::Finalize {
126                    finalize_cert,
127                    notarize_cert,
128                },
129                signers,
130            })
131        } else {
132            // Fast finalization
133            let fast_finalize_cert_type = CertificateType::FinalizeFast(block);
134
135            let fast_finalize_cert = UnverifiedCertificate {
136                cert_type: fast_finalize_cert_type,
137                signature: block_final_cert
138                    .final_aggregate
139                    .uncompress_signature()
140                    .map_err(|e| {
141                        BlockFinalizationCertError::BlsError(fast_finalize_cert_type, e)
142                    })?,
143                bitmap: block_final_cert.final_aggregate.into_bitmap(),
144                shred_version,
145            };
146
147            let fast_finalize_cert = Self::verify_certificate(bank, fast_finalize_cert)?;
148
149            let signers = Self::extract_signers(bank, &fast_finalize_cert)?;
150            Ok(Self {
151                kind: ValidatedBlockFinalizationCertKind::FastFinalize(fast_finalize_cert),
152                signers,
153            })
154        }
155    }
156
157    /// Creates a validated block finalization certificate from already-validated certificates.
158    ///
159    /// This is intended for use by the consensus pool, where certificates have already been
160    /// validated by the bls sigverifier
161    ///
162    /// # Safety
163    /// The caller must ensure that the provided certificates have been properly validated.
164    /// Using unvalidated certificates will compromise the type's safety guarantees.
165    pub fn from_validated_slow(
166        finalize_cert: Certificate,
167        notarize_cert: Certificate,
168        bank: &Bank,
169    ) -> Self {
170        debug_assert!(finalize_cert.cert_type.is_slow_finalization());
171        debug_assert!(notarize_cert.cert_type.is_notarize());
172        debug_assert_eq!(
173            notarize_cert.cert_type.slot(),
174            finalize_cert.cert_type.slot()
175        );
176
177        let signers = Self::extract_signers(bank, &finalize_cert)
178            .expect("Certificate should have been validated");
179        Self {
180            kind: ValidatedBlockFinalizationCertKind::Finalize {
181                finalize_cert,
182                notarize_cert,
183            },
184            signers,
185        }
186    }
187
188    /// Creates a validated fast finalization certificate from an already-validated certificate.
189    ///
190    /// This is intended for use by the consensus pool, where certificates have already been
191    /// validated by the bls sigverifier
192    ///
193    /// # Safety
194    /// The caller must ensure that the provided certificate has been properly validated.
195    /// Using an unvalidated certificate will compromise the type's safety guarantees.
196    pub fn from_validated_fast(cert: Certificate, bank: &Bank) -> Self {
197        debug_assert!(cert.cert_type.is_fast_finalization());
198
199        let signers =
200            Self::extract_signers(bank, &cert).expect("Certificate should have been validated");
201        Self {
202            kind: ValidatedBlockFinalizationCertKind::FastFinalize(cert),
203            signers,
204        }
205    }
206
207    /// Returns the slot that is finalized.
208    pub fn slot(&self) -> FinalizedSlot {
209        match &self.kind {
210            ValidatedBlockFinalizationCertKind::Finalize {
211                finalize_cert,
212                notarize_cert,
213            } => {
214                debug_assert_eq!(
215                    finalize_cert.cert_type.slot(),
216                    notarize_cert.cert_type.slot()
217                );
218                FinalizedSlot::Slow(finalize_cert.cert_type.slot())
219            }
220            ValidatedBlockFinalizationCertKind::FastFinalize(certificate) => {
221                FinalizedSlot::Fast(certificate.cert_type.slot())
222            }
223        }
224    }
225
226    /// Returns the block that is finalized (slot, block_id).
227    pub fn block(&self) -> Block {
228        match &self.kind {
229            ValidatedBlockFinalizationCertKind::Finalize { notarize_cert, .. } => notarize_cert
230                .cert_type
231                .to_block()
232                .expect("notarize certificate has block"),
233            ValidatedBlockFinalizationCertKind::FastFinalize(cert) => cert
234                .cert_type
235                .to_block()
236                .expect("fast finalize certificate has block"),
237        }
238    }
239
240    /// Returns true if this is a fast finalization.
241    pub fn is_fast(&self) -> bool {
242        matches!(
243            self.kind,
244            ValidatedBlockFinalizationCertKind::FastFinalize(_)
245        )
246    }
247
248    /// Returns cloned copies of the certificates that prove this finalization.
249    ///
250    /// For slow finalization this returns the finalize certificate followed by the notarize
251    /// certificate. For fast finalization this returns the single fast-finalize certificate.
252    pub fn clone_certificates(&self) -> Vec<Certificate> {
253        match &self.kind {
254            ValidatedBlockFinalizationCertKind::Finalize {
255                finalize_cert,
256                notarize_cert,
257            } => vec![finalize_cert.clone(), notarize_cert.clone()],
258            ValidatedBlockFinalizationCertKind::FastFinalize(cert) => vec![cert.clone()],
259        }
260    }
261
262    /// Returns the data needed to calculating and paying vote rewards.
263    pub fn vote_rewards_input(&self) -> (&HashSet<Pubkey>, Slot) {
264        (&self.signers, self.slot().slot())
265    }
266
267    /// Consumes self and returns the contained certificates and the signers.
268    ///
269    /// For slow finalization, returns (signers, finalize_cert, Some(notarize_cert)).
270    /// For fast finalization, returns (signers, fast_finalize_cert, None).
271    pub(crate) fn into_parts(self) -> (HashSet<Pubkey>, Certificate, Option<Certificate>) {
272        let signers = self.signers;
273        let (final_cert, notar_cert) = match self.kind {
274            ValidatedBlockFinalizationCertKind::Finalize {
275                finalize_cert,
276                notarize_cert,
277            } => (finalize_cert, Some(notarize_cert)),
278            ValidatedBlockFinalizationCertKind::FastFinalize(cert) => (cert, None),
279        };
280        (signers, final_cert, notar_cert)
281    }
282
283    /// Converts this validated certificate into a [`BlockFinalizationCert`] for inclusion in a block footer.
284    pub fn to_block_final_cert(&self) -> BlockFinalizationCert {
285        match &self.kind {
286            ValidatedBlockFinalizationCertKind::Finalize {
287                finalize_cert,
288                notarize_cert,
289            } => {
290                let slot = finalize_cert.cert_type.slot();
291                let block_id = notarize_cert
292                    .cert_type
293                    .to_block()
294                    .expect("notarize certificates correspond to blocks")
295                    .block_id;
296                BlockFinalizationCert {
297                    slot,
298                    block_id,
299                    final_aggregate: VotesAggregate::from_certificate(finalize_cert),
300                    notar_aggregate: Some(VotesAggregate::from_certificate(notarize_cert)),
301                }
302            }
303            ValidatedBlockFinalizationCertKind::FastFinalize(cert) => {
304                let block = cert
305                    .cert_type
306                    .to_block()
307                    .expect("fast finalizations correspond to blocks");
308                BlockFinalizationCert {
309                    slot: block.slot,
310                    block_id: block.block_id,
311                    final_aggregate: VotesAggregate::from_certificate(cert),
312                    notar_aggregate: None,
313                }
314            }
315        }
316    }
317
318    /// Verifies the certificate, returning (stake present in certificate, total stake in validator set) on success.
319    fn verify_certificate(
320        bank: &Bank,
321        cert: UnverifiedCertificate,
322    ) -> Result<Certificate, BlockFinalizationCertError> {
323        let cert_type = cert.cert_type;
324        let cert = bank
325            .verify_certificate(cert)
326            .map_err(|_| BlockFinalizationCertError::CertificateVerificationFailed(cert_type))?;
327        Ok(cert)
328    }
329
330    fn extract_signers(
331        bank: &Bank,
332        cert: &Certificate,
333    ) -> Result<HashSet<Pubkey>, BlockFinalizationCertError> {
334        let Some(epoch_stakes) = bank.epoch_stakes_from_slot(cert.cert_type.slot()) else {
335            return Err(BlockFinalizationCertError::MissingEpochStakes(
336                cert.cert_type,
337            ));
338        };
339        let rank_map = epoch_stakes.bls_pubkey_to_rank_map();
340        let decoded = decode(&cert.bitmap, rank_map.len());
341        let ranks = match decoded {
342            Ok(Decoded::Base2(ranks)) => ranks,
343            Ok(Decoded::Base3(_, _)) => {
344                return Err(BlockFinalizationCertError::Base3Finalization(
345                    cert.cert_type,
346                ));
347            }
348            Err(err) => return Err(BlockFinalizationCertError::DecodeError(cert.cert_type, err)),
349        };
350
351        Ok(ranks
352            .iter_ones()
353            .filter_map(|rank| {
354                rank_map
355                    .get_pubkey_stake_entry(rank)
356                    .map(|entry| entry.vote_account_pubkey)
357            })
358            .collect())
359    }
360}
361
362#[cfg(test)]
363mod tests {
364    use {
365        super::*,
366        crate::genesis_utils::{
367            ValidatorVoteKeypairs, create_genesis_config_with_alpenglow_vote_accounts,
368        },
369        agave_votor_messages::{vote::Vote, wire::get_vote_payload_to_sign},
370        bitvec::prelude::*,
371        rand::Rng,
372        solana_bls_signatures::SignatureProjective,
373        solana_entry::block_component::VotesAggregate,
374        solana_hash::Hash,
375        solana_signer_store::encode_base2,
376        std::sync::Arc,
377    };
378
379    /// Creates a bank with BLS-enabled validators for testing certificate verification.
380    /// Returns (bank, validator_keypairs) where bank has validators with BLS keys.
381    fn create_bank_with_bls_validators(
382        num_validators: usize,
383        stakes: Vec<u64>,
384    ) -> (Arc<Bank>, Vec<ValidatorVoteKeypairs>) {
385        assert_eq!(num_validators, stakes.len());
386        let validator_keypairs: Vec<ValidatorVoteKeypairs> = (0..num_validators)
387            .map(|_| ValidatorVoteKeypairs::new_rand())
388            .collect();
389
390        let genesis_config_info = create_genesis_config_with_alpenglow_vote_accounts(
391            10_000_000,
392            &validator_keypairs,
393            stakes,
394        );
395
396        let bank = Arc::new(Bank::new_for_tests(&genesis_config_info.genesis_config));
397        (bank, validator_keypairs)
398    }
399
400    /// Build a certificate by manually aggregating BLS signatures and encoding bitmap.
401    /// `signing_ranks` specifies which validator ranks are signing.
402    fn build_certificate_manual(
403        cert_type: CertificateType,
404        vote: Vote,
405        shred_version: u16,
406        signing_ranks: &[usize],
407        validator_keypairs: &[ValidatorVoteKeypairs],
408    ) -> Certificate {
409        let serialized_vote = get_vote_payload_to_sign(vote, shred_version);
410
411        // Aggregate signatures
412        let mut signature = SignatureProjective::identity();
413        for &rank in signing_ranks {
414            let sig = validator_keypairs[rank].bls_keypair.sign(&serialized_vote);
415            signature.aggregate_with(std::iter::once(&sig)).unwrap();
416        }
417
418        // Build bitmap
419        let max_rank = signing_ranks.iter().copied().max().unwrap_or(0);
420        let mut bitvec = BitVec::<u8, Lsb0>::repeat(false, max_rank.saturating_add(1));
421        for &rank in signing_ranks {
422            bitvec.set(rank, true);
423        }
424        let bitmap = encode_base2(&bitvec).expect("Failed to encode bitmap");
425
426        Certificate {
427            cert_type,
428            signature: signature.into(),
429            bitmap,
430        }
431    }
432
433    #[test]
434    fn test_verify_final_cert_valid() {
435        // Create 10 validators with descending stakes (1000, 900, 800, ...)
436        // Total stake = 5500
437        let num_validators = 10;
438        let stakes: Vec<u64> = (0..num_validators)
439            .map(|i| (1000u64).saturating_sub((i as u64).saturating_mul(100)))
440            .collect();
441        let (bank, validator_keypairs) = create_bank_with_bls_validators(num_validators, stakes);
442        let shred_version = rand::rng().random();
443
444        let block = Block {
445            slot: bank.slot(),
446            block_id: Hash::new_unique(),
447        };
448
449        // Test 1: Fast finalize (requires 80% stake = 4400)
450        // Top 6 validators = 1000+900+800+700+600+500 = 4500 (>= 80%)
451        {
452            let cert_type = CertificateType::FinalizeFast(block);
453            let vote = Vote::new_notarization_vote(block);
454            let signing_ranks: Vec<usize> = (0..6).collect();
455            let fast_finalize_cert = build_certificate_manual(
456                cert_type,
457                vote,
458                shred_version,
459                &signing_ranks,
460                &validator_keypairs,
461            );
462
463            let block_final_cert = BlockFinalizationCert {
464                slot: block.slot,
465                block_id: block.block_id,
466                final_aggregate: VotesAggregate::from_certificate(&fast_finalize_cert),
467                notar_aggregate: None,
468            };
469
470            let validated = ValidatedBlockFinalizationCert::try_from_footer(
471                block_final_cert,
472                &bank,
473                shred_version,
474            )
475            .expect("Valid fast finalize certificate should pass verification");
476            assert_eq!(validated.clone_certificates(), vec![fast_finalize_cert]);
477        }
478
479        // Test 2: Slow finalize (requires 60% stake = 3300 for both certs)
480        // Top 4 validators = 1000+900+800+700 = 3400 (>= 60%)
481        {
482            let notarize_cert_type = CertificateType::Notarize(block);
483            let notarize_vote = Vote::new_notarization_vote(block);
484            let notarize_signing_ranks: Vec<usize> = (0..4).collect();
485            let notarize_cert = build_certificate_manual(
486                notarize_cert_type,
487                notarize_vote,
488                shred_version,
489                &notarize_signing_ranks,
490                &validator_keypairs,
491            );
492
493            let finalize_cert_type = CertificateType::Finalize(block.slot);
494            let finalize_vote = Vote::new_finalization_vote(block.slot);
495            let finalize_signing_ranks: Vec<usize> = (0..4).collect();
496            let finalize_cert = build_certificate_manual(
497                finalize_cert_type,
498                finalize_vote,
499                shred_version,
500                &finalize_signing_ranks,
501                &validator_keypairs,
502            );
503
504            let block_final_cert = BlockFinalizationCert {
505                slot: block.slot,
506                block_id: block.block_id,
507                final_aggregate: VotesAggregate::from_certificate(&finalize_cert),
508                notar_aggregate: Some(VotesAggregate::from_certificate(&notarize_cert)),
509            };
510
511            let validated = ValidatedBlockFinalizationCert::try_from_footer(
512                block_final_cert,
513                &bank,
514                shred_version,
515            )
516            .expect("Valid slow finalize certificate should pass verification");
517            assert_eq!(
518                validated.clone_certificates(),
519                vec![finalize_cert, notarize_cert]
520            );
521        }
522    }
523
524    #[test]
525    fn test_verify_final_cert_insufficient_stake() {
526        // Create 10 validators with descending stakes (1000, 900, 800, ...)
527        // Total stake = 5500
528        let num_validators = 10;
529        let stakes: Vec<u64> = (0..num_validators)
530            .map(|i| (1000u64).saturating_sub((i as u64).saturating_mul(100)))
531            .collect();
532        let (bank, validator_keypairs) = create_bank_with_bls_validators(num_validators, stakes);
533        let shred_version = rand::rng().random();
534
535        let block = Block {
536            slot: bank.slot(),
537            block_id: Hash::new_unique(),
538        };
539
540        // Fast finalize with insufficient stake (requires 80% = 4400)
541        // Top 5 validators = 1000+900+800+700+600 = 4000 (< 80%)
542        {
543            let cert_type = CertificateType::FinalizeFast(block);
544            let vote = Vote::new_notarization_vote(block);
545            let signing_ranks: Vec<usize> = (0..5).collect();
546            let fast_finalize_cert = build_certificate_manual(
547                cert_type,
548                vote,
549                shred_version,
550                &signing_ranks,
551                &validator_keypairs,
552            );
553
554            let block_final_cert = BlockFinalizationCert {
555                slot: block.slot,
556                block_id: block.block_id,
557                final_aggregate: VotesAggregate::from_certificate(&fast_finalize_cert),
558                notar_aggregate: None,
559            };
560
561            let err = ValidatedBlockFinalizationCert::try_from_footer(
562                block_final_cert,
563                &bank,
564                shred_version,
565            )
566            .unwrap_err();
567            assert!(
568                matches!(
569                    err,
570                    BlockFinalizationCertError::CertificateVerificationFailed { .. }
571                ),
572                "Fast finalize with insufficient stake should fail verification.  {err:?}"
573            );
574        }
575
576        // Slow finalize with insufficient notarize stake (requires 60% = 3300)
577        // Top 3 validators = 1000+900+800 = 2700 (< 60%)
578        {
579            let notarize_cert_type = CertificateType::Notarize(block);
580            let notarize_vote = Vote::new_notarization_vote(block);
581            let notarize_signing_ranks: Vec<usize> = (0..3).collect();
582            let notarize_cert = build_certificate_manual(
583                notarize_cert_type,
584                notarize_vote,
585                shred_version,
586                &notarize_signing_ranks,
587                &validator_keypairs,
588            );
589
590            // Finalize cert has enough stake
591            let finalize_cert_type = CertificateType::Finalize(block.slot);
592            let finalize_vote = Vote::new_finalization_vote(block.slot);
593            let finalize_signing_ranks: Vec<usize> = (0..4).collect();
594            let finalize_cert = build_certificate_manual(
595                finalize_cert_type,
596                finalize_vote,
597                shred_version,
598                &finalize_signing_ranks,
599                &validator_keypairs,
600            );
601
602            let block_final_cert = BlockFinalizationCert {
603                slot: block.slot,
604                block_id: block.block_id,
605                final_aggregate: VotesAggregate::from_certificate(&finalize_cert),
606                notar_aggregate: Some(VotesAggregate::from_certificate(&notarize_cert)),
607            };
608
609            let err = ValidatedBlockFinalizationCert::try_from_footer(
610                block_final_cert,
611                &bank,
612                shred_version,
613            )
614            .unwrap_err();
615            assert!(
616                matches!(
617                    err,
618                    BlockFinalizationCertError::CertificateVerificationFailed { .. }
619                ),
620                "Slow finalize with insufficient notarize stake should fail verification"
621            );
622        }
623
624        // Slow finalize with insufficient finalize stake (requires 60% = 3300)
625        // Notarize has enough stake, but finalize doesn't
626        {
627            // Notarize cert has enough stake
628            let notarize_cert_type = CertificateType::Notarize(block);
629            let notarize_vote = Vote::new_notarization_vote(block);
630            let notarize_signing_ranks: Vec<usize> = (0..4).collect();
631            let notarize_cert = build_certificate_manual(
632                notarize_cert_type,
633                notarize_vote,
634                shred_version,
635                &notarize_signing_ranks,
636                &validator_keypairs,
637            );
638
639            // Finalize cert has insufficient stake
640            // Top 3 validators = 1000+900+800 = 2700 (< 60%)
641            let finalize_cert_type = CertificateType::Finalize(block.slot);
642            let finalize_vote = Vote::new_finalization_vote(block.slot);
643            let finalize_signing_ranks: Vec<usize> = (0..3).collect();
644            let finalize_cert = build_certificate_manual(
645                finalize_cert_type,
646                finalize_vote,
647                shred_version,
648                &finalize_signing_ranks,
649                &validator_keypairs,
650            );
651
652            let block_final_cert = BlockFinalizationCert {
653                slot: block.slot,
654                block_id: block.block_id,
655                final_aggregate: VotesAggregate::from_certificate(&finalize_cert),
656                notar_aggregate: Some(VotesAggregate::from_certificate(&notarize_cert)),
657            };
658
659            let err = ValidatedBlockFinalizationCert::try_from_footer(
660                block_final_cert,
661                &bank,
662                shred_version,
663            )
664            .unwrap_err();
665            assert!(
666                matches!(
667                    err,
668                    BlockFinalizationCertError::CertificateVerificationFailed { .. }
669                ),
670                "Slow finalize with insufficient finalize stake should fail verification"
671            );
672        }
673    }
674}