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.mysticeti_fastpath() {
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;
245    use consensus_types::block::{BlockDigest, BlockRef, TransactionIndex};
246    use sui_protocol_config::ProtocolConfig;
247
248    use super::*;
249    use crate::{
250        block::{TestBlock, Transaction},
251        context::Context,
252        transaction::{TransactionVerifier, ValidationError},
253    };
254
255    struct TxnSizeVerifier {}
256
257    impl TransactionVerifier for TxnSizeVerifier {
258        // Fails verification if any transaction is < 4 bytes.
259        fn verify_batch(&self, transactions: &[&[u8]]) -> Result<(), ValidationError> {
260            for txn in transactions {
261                if txn.len() < 4 {
262                    return Err(ValidationError::InvalidTransaction(format!(
263                        "Length {} is too short!",
264                        txn.len()
265                    )));
266                }
267            }
268            Ok(())
269        }
270
271        // Fails verification if any transaction is < 4 bytes.
272        // Rejects transactions with length [4, 16) bytes.
273        fn verify_and_vote_batch(
274            &self,
275            _block_ref: &BlockRef,
276            batch: &[&[u8]],
277        ) -> Result<Vec<TransactionIndex>, ValidationError> {
278            let mut rejected_indices = vec![];
279            for (i, txn) in batch.iter().enumerate() {
280                if txn.len() < 4 {
281                    return Err(ValidationError::InvalidTransaction(format!(
282                        "Length {} is too short!",
283                        txn.len()
284                    )));
285                }
286                if txn.len() < 16 {
287                    rejected_indices.push(i as TransactionIndex);
288                }
289            }
290            Ok(rejected_indices)
291        }
292    }
293
294    #[tokio::test]
295    async fn test_verify_block() {
296        let (context, keypairs) = Context::new_for_test(4);
297        let context = Arc::new(context);
298        const AUTHOR: u32 = 2;
299        let author_protocol_keypair = &keypairs[AUTHOR as usize].1;
300        let verifier = SignedBlockVerifier::new(context.clone(), Arc::new(TxnSizeVerifier {}));
301
302        let test_block = TestBlock::new(10, AUTHOR)
303            .set_ancestors(vec![
304                BlockRef::new(9, AuthorityIndex::new_for_test(2), BlockDigest::MIN),
305                BlockRef::new(9, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
306                BlockRef::new(9, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
307                BlockRef::new(7, AuthorityIndex::new_for_test(3), BlockDigest::MIN),
308            ])
309            .set_transactions(vec![Transaction::new(vec![4; 8])]);
310
311        // Valid SignedBlock.
312        {
313            let block = test_block.clone().build();
314            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
315            verifier.verify_block(&signed_block).unwrap();
316        }
317
318        // Block with wrong epoch.
319        {
320            let block = test_block.clone().set_epoch(1).build();
321            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
322            assert!(matches!(
323                verifier.verify_block(&signed_block),
324                Err(ConsensusError::WrongEpoch {
325                    expected: _,
326                    actual: _
327                })
328            ));
329        }
330
331        // Block at genesis round.
332        {
333            let block = test_block.clone().set_round(0).build();
334            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
335            assert!(matches!(
336                verifier.verify_block(&signed_block),
337                Err(ConsensusError::UnexpectedGenesisBlock)
338            ));
339        }
340
341        // Block with invalid authority index.
342        {
343            let block = test_block
344                .clone()
345                .set_author(AuthorityIndex::new_for_test(4))
346                .build();
347            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
348            assert!(matches!(
349                verifier.verify_block(&signed_block),
350                Err(ConsensusError::InvalidAuthorityIndex { index: _, max: _ })
351            ));
352        }
353
354        // Block with mismatched authority index and signature.
355        {
356            let block = test_block
357                .clone()
358                .set_author(AuthorityIndex::new_for_test(1))
359                .build();
360            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
361            assert!(matches!(
362                verifier.verify_block(&signed_block),
363                Err(ConsensusError::SignatureVerificationFailure(_))
364            ));
365        }
366
367        // Block with wrong key.
368        {
369            let block = test_block.clone().build();
370            let signed_block = SignedBlock::new(block, &keypairs[3].1).unwrap();
371            assert!(matches!(
372                verifier.verify_block(&signed_block),
373                Err(ConsensusError::SignatureVerificationFailure(_))
374            ));
375        }
376
377        // Block without signature.
378        {
379            let block = test_block.clone().build();
380            let mut signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
381            signed_block.clear_signature();
382            assert!(matches!(
383                verifier.verify_block(&signed_block),
384                Err(ConsensusError::MalformedSignature(_))
385            ));
386        }
387
388        // Block with invalid ancestor round.
389        {
390            let block = test_block
391                .clone()
392                .set_ancestors(vec![
393                    BlockRef::new(9, AuthorityIndex::new_for_test(2), BlockDigest::MIN),
394                    BlockRef::new(9, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
395                    BlockRef::new(9, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
396                    BlockRef::new(10, AuthorityIndex::new_for_test(3), BlockDigest::MIN),
397                ])
398                .build();
399            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
400            assert!(matches!(
401                verifier.verify_block(&signed_block),
402                Err(ConsensusError::InvalidAncestorRound {
403                    ancestor: _,
404                    block: _
405                })
406            ));
407        }
408
409        // Block with parents not reaching quorum.
410        {
411            let block = test_block
412                .clone()
413                .set_ancestors(vec![
414                    BlockRef::new(9, AuthorityIndex::new_for_test(2), BlockDigest::MIN),
415                    BlockRef::new(9, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
416                    BlockRef::new(8, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
417                    BlockRef::new(8, AuthorityIndex::new_for_test(3), BlockDigest::MIN),
418                ])
419                .build();
420            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
421            assert!(matches!(
422                verifier.verify_block(&signed_block),
423                Err(ConsensusError::InsufficientParentStakes {
424                    parent_stakes: _,
425                    quorum: _
426                })
427            ));
428        }
429
430        // Block with too many ancestors.
431        {
432            let block = test_block
433                .clone()
434                .set_ancestors(vec![
435                    BlockRef::new(9, AuthorityIndex::new_for_test(2), BlockDigest::MIN),
436                    BlockRef::new(9, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
437                    BlockRef::new(8, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
438                    BlockRef::new(8, AuthorityIndex::new_for_test(3), BlockDigest::MIN),
439                    BlockRef::new(9, AuthorityIndex::new_for_test(3), BlockDigest::MIN),
440                ])
441                .build();
442            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
443            assert!(matches!(
444                verifier.verify_block(&signed_block),
445                Err(ConsensusError::TooManyAncestors(_, _))
446            ));
447        }
448
449        // Block without own ancestor.
450        {
451            let block = test_block
452                .clone()
453                .set_ancestors(vec![
454                    BlockRef::new(9, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
455                    BlockRef::new(8, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
456                    BlockRef::new(8, AuthorityIndex::new_for_test(3), BlockDigest::MIN),
457                ])
458                .build();
459            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
460            assert!(matches!(
461                verifier.verify_block(&signed_block),
462                Err(ConsensusError::InvalidAncestorPosition {
463                    block_authority: _,
464                    ancestor_authority: _,
465                    position: _
466                })
467            ));
468        }
469
470        // Block with own ancestor at wrong position.
471        {
472            let block = test_block
473                .clone()
474                .set_ancestors(vec![
475                    BlockRef::new(9, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
476                    BlockRef::new(8, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
477                    BlockRef::new(8, AuthorityIndex::new_for_test(2), BlockDigest::MIN),
478                    BlockRef::new(8, AuthorityIndex::new_for_test(3), BlockDigest::MIN),
479                ])
480                .build();
481            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
482            assert!(matches!(
483                verifier.verify_block(&signed_block),
484                Err(ConsensusError::InvalidAncestorPosition {
485                    block_authority: _,
486                    ancestor_authority: _,
487                    position: _
488                })
489            ));
490        }
491
492        // Block with ancestors from the same authority.
493        {
494            let block = test_block
495                .clone()
496                .set_ancestors(vec![
497                    BlockRef::new(8, AuthorityIndex::new_for_test(2), BlockDigest::MIN),
498                    BlockRef::new(8, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
499                    BlockRef::new(8, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
500                ])
501                .build();
502            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
503            assert!(matches!(
504                verifier.verify_block(&signed_block),
505                Err(ConsensusError::DuplicatedAncestorsAuthority(_))
506            ));
507        }
508
509        // Block with transaction too large.
510        {
511            let block = test_block
512                .clone()
513                .set_transactions(vec![Transaction::new(vec![4; 257 * 1024])])
514                .build();
515            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
516            assert!(matches!(
517                verifier.verify_block(&signed_block),
518                Err(ConsensusError::TransactionTooLarge { size: _, limit: _ })
519            ));
520        }
521
522        // Block with too many transactions.
523        {
524            let block = test_block
525                .clone()
526                .set_transactions((0..1000).map(|_| Transaction::new(vec![4; 8])).collect())
527                .build();
528            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
529            assert!(matches!(
530                verifier.verify_block(&signed_block),
531                Err(ConsensusError::TooManyTransactions { count: _, limit: _ })
532            ));
533        }
534
535        // Block with too many transaction bytes.
536        {
537            let block = test_block
538                .clone()
539                .set_transactions(
540                    (0..100)
541                        .map(|_| Transaction::new(vec![4; 8 * 1024]))
542                        .collect(),
543                )
544                .build();
545            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
546            assert!(matches!(
547                verifier.verify_block(&signed_block),
548                Err(ConsensusError::TooManyTransactionBytes { size: _, limit: _ })
549            ));
550        }
551
552        // Block with an invalid transaction.
553        {
554            let block = test_block
555                .clone()
556                .set_transactions(vec![
557                    Transaction::new(vec![1; 4]),
558                    Transaction::new(vec![1; 2]),
559                ])
560                .build();
561            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
562            let serialized_block = signed_block
563                .serialize()
564                .expect("Block serialization failed.");
565            assert!(matches!(
566                verifier.verify_and_vote(signed_block, serialized_block),
567                Err(ConsensusError::InvalidTransaction(_))
568            ));
569        }
570    }
571
572    #[tokio::test]
573    async fn test_verify_and_vote_transactions() {
574        let mut protocol_config = ProtocolConfig::get_for_max_version_UNSAFE();
575        protocol_config.set_mysticeti_fastpath_for_testing(true);
576
577        let (context, keypairs) = Context::new_for_test(4);
578        let context = Arc::new(context.with_protocol_config(protocol_config));
579
580        const AUTHOR: u32 = 2;
581        let author_protocol_keypair = &keypairs[AUTHOR as usize].1;
582        let verifier = SignedBlockVerifier::new(context.clone(), Arc::new(TxnSizeVerifier {}));
583
584        let base_block = TestBlock::new(10, AUTHOR).set_ancestors(vec![
585            BlockRef::new(9, AuthorityIndex::new_for_test(2), BlockDigest::MIN),
586            BlockRef::new(9, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
587            BlockRef::new(9, AuthorityIndex::new_for_test(1), BlockDigest::MIN),
588            BlockRef::new(7, AuthorityIndex::new_for_test(3), BlockDigest::MIN),
589        ]);
590
591        // Block with all transactions valid and accepted.
592        {
593            let block = base_block
594                .clone()
595                .set_transactions(vec![
596                    Transaction::new(vec![1; 16]),
597                    Transaction::new(vec![2; 16]),
598                    Transaction::new(vec![3; 16]),
599                    Transaction::new(vec![4; 16]),
600                ])
601                .build();
602            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
603            let serialized_block = signed_block
604                .serialize()
605                .expect("Block serialization failed.");
606            let (verified_block, rejected_transactions) = verifier
607                .verify_and_vote(signed_block, serialized_block.clone())
608                .unwrap();
609            assert_eq!(rejected_transactions, Vec::<TransactionIndex>::new());
610            assert_eq!(verified_block.serialized().clone(), serialized_block);
611        }
612
613        // Block with 2 transactions rejected.
614        {
615            let block = base_block
616                .clone()
617                .set_transactions(vec![
618                    Transaction::new(vec![1; 16]),
619                    Transaction::new(vec![2; 8]),
620                    Transaction::new(vec![3; 16]),
621                    Transaction::new(vec![4; 9]),
622                ])
623                .build();
624            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
625            let serialized_block = signed_block
626                .serialize()
627                .expect("Block serialization failed.");
628            let (_verified_block, rejected_transactions) = verifier
629                .verify_and_vote(signed_block, serialized_block)
630                .unwrap();
631            assert_eq!(
632                rejected_transactions,
633                vec![1 as TransactionIndex, 3 as TransactionIndex],
634            );
635        }
636
637        // Block with an invalid transaction returns an error.
638        {
639            let block = base_block
640                .clone()
641                .set_transactions(vec![
642                    Transaction::new(vec![1; 16]),
643                    Transaction::new(vec![2; 8]),
644                    Transaction::new(vec![3; 1]), // Invalid transaction size
645                    Transaction::new(vec![4; 9]),
646                ])
647                .build();
648            let signed_block = SignedBlock::new(block, author_protocol_keypair).unwrap();
649            let serialized_block = signed_block
650                .serialize()
651                .expect("Block serialization failed.");
652            assert!(matches!(
653                verifier.verify_and_vote(signed_block, serialized_block),
654                Err(ConsensusError::InvalidTransaction(_))
655            ));
656        }
657    }
658}