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