consensus_core/
block_verifier.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{collections::BTreeSet, sync::Arc};
6
7use crate::{
8    Round,
9    block::{
10        BlockAPI, BlockRef, BlockTimestampMs, GENESIS_ROUND, SignedBlock, VerifiedBlock,
11        genesis_blocks,
12    },
13    context::Context,
14    error::{ConsensusError, ConsensusResult},
15    transaction::TransactionVerifier,
16};
17
18pub(crate) trait BlockVerifier: Send + Sync + 'static {
19    /// Verifies a block's metadata and transactions.
20    /// This is called before examining a block's causal history.
21    fn verify(&self, block: &SignedBlock) -> ConsensusResult<()>;
22
23    /// Verifies a block w.r.t. ancestor blocks.
24    /// This is called after a block has complete causal history locally,
25    /// and is ready to be accepted into the DAG.
26    ///
27    /// Caller must make sure ancestors corresponse to block.ancestors() 1-to-1,
28    /// in the same order.
29    fn check_ancestors(
30        &self,
31        block: &VerifiedBlock,
32        ancestors: &[Option<VerifiedBlock>],
33        gc_enabled: bool,
34        gc_round: Round,
35    ) -> ConsensusResult<()>;
36}
37
38/// `SignedBlockVerifier` checks the validity of a block.
39///
40/// Blocks that fail verification at one honest authority will be rejected by
41/// all other honest authorities as well. The means invalid blocks, and blocks
42/// with an invalid ancestor, will never be accepted into the DAG.
43pub(crate) struct SignedBlockVerifier {
44    context: Arc<Context>,
45    genesis: BTreeSet<BlockRef>,
46    transaction_verifier: Arc<dyn TransactionVerifier>,
47}
48
49impl SignedBlockVerifier {
50    pub(crate) fn new(
51        context: Arc<Context>,
52        transaction_verifier: Arc<dyn TransactionVerifier>,
53    ) -> Self {
54        let genesis = genesis_blocks(context.clone())
55            .into_iter()
56            .map(|b| b.reference())
57            .collect();
58        Self {
59            context,
60            genesis,
61            transaction_verifier,
62        }
63    }
64
65    pub(crate) fn check_transactions(&self, batch: &[&[u8]]) -> ConsensusResult<()> {
66        let max_transaction_size_limit =
67            self.context.protocol_config.max_transaction_size_bytes() as usize;
68        for t in batch {
69            if t.len() > max_transaction_size_limit && max_transaction_size_limit > 0 {
70                return Err(ConsensusError::TransactionTooLarge {
71                    size: t.len(),
72                    limit: max_transaction_size_limit,
73                });
74            }
75        }
76
77        let max_num_transactions_limit =
78            self.context.protocol_config.max_num_transactions_in_block() as usize;
79        if batch.len() > max_num_transactions_limit && max_num_transactions_limit > 0 {
80            return Err(ConsensusError::TooManyTransactions {
81                count: batch.len(),
82                limit: max_num_transactions_limit,
83            });
84        }
85
86        let total_transactions_size_limit = self
87            .context
88            .protocol_config
89            .max_transactions_in_block_bytes() as usize;
90        if batch.iter().map(|t| t.len()).sum::<usize>() > total_transactions_size_limit
91            && total_transactions_size_limit > 0
92        {
93            return Err(ConsensusError::TooManyTransactionBytes {
94                size: batch.len(),
95                limit: total_transactions_size_limit,
96            });
97        }
98        Ok(())
99    }
100}
101
102// All block verification logic are implemented below.
103impl BlockVerifier for SignedBlockVerifier {
104    fn verify(&self, block: &SignedBlock) -> ConsensusResult<()> {
105        let committee = &self.context.committee;
106        // The block must belong to the current epoch and have valid authority index,
107        // before having its signature verified.
108        if block.epoch() != committee.epoch() {
109            return Err(ConsensusError::WrongEpoch {
110                expected: committee.epoch(),
111                actual: block.epoch(),
112            });
113        }
114        if block.round() == 0 {
115            return Err(ConsensusError::UnexpectedGenesisBlock);
116        }
117        if !committee.is_valid_index(block.author()) {
118            return Err(ConsensusError::InvalidAuthorityIndex {
119                index: block.author(),
120                max: committee.size() - 1,
121            });
122        }
123
124        // Verify the block's signature.
125        block.verify_signature(&self.context)?;
126
127        // Verify the block's ancestor refs are consistent with the block's round,
128        // and total parent stakes reach quorum.
129        if block.ancestors().len() > committee.size() {
130            return Err(ConsensusError::TooManyAncestors(
131                block.ancestors().len(),
132                committee.size(),
133            ));
134        }
135        if block.ancestors().is_empty() {
136            return Err(ConsensusError::InsufficientParentStakes {
137                parent_stakes: 0,
138                quorum: committee.quorum_threshold(),
139            });
140        }
141        let mut seen_ancestors = vec![false; committee.size()];
142        let mut parent_stakes = 0;
143        for (i, ancestor) in block.ancestors().iter().enumerate() {
144            if !committee.is_valid_index(ancestor.author) {
145                return Err(ConsensusError::InvalidAuthorityIndex {
146                    index: ancestor.author,
147                    max: committee.size() - 1,
148                });
149            }
150            if (i == 0 && ancestor.author != block.author())
151                || (i > 0 && ancestor.author == block.author())
152            {
153                return Err(ConsensusError::InvalidAncestorPosition {
154                    block_authority: block.author(),
155                    ancestor_authority: ancestor.author,
156                    position: i,
157                });
158            }
159            if ancestor.round >= block.round() {
160                return Err(ConsensusError::InvalidAncestorRound {
161                    ancestor: ancestor.round,
162                    block: block.round(),
163                });
164            }
165            if ancestor.round == GENESIS_ROUND && !self.genesis.contains(ancestor) {
166                return Err(ConsensusError::InvalidGenesisAncestor(*ancestor));
167            }
168            if seen_ancestors[ancestor.author] {
169                return Err(ConsensusError::DuplicatedAncestorsAuthority(
170                    ancestor.author,
171                ));
172            }
173            seen_ancestors[ancestor.author] = true;
174            // Block must have round >= 1 so checked_sub(1) should be safe.
175            if ancestor.round == block.round().checked_sub(1).unwrap() {
176                parent_stakes += committee.stake(ancestor.author);
177            }
178        }
179        if !committee.reached_quorum(parent_stakes) {
180            return Err(ConsensusError::InsufficientParentStakes {
181                parent_stakes,
182                quorum: committee.quorum_threshold(),
183            });
184        }
185
186        let batch: Vec<_> = block.transactions().iter().map(|t| t.data()).collect();
187
188        self.check_transactions(&batch)?;
189
190        self.transaction_verifier
191            .verify_batch(&batch)
192            .map_err(|e| ConsensusError::InvalidTransaction(format!("{e:?}")))
193    }
194
195    fn check_ancestors(
196        &self,
197        block: &VerifiedBlock,
198        ancestors: &[Option<VerifiedBlock>],
199        gc_enabled: bool,
200        gc_round: Round,
201    ) -> ConsensusResult<()> {
202        if gc_enabled {
203            // TODO: will be removed with new timestamp calculation is in place as all these
204            // will be irrelevant. When gc is enabled we don't have guarantees
205            // that all ancestors will be available. We'll take into account only the passed
206            // gc_round ones for the timestamp check.
207            let mut max_timestamp_ms = BlockTimestampMs::MIN;
208            for ancestor in ancestors.iter().flatten() {
209                if ancestor.round() <= gc_round {
210                    continue;
211                }
212                max_timestamp_ms = max_timestamp_ms.max(ancestor.timestamp_ms());
213                if max_timestamp_ms > block.timestamp_ms() {
214                    return Err(ConsensusError::InvalidBlockTimestamp {
215                        max_timestamp_ms,
216                        block_timestamp_ms: block.timestamp_ms(),
217                    });
218                }
219            }
220        } else {
221            assert_eq!(block.ancestors().len(), ancestors.len());
222            // This checks the invariant that block timestamp >= max ancestor timestamp.
223            let mut max_timestamp_ms = BlockTimestampMs::MIN;
224            for (ancestor_ref, ancestor_block) in block.ancestors().iter().zip(ancestors.iter()) {
225                let ancestor_block = ancestor_block
226                    .as_ref()
227                    .expect("There should never be an empty slot");
228                assert_eq!(ancestor_ref, &ancestor_block.reference());
229                max_timestamp_ms = max_timestamp_ms.max(ancestor_block.timestamp_ms());
230            }
231            if max_timestamp_ms > block.timestamp_ms() {
232                return Err(ConsensusError::InvalidBlockTimestamp {
233                    max_timestamp_ms,
234                    block_timestamp_ms: block.timestamp_ms(),
235                });
236            }
237        }
238        Ok(())
239    }
240}
241
242#[cfg(test)]
243pub(crate) struct NoopBlockVerifier;
244
245#[cfg(test)]
246impl BlockVerifier for NoopBlockVerifier {
247    fn verify(&self, _block: &SignedBlock) -> ConsensusResult<()> {
248        Ok(())
249    }
250
251    fn check_ancestors(
252        &self,
253        _block: &VerifiedBlock,
254        _ancestors: &[Option<VerifiedBlock>],
255        _gc_enabled: bool,
256        _gc_round: Round,
257    ) -> ConsensusResult<()> {
258        Ok(())
259    }
260}
261
262#[cfg(test)]
263mod test {
264    use consensus_config::AuthorityIndex;
265    use rstest::rstest;
266
267    use super::*;
268    use crate::{
269        block::{BlockDigest, BlockRef, TestBlock, Transaction},
270        context::Context,
271        transaction::{TransactionVerifier, ValidationError},
272    };
273
274    struct TxnSizeVerifier {}
275
276    impl TransactionVerifier for TxnSizeVerifier {
277        // Fails verification if any transaction is < 4 bytes.
278        fn verify_batch(&self, transactions: &[&[u8]]) -> Result<(), ValidationError> {
279            for txn in transactions {
280                if txn.len() < 4 {
281                    return Err(ValidationError::InvalidTransaction(format!(
282                        "Length {} is too short!",
283                        txn.len()
284                    )));
285                }
286            }
287            Ok(())
288        }
289    }
290
291    #[tokio::test]
292    async fn test_verify_block() {
293        let (context, keypairs) = Context::new_for_test(4);
294        let context = Arc::new(context);
295        let authority_2_protocol_keypair = &keypairs[2].1;
296        let verifier = SignedBlockVerifier::new(context.clone(), Arc::new(TxnSizeVerifier {}));
297
298        let test_block = TestBlock::new(10, 2)
299            .set_ancestors(vec![
300                BlockRef::new(9, AuthorityIndex::new_for_test(2), BlockDigest::MIN),
301                BlockRef::new(9, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
302                BlockRef::new(9, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
303                BlockRef::new(7, AuthorityIndex::new_for_test(3), BlockDigest::MIN),
304            ])
305            .set_transactions(vec![Transaction::new(vec![4; 8])]);
306
307        // Valid SignedBlock.
308        {
309            let block = test_block.clone().build();
310            let signed_block = SignedBlock::new(block, authority_2_protocol_keypair).unwrap();
311            verifier.verify(&signed_block).unwrap();
312        }
313
314        // Block with wrong epoch.
315        {
316            let block = test_block.clone().set_epoch(1).build();
317            let signed_block = SignedBlock::new(block, authority_2_protocol_keypair).unwrap();
318            assert!(matches!(
319                verifier.verify(&signed_block),
320                Err(ConsensusError::WrongEpoch {
321                    expected: _,
322                    actual: _
323                })
324            ));
325        }
326
327        // Block at genesis round.
328        {
329            let block = test_block.clone().set_round(0).build();
330            let signed_block = SignedBlock::new(block, authority_2_protocol_keypair).unwrap();
331            assert!(matches!(
332                verifier.verify(&signed_block),
333                Err(ConsensusError::UnexpectedGenesisBlock)
334            ));
335        }
336
337        // Block with invalid authority index.
338        {
339            let block = test_block
340                .clone()
341                .set_author(AuthorityIndex::new_for_test(4))
342                .build();
343            let signed_block = SignedBlock::new(block, authority_2_protocol_keypair).unwrap();
344            assert!(matches!(
345                verifier.verify(&signed_block),
346                Err(ConsensusError::InvalidAuthorityIndex { index: _, max: _ })
347            ));
348        }
349
350        // Block with mismatched authority index and signature.
351        {
352            let block = test_block
353                .clone()
354                .set_author(AuthorityIndex::new_for_test(1))
355                .build();
356            let signed_block = SignedBlock::new(block, authority_2_protocol_keypair).unwrap();
357            assert!(matches!(
358                verifier.verify(&signed_block),
359                Err(ConsensusError::SignatureVerificationFailure(_))
360            ));
361        }
362
363        // Block with wrong key.
364        {
365            let block = test_block.clone().build();
366            let signed_block = SignedBlock::new(block, &keypairs[3].1).unwrap();
367            assert!(matches!(
368                verifier.verify(&signed_block),
369                Err(ConsensusError::SignatureVerificationFailure(_))
370            ));
371        }
372
373        // Block without signature.
374        {
375            let block = test_block.clone().build();
376            let mut signed_block = SignedBlock::new(block, authority_2_protocol_keypair).unwrap();
377            signed_block.clear_signature();
378            assert!(matches!(
379                verifier.verify(&signed_block),
380                Err(ConsensusError::MalformedSignature(_))
381            ));
382        }
383
384        // Block with invalid ancestor round.
385        {
386            let block = test_block
387                .clone()
388                .set_ancestors(vec![
389                    BlockRef::new(9, AuthorityIndex::new_for_test(2), BlockDigest::MIN),
390                    BlockRef::new(9, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
391                    BlockRef::new(9, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
392                    BlockRef::new(10, AuthorityIndex::new_for_test(3), BlockDigest::MIN),
393                ])
394                .build();
395            let signed_block = SignedBlock::new(block, authority_2_protocol_keypair).unwrap();
396            assert!(matches!(
397                verifier.verify(&signed_block),
398                Err(ConsensusError::InvalidAncestorRound {
399                    ancestor: _,
400                    block: _
401                })
402            ));
403        }
404
405        // Block with parents not reaching quorum.
406        {
407            let block = test_block
408                .clone()
409                .set_ancestors(vec![
410                    BlockRef::new(9, AuthorityIndex::new_for_test(2), BlockDigest::MIN),
411                    BlockRef::new(9, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
412                    BlockRef::new(8, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
413                    BlockRef::new(8, AuthorityIndex::new_for_test(3), BlockDigest::MIN),
414                ])
415                .build();
416            let signed_block = SignedBlock::new(block, authority_2_protocol_keypair).unwrap();
417            assert!(matches!(
418                verifier.verify(&signed_block),
419                Err(ConsensusError::InsufficientParentStakes {
420                    parent_stakes: _,
421                    quorum: _
422                })
423            ));
424        }
425
426        // Block with too many ancestors.
427        {
428            let block = test_block
429                .clone()
430                .set_ancestors(vec![
431                    BlockRef::new(9, AuthorityIndex::new_for_test(2), BlockDigest::MIN),
432                    BlockRef::new(9, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
433                    BlockRef::new(8, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
434                    BlockRef::new(8, AuthorityIndex::new_for_test(3), BlockDigest::MIN),
435                    BlockRef::new(9, AuthorityIndex::new_for_test(3), BlockDigest::MIN),
436                ])
437                .build();
438            let signed_block = SignedBlock::new(block, authority_2_protocol_keypair).unwrap();
439            assert!(matches!(
440                verifier.verify(&signed_block),
441                Err(ConsensusError::TooManyAncestors(_, _))
442            ));
443        }
444
445        // Block without own ancestor.
446        {
447            let block = test_block
448                .clone()
449                .set_ancestors(vec![
450                    BlockRef::new(9, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
451                    BlockRef::new(8, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
452                    BlockRef::new(8, AuthorityIndex::new_for_test(3), BlockDigest::MIN),
453                ])
454                .build();
455            let signed_block = SignedBlock::new(block, authority_2_protocol_keypair).unwrap();
456            assert!(matches!(
457                verifier.verify(&signed_block),
458                Err(ConsensusError::InvalidAncestorPosition {
459                    block_authority: _,
460                    ancestor_authority: _,
461                    position: _
462                })
463            ));
464        }
465
466        // Block with own ancestor at wrong position.
467        {
468            let block = test_block
469                .clone()
470                .set_ancestors(vec![
471                    BlockRef::new(9, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
472                    BlockRef::new(8, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
473                    BlockRef::new(8, AuthorityIndex::new_for_test(2), BlockDigest::MIN),
474                    BlockRef::new(8, AuthorityIndex::new_for_test(3), BlockDigest::MIN),
475                ])
476                .build();
477            let signed_block = SignedBlock::new(block, authority_2_protocol_keypair).unwrap();
478            assert!(matches!(
479                verifier.verify(&signed_block),
480                Err(ConsensusError::InvalidAncestorPosition {
481                    block_authority: _,
482                    ancestor_authority: _,
483                    position: _
484                })
485            ));
486        }
487
488        // Block with ancestors from the same authority.
489        {
490            let block = test_block
491                .clone()
492                .set_ancestors(vec![
493                    BlockRef::new(8, AuthorityIndex::new_for_test(2), BlockDigest::MIN),
494                    BlockRef::new(8, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
495                    BlockRef::new(8, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
496                ])
497                .build();
498            let signed_block = SignedBlock::new(block, authority_2_protocol_keypair).unwrap();
499            assert!(matches!(
500                verifier.verify(&signed_block),
501                Err(ConsensusError::DuplicatedAncestorsAuthority(_))
502            ));
503        }
504
505        // Block with invalid transaction.
506        {
507            let block = test_block
508                .clone()
509                .set_transactions(vec![Transaction::new(vec![1; 2])])
510                .build();
511            let signed_block = SignedBlock::new(block, authority_2_protocol_keypair).unwrap();
512            assert!(matches!(
513                verifier.verify(&signed_block),
514                Err(ConsensusError::InvalidTransaction(_))
515            ));
516        }
517
518        // Block with transaction too large.
519        {
520            let block = test_block
521                .clone()
522                .set_transactions(vec![Transaction::new(vec![4; 257 * 1024])])
523                .build();
524            let signed_block = SignedBlock::new(block, authority_2_protocol_keypair).unwrap();
525            assert!(matches!(
526                verifier.verify(&signed_block),
527                Err(ConsensusError::TransactionTooLarge { size: _, limit: _ })
528            ));
529        }
530
531        // Block with too many transactions.
532        {
533            let block = test_block
534                .clone()
535                .set_transactions((0..1000).map(|_| Transaction::new(vec![4; 8])).collect())
536                .build();
537            let signed_block = SignedBlock::new(block, authority_2_protocol_keypair).unwrap();
538            assert!(matches!(
539                verifier.verify(&signed_block),
540                Err(ConsensusError::TooManyTransactions { count: _, limit: _ })
541            ));
542        }
543
544        // Block with too many transaction bytes.
545        {
546            let block = test_block
547                .clone()
548                .set_transactions(
549                    (0..100)
550                        .map(|_| Transaction::new(vec![4; 8 * 1024]))
551                        .collect(),
552                )
553                .build();
554            let signed_block = SignedBlock::new(block, authority_2_protocol_keypair).unwrap();
555            assert!(matches!(
556                verifier.verify(&signed_block),
557                Err(ConsensusError::TooManyTransactionBytes { size: _, limit: _ })
558            ));
559        }
560    }
561
562    /// Tests the block's ancestors for timestamp monotonicity. Test will run
563    /// for both when gc is enabled and disabled, but with none of the
564    /// ancestors being below the gc_round.
565    #[rstest]
566    #[tokio::test]
567    async fn test_check_ancestors(#[values(false, true)] gc_enabled: bool) {
568        let num_authorities = 4;
569        let (context, _keypairs) = Context::new_for_test(num_authorities);
570        let context = Arc::new(context);
571        let verifier = SignedBlockVerifier::new(context.clone(), Arc::new(TxnSizeVerifier {}));
572        let gc_round = 0;
573
574        let mut ancestor_blocks = vec![];
575        for i in 0..num_authorities {
576            let test_block = TestBlock::new(10, i as u32)
577                .set_timestamp_ms(1000 + 100 * i as BlockTimestampMs)
578                .build();
579            ancestor_blocks.push(Some(VerifiedBlock::new_for_test(test_block)));
580        }
581        let ancestor_refs = ancestor_blocks
582            .iter()
583            .flatten()
584            .map(|block| block.reference())
585            .collect::<Vec<_>>();
586
587        // Block respecting timestamp invariant.
588        {
589            let block = TestBlock::new(11, 0)
590                .set_ancestors(ancestor_refs.clone())
591                .set_timestamp_ms(1500)
592                .build();
593            let verified_block = VerifiedBlock::new_for_test(block);
594            assert!(
595                verifier
596                    .check_ancestors(&verified_block, &ancestor_blocks, gc_enabled, gc_round)
597                    .is_ok()
598            );
599        }
600
601        // Block not respecting timestamp invariant.
602        {
603            let block = TestBlock::new(11, 0)
604                .set_ancestors(ancestor_refs.clone())
605                .set_timestamp_ms(1000)
606                .build();
607            let verified_block = VerifiedBlock::new_for_test(block);
608            assert!(matches!(
609                verifier.check_ancestors(&verified_block, &ancestor_blocks, gc_enabled, gc_round),
610                Err(ConsensusError::InvalidBlockTimestamp {
611                    max_timestamp_ms: _,
612                    block_timestamp_ms: _
613                })
614            ));
615        }
616    }
617
618    #[tokio::test]
619    async fn test_check_ancestors_passed_gc_round() {
620        let num_authorities = 4;
621        let (context, _keypairs) = Context::new_for_test(num_authorities);
622        let context = Arc::new(context);
623        let verifier = SignedBlockVerifier::new(context.clone(), Arc::new(TxnSizeVerifier {}));
624        let gc_enabled = true;
625        let gc_round = 3;
626
627        let mut ancestor_blocks = vec![];
628
629        // Create one block just on the `gc_round` (so it should be considered garbage
630        // collected). This has higher timestamp that the block we are testing.
631        let test_block = TestBlock::new(gc_round, 0_u32)
632            .set_timestamp_ms(1500 as BlockTimestampMs)
633            .build();
634        ancestor_blocks.push(Some(VerifiedBlock::new_for_test(test_block)));
635
636        // Rest of the blocks
637        for i in 1..=3 {
638            let test_block = TestBlock::new(gc_round + 1, i as u32)
639                .set_timestamp_ms(1000 + 100 * i as BlockTimestampMs)
640                .build();
641            ancestor_blocks.push(Some(VerifiedBlock::new_for_test(test_block)));
642        }
643
644        let ancestor_refs = ancestor_blocks
645            .iter()
646            .flatten()
647            .map(|block| block.reference())
648            .collect::<Vec<_>>();
649
650        // Block respecting timestamp invariant.
651        {
652            let block = TestBlock::new(gc_round + 2, 0)
653                .set_ancestors(ancestor_refs.clone())
654                .set_timestamp_ms(1600)
655                .build();
656            let verified_block = VerifiedBlock::new_for_test(block);
657            assert!(
658                verifier
659                    .check_ancestors(&verified_block, &ancestor_blocks, gc_enabled, gc_round)
660                    .is_ok()
661            );
662        }
663
664        // Block not respecting timestamp invariant for the block that is garbage
665        // collected Validation should pass.
666        {
667            let block = TestBlock::new(11, 0)
668                .set_ancestors(ancestor_refs.clone())
669                .set_timestamp_ms(1400)
670                .build();
671            let verified_block = VerifiedBlock::new_for_test(block);
672            assert!(
673                verifier
674                    .check_ancestors(&verified_block, &ancestor_blocks, gc_enabled, gc_round)
675                    .is_ok()
676            );
677        }
678
679        // Block not respecting timestamp invariant for the blocks that are not garbage
680        // collected
681        {
682            let block = TestBlock::new(11, 0)
683                .set_ancestors(ancestor_refs.clone())
684                .set_timestamp_ms(1100)
685                .build();
686            let verified_block = VerifiedBlock::new_for_test(block);
687            assert!(matches!(
688                verifier.check_ancestors(&verified_block, &ancestor_blocks, gc_enabled, gc_round),
689                Err(ConsensusError::InvalidBlockTimestamp {
690                    max_timestamp_ms: _,
691                    block_timestamp_ms: _
692                })
693            ));
694        }
695    }
696}