Skip to main content

consensus_core/
block_verifier.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use bytes::Bytes;
5use consensus_types::block::{BlockRef, NUM_RESERVED_TRANSACTION_INDICES, TransactionIndex};
6use std::{collections::BTreeSet, sync::Arc};
7
8use crate::{
9    VerifiedBlock,
10    block::{
11        Block, BlockAPI, GENESIS_ROUND, SignedBlock, genesis_blocks, max_transaction_vote_targets,
12    },
13    context::Context,
14    error::{ConsensusError, ConsensusResult},
15    transaction::TransactionVerifier,
16};
17
18pub trait BlockVerifier: Send + Sync + 'static {
19    /// Verifies a block and its transactions, checking signatures, size limits,
20    /// and transaction validity. All honest validators should produce the same verification
21    /// outcome for the same block, so any verification error should be due to equivocation.
22    /// Returns the verified block.
23    ///
24    /// When Mysticeti fastpath is enabled, it also votes on the transactions in verified blocks,
25    /// and can return a non-empty list of rejected transaction indices. Different honest
26    /// validators may vote differently on transactions.
27    ///
28    /// The method takes both the SignedBlock and its serialized bytes, to avoid re-serializing the block.
29    #[allow(private_interfaces)]
30    fn verify_and_vote(
31        &self,
32        block: SignedBlock,
33        serialized_block: Bytes,
34    ) -> ConsensusResult<(VerifiedBlock, Vec<TransactionIndex>)>;
35
36    /// Votes on the transactions in a verified block.
37    /// This is used to vote on transactions in a verified block, without having to verify the block again. The method
38    /// will verify the transactions and vote on them.
39    fn vote(&self, block: &VerifiedBlock) -> ConsensusResult<Vec<TransactionIndex>>;
40}
41
42/// `SignedBlockVerifier` checks the validity of a block.
43///
44/// Blocks that fail verification at one honest authority will be rejected by all other honest
45/// authorities as well. The means invalid blocks, and blocks with an invalid ancestor, will never
46/// be accepted into the DAG.
47pub(crate) struct SignedBlockVerifier {
48    context: Arc<Context>,
49    genesis: BTreeSet<BlockRef>,
50    transaction_verifier: Arc<dyn TransactionVerifier>,
51}
52
53impl SignedBlockVerifier {
54    pub(crate) fn new(
55        context: Arc<Context>,
56        transaction_verifier: Arc<dyn TransactionVerifier>,
57    ) -> Self {
58        let genesis = genesis_blocks(&context)
59            .into_iter()
60            .map(|b| b.reference())
61            .collect();
62        Self {
63            context,
64            genesis,
65            transaction_verifier,
66        }
67    }
68
69    fn verify_block(&self, block: &SignedBlock) -> ConsensusResult<()> {
70        let committee = &self.context.committee;
71        // The block must belong to the current epoch, have the block version of the epoch and
72        // have a valid authority index, before its signature is verified.
73        if block.epoch() != committee.epoch() {
74            return Err(ConsensusError::WrongEpoch {
75                expected: committee.epoch(),
76                actual: block.epoch(),
77            });
78        }
79        if block.round() == 0 {
80            return Err(ConsensusError::UnexpectedGenesisBlock);
81        }
82        // All authorities of an epoch must run with the same protocol config. So a block of
83        // another version is invalid, even if it is otherwise well formed. The match keeps this
84        // check complete when a new block version is added.
85        let enable_v3 = self.context.protocol_config.enable_v3();
86        let version_matches = match **block {
87            Block::V1(_) | Block::V2(_) => !enable_v3,
88            Block::V3(_) => enable_v3,
89        };
90        if !version_matches {
91            return Err(ConsensusError::UnexpectedBlockVersion {
92                version: if enable_v3 {
93                    format!("block {} is not V3 but v3 is enabled", block.slot())
94                } else {
95                    format!("block {} is V3 but v3 is disabled", block.slot())
96                },
97            });
98        }
99        if !committee.is_valid_index(block.author()) {
100            return Err(ConsensusError::InvalidAuthorityIndex {
101                loc: format!("verifying block {}", block.slot()),
102                index: block.author(),
103                max: committee.size() - 1,
104            });
105        }
106
107        // Verify the block's signature.
108        block.verify_signature(&self.context)?;
109
110        // Verify the block's ancestor refs are consistent with the block's round,
111        // and total parent stakes reach quorum.
112        if block.ancestors().len() > committee.size() {
113            return Err(ConsensusError::TooManyAncestors(
114                block.ancestors().len(),
115                committee.size(),
116            ));
117        }
118        if block.ancestors().is_empty() {
119            return Err(ConsensusError::InsufficientParentStakes {
120                parent_stakes: 0,
121                quorum: committee.quorum_threshold(),
122            });
123        }
124        let mut seen_ancestors = vec![false; committee.size()];
125        let mut parent_stakes = 0;
126        for (i, ancestor) in block.ancestors().iter().enumerate() {
127            if !committee.is_valid_index(ancestor.author) {
128                return Err(ConsensusError::InvalidAuthorityIndex {
129                    loc: format!("ancestor {}", ancestor),
130                    index: ancestor.author,
131                    max: committee.size() - 1,
132                });
133            }
134            if (i == 0 && ancestor.author != block.author())
135                || (i > 0 && ancestor.author == block.author())
136            {
137                return Err(ConsensusError::InvalidAncestorPosition {
138                    block_authority: block.author(),
139                    ancestor_authority: ancestor.author,
140                    position: i,
141                });
142            }
143            if ancestor.round >= block.round() {
144                return Err(ConsensusError::InvalidAncestorRound {
145                    ancestor: ancestor.round,
146                    block: block.round(),
147                });
148            }
149            if ancestor.round == GENESIS_ROUND && !self.genesis.contains(ancestor) {
150                return Err(ConsensusError::InvalidGenesisAncestor(*ancestor));
151            }
152            if seen_ancestors[ancestor.author] {
153                return Err(ConsensusError::DuplicatedAncestorsAuthority(
154                    ancestor.author,
155                ));
156            }
157            seen_ancestors[ancestor.author] = true;
158            // Block must have round >= 1 so checked_sub(1) should be safe.
159            if ancestor.round == block.round().checked_sub(1).unwrap() {
160                parent_stakes += committee.stake(ancestor.author);
161            }
162        }
163        if !committee.reached_quorum(parent_stakes) {
164            return Err(ConsensusError::InsufficientParentStakes {
165                parent_stakes,
166                quorum: committee.quorum_threshold(),
167            });
168        }
169
170        if enable_v3 {
171            let cutoff = block.transaction_votes_cutoff_round();
172            if cutoff >= block.round() {
173                return Err(ConsensusError::InvalidTransactionVotesCutoff {
174                    cutoff,
175                    block: block.round(),
176                });
177            }
178            self.check_transaction_votes(block)?;
179        }
180
181        let batch: Vec<_> = block.transactions().iter().map(|t| t.data()).collect();
182        self.check_transactions(&batch)
183    }
184
185    fn check_transaction_votes(&self, block: &SignedBlock) -> ConsensusResult<()> {
186        let transaction_votes = block.transaction_votes();
187        if transaction_votes.is_empty() {
188            return Ok(());
189        }
190        // This check runs before the allocation and the scans below, to keep them bounded.
191        let vote_target_limit = max_transaction_vote_targets(&self.context);
192        if transaction_votes.len() > vote_target_limit {
193            return Err(ConsensusError::InvalidTransactionVotes(format!(
194                "block at round {} from {} has {} vote targets but the limit is {}",
195                block.round(),
196                block.author(),
197                transaction_votes.len(),
198                vote_target_limit,
199            )));
200        }
201        let cutoff_round = block.transaction_votes_cutoff_round();
202        let transaction_limit = self
203            .context
204            .protocol_config
205            .max_num_transactions_in_block()
206            .min(
207                TransactionIndex::MAX
208                    .saturating_sub(NUM_RESERVED_TRANSACTION_INDICES)
209                    .into(),
210            ) as u16;
211
212        let mut vote_targets = BTreeSet::new();
213        for votes in transaction_votes {
214            if !vote_targets.insert(votes.block_ref) {
215                return Err(ConsensusError::InvalidTransactionVotes(format!(
216                    "vote target {} appears more than once",
217                    votes.block_ref,
218                )));
219            }
220            if votes.block_ref.round >= block.round() {
221                return Err(ConsensusError::InvalidTransactionVotes(format!(
222                    "vote target {} must have a round less than block round {} from {}",
223                    votes.block_ref,
224                    block.round(),
225                    block.author(),
226                )));
227            }
228            if votes.block_ref.round <= cutoff_round {
229                return Err(ConsensusError::InvalidTransactionVotes(format!(
230                    "vote target {} is at or below cutoff round {}",
231                    votes.block_ref, cutoff_round,
232                )));
233            }
234            if !self
235                .context
236                .committee
237                .is_valid_index(votes.block_ref.author)
238            {
239                return Err(ConsensusError::InvalidAuthorityIndex {
240                    loc: format!("transaction vote block {}", votes.block_ref),
241                    index: votes.block_ref.author,
242                    max: self.context.committee.size() - 1,
243                });
244            }
245            if votes.rejects.is_empty() {
246                return Err(ConsensusError::InvalidTransactionVotes(format!(
247                    "vote target {} has no reject indices",
248                    votes.block_ref,
249                )));
250            }
251            if votes.rejects.len() > transaction_limit as usize {
252                return Err(ConsensusError::InvalidTransactionVotes(format!(
253                    "vote target {} has {} reject indices but the limit is {}",
254                    votes.block_ref,
255                    votes.rejects.len(),
256                    transaction_limit,
257                )));
258            }
259            if votes.rejects.windows(2).any(|pair| pair[0] >= pair[1]) {
260                return Err(ConsensusError::InvalidTransactionVotes(format!(
261                    "reject indices for vote target {} are not strictly increasing: {:?}",
262                    votes.block_ref, votes.rejects,
263                )));
264            }
265            if let Some(reject) = votes.rejects.last()
266                && *reject >= transaction_limit
267            {
268                return Err(ConsensusError::InvalidTransactionVotes(format!(
269                    "reject index {} for vote target {} is not below limit {}",
270                    reject, votes.block_ref, transaction_limit,
271                )));
272            }
273        }
274
275        Ok(())
276    }
277
278    pub(crate) fn check_transactions(&self, batch: &[&[u8]]) -> ConsensusResult<()> {
279        let max_transaction_size_limit =
280            self.context.protocol_config.max_transaction_size_bytes() as usize;
281        for t in batch {
282            if t.len() > max_transaction_size_limit && max_transaction_size_limit > 0 {
283                return Err(ConsensusError::TransactionTooLarge {
284                    size: t.len(),
285                    limit: max_transaction_size_limit,
286                });
287            }
288        }
289
290        let max_num_transactions_limit =
291            self.context.protocol_config.max_num_transactions_in_block() as usize;
292        if batch.len() > max_num_transactions_limit && max_num_transactions_limit > 0 {
293            return Err(ConsensusError::TooManyTransactions {
294                count: batch.len(),
295                limit: max_num_transactions_limit,
296            });
297        }
298
299        let total_transactions_size_limit = self
300            .context
301            .protocol_config
302            .max_transactions_in_block_bytes() as usize;
303        if batch.iter().map(|t| t.len()).sum::<usize>() > total_transactions_size_limit
304            && total_transactions_size_limit > 0
305        {
306            return Err(ConsensusError::TooManyTransactionBytes {
307                size: batch.len(),
308                limit: total_transactions_size_limit,
309            });
310        }
311        Ok(())
312    }
313}
314
315// All block verification logic are implemented below.
316impl BlockVerifier for SignedBlockVerifier {
317    fn verify_and_vote(
318        &self,
319        block: SignedBlock,
320        serialized_block: Bytes,
321    ) -> ConsensusResult<(VerifiedBlock, Vec<TransactionIndex>)> {
322        self.verify_block(&block)?;
323
324        // If the block verification passed then we can produce the verified block, but we should only return it if the transaction verification passed as well.
325        let verified_block = VerifiedBlock::new_verified(block, serialized_block);
326
327        let rejected_transactions = if self.context.protocol_config.transaction_voting_enabled() {
328            self.vote(&verified_block)?
329        } else {
330            self.transaction_verifier
331                .verify_batch(
332                    &verified_block.reference(),
333                    &verified_block.transactions_data(),
334                )
335                .map_err(|e| ConsensusError::InvalidTransaction(e.to_string()))?;
336            vec![]
337        };
338        Ok((verified_block, rejected_transactions))
339    }
340
341    fn vote(&self, block: &VerifiedBlock) -> ConsensusResult<Vec<TransactionIndex>> {
342        self.transaction_verifier
343            .verify_and_vote_batch(&block.reference(), &block.transactions_data())
344            .map_err(|e| ConsensusError::InvalidTransaction(e.to_string()))
345    }
346}
347
348/// Allows all transactions to pass verification, for testing.
349pub struct NoopBlockVerifier;
350
351impl BlockVerifier for NoopBlockVerifier {
352    #[allow(private_interfaces)]
353    fn verify_and_vote(
354        &self,
355        _block: SignedBlock,
356        _serialized_block: Bytes,
357    ) -> ConsensusResult<(VerifiedBlock, Vec<TransactionIndex>)> {
358        Ok((
359            VerifiedBlock::new_verified(_block, _serialized_block),
360            vec![],
361        ))
362    }
363
364    fn vote(&self, _block: &VerifiedBlock) -> ConsensusResult<Vec<TransactionIndex>> {
365        Ok(vec![])
366    }
367}
368
369#[cfg(test)]
370mod test {
371    use consensus_config::{AuthorityIndex, ConsensusProtocolConfig};
372    use consensus_types::block::{BlockDigest, BlockRef, TransactionIndex};
373
374    use super::*;
375    use crate::{
376        block::{Block, BlockTransactionVotes, BlockV1, GENESIS_ROUND, TestBlock, Transaction},
377        context::Context,
378        transaction::{TransactionVerifier, ValidationError},
379    };
380
381    struct TxnSizeVerifier {}
382
383    impl TransactionVerifier for TxnSizeVerifier {
384        // Fails verification if any transaction is < 4 bytes.
385        fn verify_batch(
386            &self,
387            _block_ref: &BlockRef,
388            transactions: &[&[u8]],
389        ) -> Result<(), ValidationError> {
390            for txn in transactions {
391                if txn.len() < 4 {
392                    return Err(ValidationError::InvalidTransaction(format!(
393                        "Length {} is too short!",
394                        txn.len()
395                    )));
396                }
397            }
398            Ok(())
399        }
400
401        // Fails verification if any transaction is < 4 bytes.
402        // Rejects transactions with length [4, 16) bytes.
403        fn verify_and_vote_batch(
404            &self,
405            _block_ref: &BlockRef,
406            batch: &[&[u8]],
407        ) -> Result<Vec<TransactionIndex>, ValidationError> {
408            let mut rejected_indices = vec![];
409            for (i, txn) in batch.iter().enumerate() {
410                if txn.len() < 4 {
411                    return Err(ValidationError::InvalidTransaction(format!(
412                        "Length {} is too short!",
413                        txn.len()
414                    )));
415                }
416                if txn.len() < 16 {
417                    rejected_indices.push(i as TransactionIndex);
418                }
419            }
420            Ok(rejected_indices)
421        }
422    }
423
424    #[tokio::test]
425    async fn test_verify_block() {
426        let (context, keypairs) = Context::new_for_test(4);
427        let context = Arc::new(context);
428        const AUTHOR: u32 = 2;
429        let author_protocol_keypair = &keypairs[AUTHOR as usize].1;
430        let verifier = SignedBlockVerifier::new(context.clone(), Arc::new(TxnSizeVerifier {}));
431
432        let test_block = TestBlock::new(10, AUTHOR)
433            .set_ancestors_raw(vec![
434                BlockRef::new(9, AuthorityIndex::new_for_test(2), BlockDigest::MIN),
435                BlockRef::new(9, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
436                BlockRef::new(9, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
437                BlockRef::new(7, AuthorityIndex::new_for_test(3), BlockDigest::MIN),
438            ])
439            .set_transactions(vec![Transaction::new(vec![4; 8])]);
440
441        // Valid SignedBlock.
442        {
443            let block = test_block.clone().build();
444            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
445            verifier.verify_block(&signed_block).unwrap();
446        }
447
448        // Block with wrong epoch.
449        {
450            let block = test_block.clone().set_epoch(1).build();
451            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
452            assert!(matches!(
453                verifier.verify_block(&signed_block),
454                Err(ConsensusError::WrongEpoch {
455                    expected: _,
456                    actual: _
457                })
458            ));
459        }
460
461        // Block at genesis round.
462        {
463            let block = test_block.clone().set_round(0).build();
464            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
465            assert!(matches!(
466                verifier.verify_block(&signed_block),
467                Err(ConsensusError::UnexpectedGenesisBlock)
468            ));
469        }
470
471        // Block with invalid authority index.
472        {
473            let block = test_block
474                .clone()
475                .set_author(AuthorityIndex::new_for_test(4))
476                .build();
477            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
478            assert!(matches!(
479                verifier.verify_block(&signed_block),
480                Err(ConsensusError::InvalidAuthorityIndex {
481                    loc: _,
482                    index: _,
483                    max: _
484                })
485            ));
486        }
487
488        // Block with mismatched authority index and signature.
489        {
490            let block = test_block
491                .clone()
492                .set_author(AuthorityIndex::new_for_test(1))
493                .build();
494            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
495            assert!(matches!(
496                verifier.verify_block(&signed_block),
497                Err(ConsensusError::SignatureVerificationFailure(_))
498            ));
499        }
500
501        // Block with wrong key.
502        {
503            let block = test_block.clone().build();
504            let signed_block = SignedBlock::new(block, &keypairs[3].1).unwrap();
505            assert!(matches!(
506                verifier.verify_block(&signed_block),
507                Err(ConsensusError::SignatureVerificationFailure(_))
508            ));
509        }
510
511        // Block without signature.
512        {
513            let block = test_block.clone().build();
514            let mut signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
515            signed_block.clear_signature();
516            assert!(matches!(
517                verifier.verify_block(&signed_block),
518                Err(ConsensusError::MalformedSignature(_))
519            ));
520        }
521
522        // Block with invalid ancestor round.
523        {
524            let block = test_block
525                .clone()
526                .set_ancestors_raw(vec![
527                    BlockRef::new(9, AuthorityIndex::new_for_test(2), BlockDigest::MIN),
528                    BlockRef::new(9, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
529                    BlockRef::new(9, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
530                    BlockRef::new(10, AuthorityIndex::new_for_test(3), BlockDigest::MIN),
531                ])
532                .build();
533            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
534            assert!(matches!(
535                verifier.verify_block(&signed_block),
536                Err(ConsensusError::InvalidAncestorRound {
537                    ancestor: _,
538                    block: _
539                })
540            ));
541        }
542
543        // Block with parents not reaching quorum.
544        {
545            let block = test_block
546                .clone()
547                .set_ancestors_raw(vec![
548                    BlockRef::new(9, AuthorityIndex::new_for_test(2), BlockDigest::MIN),
549                    BlockRef::new(9, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
550                    BlockRef::new(8, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
551                    BlockRef::new(8, AuthorityIndex::new_for_test(3), BlockDigest::MIN),
552                ])
553                .build();
554            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
555            assert!(matches!(
556                verifier.verify_block(&signed_block),
557                Err(ConsensusError::InsufficientParentStakes {
558                    parent_stakes: _,
559                    quorum: _
560                })
561            ));
562        }
563
564        // Block with too many ancestors.
565        {
566            let block = test_block
567                .clone()
568                .set_ancestors_raw(vec![
569                    BlockRef::new(9, AuthorityIndex::new_for_test(2), BlockDigest::MIN),
570                    BlockRef::new(9, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
571                    BlockRef::new(8, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
572                    BlockRef::new(8, AuthorityIndex::new_for_test(3), BlockDigest::MIN),
573                    BlockRef::new(9, AuthorityIndex::new_for_test(3), BlockDigest::MIN),
574                ])
575                .build();
576            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
577            assert!(matches!(
578                verifier.verify_block(&signed_block),
579                Err(ConsensusError::TooManyAncestors(_, _))
580            ));
581        }
582
583        // Block without own ancestor.
584        {
585            let block = test_block
586                .clone()
587                .set_ancestors_raw(vec![
588                    BlockRef::new(9, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
589                    BlockRef::new(8, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
590                    BlockRef::new(8, AuthorityIndex::new_for_test(3), BlockDigest::MIN),
591                ])
592                .build();
593            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
594            assert!(matches!(
595                verifier.verify_block(&signed_block),
596                Err(ConsensusError::InvalidAncestorPosition {
597                    block_authority: _,
598                    ancestor_authority: _,
599                    position: _
600                })
601            ));
602        }
603
604        // Block with own ancestor at wrong position.
605        {
606            let block = test_block
607                .clone()
608                .set_ancestors_raw(vec![
609                    BlockRef::new(9, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
610                    BlockRef::new(8, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
611                    BlockRef::new(8, AuthorityIndex::new_for_test(2), BlockDigest::MIN),
612                    BlockRef::new(8, AuthorityIndex::new_for_test(3), BlockDigest::MIN),
613                ])
614                .build();
615            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
616            assert!(matches!(
617                verifier.verify_block(&signed_block),
618                Err(ConsensusError::InvalidAncestorPosition {
619                    block_authority: _,
620                    ancestor_authority: _,
621                    position: _
622                })
623            ));
624        }
625
626        // Block with ancestors from the same authority.
627        {
628            let block = test_block
629                .clone()
630                .set_ancestors_raw(vec![
631                    BlockRef::new(8, AuthorityIndex::new_for_test(2), BlockDigest::MIN),
632                    BlockRef::new(8, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
633                    BlockRef::new(8, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
634                ])
635                .build();
636            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
637            assert!(matches!(
638                verifier.verify_block(&signed_block),
639                Err(ConsensusError::DuplicatedAncestorsAuthority(_))
640            ));
641        }
642
643        // Block with transaction too large.
644        {
645            let block = test_block
646                .clone()
647                .set_transactions(vec![Transaction::new(vec![4; 257 * 1024])])
648                .build();
649            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
650            assert!(matches!(
651                verifier.verify_block(&signed_block),
652                Err(ConsensusError::TransactionTooLarge { size: _, limit: _ })
653            ));
654        }
655
656        // Block with too many transactions.
657        {
658            let block = test_block
659                .clone()
660                .set_transactions((0..1000).map(|_| Transaction::new(vec![4; 8])).collect())
661                .build();
662            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
663            assert!(matches!(
664                verifier.verify_block(&signed_block),
665                Err(ConsensusError::TooManyTransactions { count: _, limit: _ })
666            ));
667        }
668
669        // Block with too many transaction bytes.
670        {
671            let block = test_block
672                .clone()
673                .set_transactions(
674                    (0..100)
675                        .map(|_| Transaction::new(vec![4; 8 * 1024]))
676                        .collect(),
677                )
678                .build();
679            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
680            assert!(matches!(
681                verifier.verify_block(&signed_block),
682                Err(ConsensusError::TooManyTransactionBytes { size: _, limit: _ })
683            ));
684        }
685
686        // Block with an invalid transaction.
687        {
688            let block = test_block
689                .clone()
690                .set_transactions(vec![
691                    Transaction::new(vec![1; 4]),
692                    Transaction::new(vec![1; 2]),
693                ])
694                .build();
695            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
696            let serialized_block = signed_block
697                .serialize()
698                .expect("Block serialization failed.");
699            assert!(matches!(
700                verifier.verify_and_vote(signed_block, serialized_block),
701                Err(ConsensusError::InvalidTransaction(_))
702            ));
703        }
704    }
705
706    #[tokio::test]
707    async fn test_block_version_matches_protocol_config() {
708        let (context, keypairs) = Context::new_for_test(4);
709        let epoch = context.committee.epoch();
710        const AUTHOR: u32 = 2;
711        let author_key = &keypairs[AUTHOR as usize].1;
712        let mut v3_context = context.clone();
713        v3_context.protocol_config.set_enable_v3_for_testing(true);
714        // Genesis blocks differ between block versions, so a round 1 block of the wrong version
715        // also has unknown genesis ancestors.
716        let genesis_ancestors = |context: &Context| {
717            let mut refs = genesis_blocks(context)
718                .iter()
719                .map(|block| block.reference())
720                .collect::<Vec<_>>();
721            refs.swap(0, AUTHOR as usize);
722            refs
723        };
724        let v2_genesis = genesis_ancestors(&context);
725        let v3_genesis = genesis_ancestors(&v3_context);
726        let verifier = SignedBlockVerifier::new(Arc::new(context), Arc::new(TxnSizeVerifier {}));
727        let v3_verifier =
728            SignedBlockVerifier::new(Arc::new(v3_context), Arc::new(TxnSizeVerifier {}));
729        let ancestors = vec![
730            BlockRef::new(9, AuthorityIndex::new_for_test(2), BlockDigest::MIN),
731            BlockRef::new(9, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
732            BlockRef::new(9, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
733        ];
734        let test_block = TestBlock::new(10, AUTHOR).set_ancestors_raw(ancestors.clone());
735        let v1 = SignedBlock::new(
736            Block::V1(BlockV1::new(
737                epoch,
738                10,
739                AuthorityIndex::new_for_test(AUTHOR),
740                0,
741                ancestors,
742                vec![],
743                vec![],
744                vec![],
745            )),
746            author_key,
747        )
748        .unwrap();
749        let v2 = SignedBlock::new(test_block.clone().build(), author_key).unwrap();
750        let v3 = SignedBlock::new(test_block.clone().build_v3(0), author_key).unwrap();
751        assert_eq!(v1.transaction_votes_cutoff_round(), GENESIS_ROUND);
752        assert_eq!(v2.transaction_votes_cutoff_round(), GENESIS_ROUND);
753
754        // Without v3, only the earlier block versions are valid.
755        verifier.verify_block(&v1).unwrap();
756        verifier.verify_block(&v2).unwrap();
757        assert!(matches!(
758            verifier.verify_block(&v3),
759            Err(ConsensusError::UnexpectedBlockVersion { version })
760                if version.contains("is V3 but v3 is disabled")
761        ));
762
763        // With v3, only V3 blocks are valid.
764        v3_verifier.verify_block(&v3).unwrap();
765        for block in [&v1, &v2] {
766            assert!(matches!(
767                v3_verifier.verify_block(block),
768                Err(ConsensusError::UnexpectedBlockVersion { version })
769                    if version.contains("is not V3 but v3 is enabled")
770            ));
771        }
772
773        // The version check must run before the genesis ancestor check, which would otherwise
774        // report the unknown genesis ancestors of a round 1 block of the wrong version.
775        let round_1_v3 = SignedBlock::new(
776            TestBlock::new(1, AUTHOR)
777                .set_ancestors_raw(v3_genesis)
778                .build_v3(0),
779            author_key,
780        )
781        .unwrap();
782        v3_verifier.verify_block(&round_1_v3).unwrap();
783        assert!(matches!(
784            verifier.verify_block(&round_1_v3),
785            Err(ConsensusError::UnexpectedBlockVersion { version })
786                if version.contains("is V3 but v3 is disabled")
787        ));
788        let round_1_v2 = SignedBlock::new(
789            TestBlock::new(1, AUTHOR)
790                .set_ancestors_raw(v2_genesis)
791                .build(),
792            author_key,
793        )
794        .unwrap();
795        verifier.verify_block(&round_1_v2).unwrap();
796        assert!(matches!(
797            v3_verifier.verify_block(&round_1_v2),
798            Err(ConsensusError::UnexpectedBlockVersion { version })
799                if version.contains("is not V3 but v3 is enabled")
800        ));
801
802        let invalid_cutoff = SignedBlock::new(test_block.build_v3(10), author_key).unwrap();
803        assert!(matches!(
804            v3_verifier.verify_block(&invalid_cutoff),
805            Err(ConsensusError::InvalidTransactionVotesCutoff {
806                cutoff: 10,
807                block: 10,
808            })
809        ));
810    }
811
812    #[tokio::test]
813    async fn test_v3_transaction_votes_are_bounded_and_valid() {
814        let (mut context, keypairs) = Context::new_for_test(4);
815        context.protocol_config.set_enable_v3_for_testing(true);
816        context
817            .protocol_config
818            .set_max_num_transactions_in_block_for_testing(2);
819        let context = Arc::new(context);
820        const AUTHOR: u32 = 2;
821        let verifier = SignedBlockVerifier::new(context, Arc::new(TxnSizeVerifier {}));
822        let direct_target = BlockRef::new(9, AuthorityIndex::new_for_test(0), BlockDigest::MIN);
823        let old_ancestor = BlockRef::new(7, AuthorityIndex::new_for_test(3), BlockDigest::MIN);
824        let test_block = TestBlock::new(10, AUTHOR).set_ancestors_raw(vec![
825            BlockRef::new(9, AuthorityIndex::new_for_test(2), BlockDigest::MIN),
826            direct_target,
827            BlockRef::new(9, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
828        ]);
829        let verify = |votes, cutoff_round| {
830            let block = test_block
831                .clone()
832                .set_transaction_votes(votes)
833                .build_v3(cutoff_round);
834            let signed_block = SignedBlock::new(block, &keypairs[AUTHOR as usize].1).unwrap();
835            verifier.verify_block(&signed_block)
836        };
837        let assert_invalid = |result: ConsensusResult<()>, reason: &str| match result {
838            Err(ConsensusError::InvalidTransactionVotes(message)) => {
839                assert!(message.contains(reason), "unexpected error: {message}");
840            }
841            other => panic!("expected invalid transaction votes, got {other:?}"),
842        };
843
844        verify(
845            vec![BlockTransactionVotes {
846                block_ref: direct_target,
847                rejects: vec![0, 1],
848            }],
849            8,
850        )
851        .unwrap();
852
853        assert_invalid(
854            verify(
855                vec![
856                    BlockTransactionVotes {
857                        block_ref: direct_target,
858                        rejects: vec![0],
859                    },
860                    BlockTransactionVotes {
861                        block_ref: direct_target,
862                        rejects: vec![1],
863                    },
864                ],
865                8,
866            ),
867            "appears more than once",
868        );
869        verify(
870            vec![BlockTransactionVotes {
871                block_ref: old_ancestor,
872                rejects: vec![0],
873            }],
874            6,
875        )
876        .unwrap();
877        assert_invalid(
878            verify(
879                vec![BlockTransactionVotes {
880                    block_ref: BlockRef::new(10, AuthorityIndex::new_for_test(3), BlockDigest::MIN),
881                    rejects: vec![0],
882                }],
883                8,
884            ),
885            "must have a round less than block round",
886        );
887        assert_invalid(
888            verify(
889                vec![BlockTransactionVotes {
890                    block_ref: direct_target,
891                    rejects: vec![0],
892                }],
893                9,
894            ),
895            "at or below cutoff",
896        );
897        assert_invalid(
898            verify(
899                vec![BlockTransactionVotes {
900                    block_ref: direct_target,
901                    rejects: vec![],
902                }],
903                8,
904            ),
905            "has no reject indices",
906        );
907        assert_invalid(
908            verify(
909                vec![BlockTransactionVotes {
910                    block_ref: direct_target,
911                    rejects: vec![0, 0],
912                }],
913                8,
914            ),
915            "not strictly increasing",
916        );
917        assert_invalid(
918            verify(
919                vec![BlockTransactionVotes {
920                    block_ref: direct_target,
921                    rejects: vec![0, 1, 2],
922                }],
923                8,
924            ),
925            "3 reject indices but the limit is 2",
926        );
927        assert_invalid(
928            verify(
929                vec![BlockTransactionVotes {
930                    block_ref: direct_target,
931                    rejects: vec![1, 0],
932                }],
933                8,
934            ),
935            "not strictly increasing",
936        );
937        assert_invalid(
938            verify(
939                vec![BlockTransactionVotes {
940                    block_ref: direct_target,
941                    rejects: vec![2],
942                }],
943                8,
944            ),
945            "is not below limit 2",
946        );
947    }
948
949    #[tokio::test]
950    async fn test_v3_transaction_count_limit() {
951        let (mut context, keypairs) = Context::new_for_test(4);
952        context.protocol_config.set_enable_v3_for_testing(true);
953        context
954            .protocol_config
955            .set_max_num_transactions_in_block_for_testing(2);
956        let context = Arc::new(context);
957        const AUTHOR: u32 = 2;
958        let verifier = SignedBlockVerifier::new(context, Arc::new(TxnSizeVerifier {}));
959        let test_block = TestBlock::new(10, AUTHOR).set_ancestors_raw(vec![
960            BlockRef::new(9, AuthorityIndex::new_for_test(2), BlockDigest::MIN),
961            BlockRef::new(9, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
962            BlockRef::new(9, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
963        ]);
964        let verify = |num_transactions: usize| {
965            let block = test_block
966                .clone()
967                .set_transactions(vec![Transaction::new(vec![1]); num_transactions])
968                .build_v3(8);
969            let signed_block = SignedBlock::new(block, &keypairs[AUTHOR as usize].1).unwrap();
970            verifier.verify_block(&signed_block)
971        };
972
973        verify(2).unwrap();
974        assert!(matches!(
975            verify(3),
976            Err(ConsensusError::TooManyTransactions { count: 3, limit: 2 })
977        ));
978    }
979
980    #[tokio::test]
981    async fn test_v3_transaction_vote_target_limit() {
982        let (mut context, keypairs) = Context::new_for_test(4);
983        context.protocol_config.set_enable_v3_for_testing(true);
984        context.protocol_config.set_gc_depth_for_testing(2);
985        let vote_target_limit = max_transaction_vote_targets(&context);
986        assert_eq!(vote_target_limit, 8);
987        let context = Arc::new(context);
988        const AUTHOR: u32 = 2;
989        let verifier = SignedBlockVerifier::new(context, Arc::new(TxnSizeVerifier {}));
990        let test_block = TestBlock::new(10, AUTHOR).set_ancestors_raw(vec![
991            BlockRef::new(9, AuthorityIndex::new_for_test(2), BlockDigest::MIN),
992            BlockRef::new(9, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
993            BlockRef::new(9, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
994        ]);
995        // Rounds 8 and 9 hold one target per authority, which is exactly the limit.
996        let mut transaction_votes = (8..=9)
997            .flat_map(|round| {
998                (0..4).map(move |author| BlockTransactionVotes {
999                    block_ref: BlockRef::new(
1000                        round,
1001                        AuthorityIndex::new_for_test(author),
1002                        BlockDigest::MIN,
1003                    ),
1004                    rejects: vec![0],
1005                })
1006            })
1007            .collect::<Vec<_>>();
1008        let verify = |transaction_votes: Vec<BlockTransactionVotes>| {
1009            let block = test_block
1010                .clone()
1011                .set_transaction_votes(transaction_votes)
1012                .build_v3(7);
1013            let signed_block = SignedBlock::new(block, &keypairs[AUTHOR as usize].1).unwrap();
1014            verifier.verify_block(&signed_block)
1015        };
1016
1017        assert_eq!(transaction_votes.len(), vote_target_limit);
1018        verify(transaction_votes.clone()).unwrap();
1019
1020        // An equivocating block in round 9 adds the target above the limit.
1021        transaction_votes.push(BlockTransactionVotes {
1022            block_ref: BlockRef::new(9, AuthorityIndex::new_for_test(0), BlockDigest::MAX),
1023            rejects: vec![0],
1024        });
1025        assert!(matches!(
1026            verify(transaction_votes),
1027            Err(ConsensusError::InvalidTransactionVotes(message))
1028                if message.contains("has 9 vote targets but the limit is 8")
1029        ));
1030    }
1031
1032    // `TransactionConsumer::new()` asserts max_num_transactions_in_block is not more than the
1033    // first reserved index. Votes must not use reserved indices, even at that highest limit.
1034    #[tokio::test]
1035    async fn test_v3_transaction_votes_exclude_reserved_indices() {
1036        let first_reserved_index =
1037            TransactionIndex::MAX.saturating_sub(NUM_RESERVED_TRANSACTION_INDICES);
1038        let (mut context, keypairs) = Context::new_for_test(4);
1039        context.protocol_config.set_enable_v3_for_testing(true);
1040        context
1041            .protocol_config
1042            .set_max_num_transactions_in_block_for_testing(first_reserved_index.into());
1043        let context = Arc::new(context);
1044        const AUTHOR: u32 = 2;
1045        let verifier = SignedBlockVerifier::new(context, Arc::new(TxnSizeVerifier {}));
1046        let direct_target = BlockRef::new(9, AuthorityIndex::new_for_test(0), BlockDigest::MIN);
1047        let test_block = TestBlock::new(10, AUTHOR).set_ancestors_raw(vec![
1048            BlockRef::new(9, AuthorityIndex::new_for_test(2), BlockDigest::MIN),
1049            direct_target,
1050            BlockRef::new(9, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
1051        ]);
1052        let verify = |rejects: Vec<TransactionIndex>| {
1053            let block = test_block
1054                .clone()
1055                .set_transaction_votes(vec![BlockTransactionVotes {
1056                    block_ref: direct_target,
1057                    rejects,
1058                }])
1059                .build_v3(8);
1060            let signed_block = SignedBlock::new(block, &keypairs[AUTHOR as usize].1).unwrap();
1061            verifier.verify_block(&signed_block)
1062        };
1063
1064        verify(vec![first_reserved_index - 1]).unwrap();
1065        assert!(matches!(
1066            verify(vec![first_reserved_index]),
1067            Err(ConsensusError::InvalidTransactionVotes(_))
1068        ));
1069    }
1070
1071    #[tokio::test]
1072    async fn test_verify_and_vote_transactions() {
1073        let mut protocol_config = ConsensusProtocolConfig::for_testing();
1074        protocol_config.set_transaction_voting_enabled_for_testing(true);
1075
1076        let (context, keypairs) = Context::new_for_test(4);
1077        let context = Arc::new(context.with_protocol_config(protocol_config));
1078
1079        const AUTHOR: u32 = 2;
1080        let author_protocol_keypair = &keypairs[AUTHOR as usize].1;
1081        let verifier = SignedBlockVerifier::new(context.clone(), Arc::new(TxnSizeVerifier {}));
1082
1083        let base_block = TestBlock::new(10, AUTHOR).set_ancestors_raw(vec![
1084            BlockRef::new(9, AuthorityIndex::new_for_test(2), BlockDigest::MIN),
1085            BlockRef::new(9, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
1086            BlockRef::new(9, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
1087            BlockRef::new(7, AuthorityIndex::new_for_test(3), BlockDigest::MIN),
1088        ]);
1089
1090        // Block with all transactions valid and accepted.
1091        {
1092            let block = base_block
1093                .clone()
1094                .set_transactions(vec![
1095                    Transaction::new(vec![1; 16]),
1096                    Transaction::new(vec![2; 16]),
1097                    Transaction::new(vec![3; 16]),
1098                    Transaction::new(vec![4; 16]),
1099                ])
1100                .build();
1101            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
1102            let serialized_block = signed_block
1103                .serialize()
1104                .expect("Block serialization failed.");
1105            let (verified_block, rejected_transactions) = verifier
1106                .verify_and_vote(signed_block, serialized_block.clone())
1107                .unwrap();
1108            assert_eq!(rejected_transactions, Vec::<TransactionIndex>::new());
1109            assert_eq!(verified_block.serialized().clone(), serialized_block);
1110        }
1111
1112        // Block with 2 transactions rejected.
1113        {
1114            let block = base_block
1115                .clone()
1116                .set_transactions(vec![
1117                    Transaction::new(vec![1; 16]),
1118                    Transaction::new(vec![2; 8]),
1119                    Transaction::new(vec![3; 16]),
1120                    Transaction::new(vec![4; 9]),
1121                ])
1122                .build();
1123            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
1124            let serialized_block = signed_block
1125                .serialize()
1126                .expect("Block serialization failed.");
1127            let (_verified_block, rejected_transactions) = verifier
1128                .verify_and_vote(signed_block, serialized_block)
1129                .unwrap();
1130            assert_eq!(
1131                rejected_transactions,
1132                vec![1 as TransactionIndex, 3 as TransactionIndex],
1133            );
1134        }
1135
1136        // Block with an invalid transaction returns an error.
1137        {
1138            let block = base_block
1139                .clone()
1140                .set_transactions(vec![
1141                    Transaction::new(vec![1; 16]),
1142                    Transaction::new(vec![2; 8]),
1143                    Transaction::new(vec![3; 1]), // Invalid transaction size
1144                    Transaction::new(vec![4; 9]),
1145                ])
1146                .build();
1147            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
1148            let serialized_block = signed_block
1149                .serialize()
1150                .expect("Block serialization failed.");
1151            assert!(matches!(
1152                verifier.verify_and_vote(signed_block, serialized_block),
1153                Err(ConsensusError::InvalidTransaction(_))
1154            ));
1155        }
1156    }
1157}