Skip to main content

consensus_core/
block.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::{fmt, hash::Hash, ops::Deref, sync::Arc};
5
6use bytes::Bytes;
7use consensus_config::{
8    AuthorityIndex, DefaultHashFunction, Epoch, ProtocolKeyPair, ProtocolKeySignature,
9    ProtocolPublicKey,
10};
11use consensus_types::block::{BlockDigest, BlockRef, BlockTimestampMs, Round, TransactionIndex};
12use enum_dispatch::enum_dispatch;
13use fastcrypto::hash::HashFunction;
14use itertools::Itertools as _;
15use serde::{Deserialize, Serialize};
16use shared_crypto::intent::{Intent, IntentMessage, IntentScope};
17
18use crate::{
19    commit::CommitVote,
20    context::Context,
21    ensure,
22    error::{ConsensusError, ConsensusResult},
23};
24
25pub(crate) const GENESIS_ROUND: Round = 0;
26
27/// Sui transaction in serialised bytes
28#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Default, Debug)]
29pub struct Transaction {
30    data: Bytes,
31}
32
33impl Transaction {
34    pub fn new(data: Vec<u8>) -> Self {
35        Self { data: data.into() }
36    }
37
38    pub fn data(&self) -> &[u8] {
39        &self.data
40    }
41
42    pub fn into_data(self) -> Bytes {
43        self.data
44    }
45}
46/// Votes on transactions in a specific block.
47/// Reject votes are explicit. The rest of transactions in the block receive implicit accept votes.
48// TODO: look into making fields `pub`.
49#[derive(Clone, Deserialize, Serialize)]
50pub(crate) struct BlockTransactionVotes {
51    pub(crate) block_ref: BlockRef,
52    pub(crate) rejects: Vec<TransactionIndex>,
53}
54
55/// A block includes references to previous round blocks and transactions that the authority
56/// considers valid.
57/// Well behaved authorities produce at most one block per round, but malicious authorities can
58/// equivocate.
59#[allow(private_interfaces)]
60#[derive(Clone, Deserialize, Serialize)]
61#[enum_dispatch(BlockAPI)]
62pub enum Block {
63    V1(BlockV1),
64    V2(BlockV2),
65    V3(BlockV3),
66}
67
68#[allow(private_interfaces)]
69#[enum_dispatch]
70pub trait BlockAPI {
71    fn epoch(&self) -> Epoch;
72    fn round(&self) -> Round;
73    fn author(&self) -> AuthorityIndex;
74    fn slot(&self) -> Slot;
75    fn timestamp_ms(&self) -> BlockTimestampMs;
76    fn ancestors(&self) -> &[BlockRef];
77
78    /// Transactions included in this block.
79    fn transactions(&self) -> &[Transaction];
80    fn transactions_data(&self) -> Vec<&[u8]>;
81
82    /// Votes on if a transaction should be accepted or rejected.
83    fn transaction_votes(&self) -> &[BlockTransactionVotes];
84
85    /// Transactions in this blocks' casual history at and before the cutoff round
86    /// will not receive accept votes from this block.
87    /// Only `BlockV3` carries this — earlier variants panic.
88    fn transaction_votes_cutoff_round(&self) -> Round;
89
90    /// Votes on commits observed by this authority.
91    fn commit_votes(&self) -> &[CommitVote];
92
93    fn misbehavior_reports(&self) -> &[MisbehaviorReport];
94}
95
96#[derive(Clone, Default, Deserialize, Serialize)]
97pub(crate) struct BlockV1 {
98    epoch: Epoch,
99    round: Round,
100    author: AuthorityIndex,
101    timestamp_ms: BlockTimestampMs,
102    ancestors: Vec<BlockRef>,
103    transactions: Vec<Transaction>,
104    commit_votes: Vec<CommitVote>,
105    misbehavior_reports: Vec<MisbehaviorReport>,
106}
107
108impl BlockV1 {
109    pub(crate) fn new(
110        epoch: Epoch,
111        round: Round,
112        author: AuthorityIndex,
113        timestamp_ms: BlockTimestampMs,
114        ancestors: Vec<BlockRef>,
115        transactions: Vec<Transaction>,
116        commit_votes: Vec<CommitVote>,
117        misbehavior_reports: Vec<MisbehaviorReport>,
118    ) -> BlockV1 {
119        Self {
120            epoch,
121            round,
122            author,
123            timestamp_ms,
124            ancestors,
125            transactions,
126            commit_votes,
127            misbehavior_reports,
128        }
129    }
130
131    fn genesis_block(context: &Context, author: AuthorityIndex) -> Self {
132        Self {
133            epoch: context.committee.epoch(),
134            round: GENESIS_ROUND,
135            author,
136            timestamp_ms: context.epoch_start_timestamp_ms,
137            ancestors: vec![],
138            transactions: vec![],
139            commit_votes: vec![],
140            misbehavior_reports: vec![],
141        }
142    }
143}
144
145impl BlockAPI for BlockV1 {
146    fn epoch(&self) -> Epoch {
147        self.epoch
148    }
149
150    fn round(&self) -> Round {
151        self.round
152    }
153
154    fn author(&self) -> AuthorityIndex {
155        self.author
156    }
157
158    fn slot(&self) -> Slot {
159        Slot::new(self.round, self.author)
160    }
161
162    fn timestamp_ms(&self) -> BlockTimestampMs {
163        self.timestamp_ms
164    }
165
166    fn ancestors(&self) -> &[BlockRef] {
167        &self.ancestors
168    }
169
170    fn transactions(&self) -> &[Transaction] {
171        &self.transactions
172    }
173
174    fn transactions_data(&self) -> Vec<&[u8]> {
175        self.transactions.iter().map(|t| t.data()).collect()
176    }
177
178    fn transaction_votes(&self) -> &[BlockTransactionVotes] {
179        &[]
180    }
181
182    fn transaction_votes_cutoff_round(&self) -> Round {
183        panic!("transaction_votes_cutoff_round() is not supported on BlockV1");
184    }
185
186    fn commit_votes(&self) -> &[CommitVote] {
187        &self.commit_votes
188    }
189
190    fn misbehavior_reports(&self) -> &[MisbehaviorReport] {
191        &self.misbehavior_reports
192    }
193}
194
195#[derive(Clone, Default, Deserialize, Serialize)]
196pub(crate) struct BlockV2 {
197    epoch: Epoch,
198    round: Round,
199    author: AuthorityIndex,
200    timestamp_ms: BlockTimestampMs,
201    ancestors: Vec<BlockRef>,
202    transactions: Vec<Transaction>,
203    transaction_votes: Vec<BlockTransactionVotes>,
204    commit_votes: Vec<CommitVote>,
205    misbehavior_reports: Vec<MisbehaviorReport>,
206}
207
208impl BlockV2 {
209    pub(crate) fn new(
210        epoch: Epoch,
211        round: Round,
212        author: AuthorityIndex,
213        timestamp_ms: BlockTimestampMs,
214        ancestors: Vec<BlockRef>,
215        transactions: Vec<Transaction>,
216        transaction_votes: Vec<BlockTransactionVotes>,
217        commit_votes: Vec<CommitVote>,
218        misbehavior_reports: Vec<MisbehaviorReport>,
219    ) -> BlockV2 {
220        Self {
221            epoch,
222            round,
223            author,
224            timestamp_ms,
225            ancestors,
226            transactions,
227            transaction_votes,
228            commit_votes,
229            misbehavior_reports,
230        }
231    }
232
233    fn genesis_block(context: &Context, author: AuthorityIndex) -> Self {
234        Self {
235            epoch: context.committee.epoch(),
236            round: GENESIS_ROUND,
237            author,
238            timestamp_ms: context.epoch_start_timestamp_ms,
239            ancestors: vec![],
240            transactions: vec![],
241            transaction_votes: vec![],
242            commit_votes: vec![],
243            misbehavior_reports: vec![],
244        }
245    }
246}
247
248impl BlockAPI for BlockV2 {
249    fn epoch(&self) -> Epoch {
250        self.epoch
251    }
252
253    fn round(&self) -> Round {
254        self.round
255    }
256
257    fn author(&self) -> AuthorityIndex {
258        self.author
259    }
260
261    fn slot(&self) -> Slot {
262        Slot::new(self.round, self.author)
263    }
264
265    fn timestamp_ms(&self) -> BlockTimestampMs {
266        self.timestamp_ms
267    }
268
269    fn ancestors(&self) -> &[BlockRef] {
270        &self.ancestors
271    }
272
273    fn transactions(&self) -> &[Transaction] {
274        &self.transactions
275    }
276
277    fn transactions_data(&self) -> Vec<&[u8]> {
278        self.transactions.iter().map(|t| t.data()).collect()
279    }
280
281    fn transaction_votes(&self) -> &[BlockTransactionVotes] {
282        &self.transaction_votes
283    }
284
285    fn transaction_votes_cutoff_round(&self) -> Round {
286        panic!("transaction_votes_cutoff_round() is not supported on BlockV2");
287    }
288
289    fn commit_votes(&self) -> &[CommitVote] {
290        &self.commit_votes
291    }
292
293    fn misbehavior_reports(&self) -> &[MisbehaviorReport] {
294        &self.misbehavior_reports
295    }
296}
297
298#[derive(Clone, Default, Deserialize, Serialize)]
299pub(crate) struct BlockV3 {
300    epoch: Epoch,
301    round: Round,
302    author: AuthorityIndex,
303    timestamp_ms: BlockTimestampMs,
304    ancestors: Vec<BlockRef>,
305    transactions: Vec<Transaction>,
306    transaction_votes: Vec<BlockTransactionVotes>,
307    transaction_votes_cutoff_round: Round,
308    commit_votes: Vec<CommitVote>,
309    misbehavior_reports: Vec<MisbehaviorReport>,
310}
311
312#[allow(dead_code)]
313impl BlockV3 {
314    pub(crate) fn new(
315        epoch: Epoch,
316        round: Round,
317        author: AuthorityIndex,
318        timestamp_ms: BlockTimestampMs,
319        ancestors: Vec<BlockRef>,
320        transactions: Vec<Transaction>,
321        transaction_votes: Vec<BlockTransactionVotes>,
322        transaction_votes_cutoff_round: Round,
323        commit_votes: Vec<CommitVote>,
324        misbehavior_reports: Vec<MisbehaviorReport>,
325    ) -> BlockV3 {
326        Self {
327            epoch,
328            round,
329            author,
330            timestamp_ms,
331            ancestors,
332            transactions,
333            transaction_votes,
334            transaction_votes_cutoff_round,
335            commit_votes,
336            misbehavior_reports,
337        }
338    }
339
340    fn genesis_block(context: &Context, author: AuthorityIndex) -> Self {
341        Self {
342            epoch: context.committee.epoch(),
343            round: GENESIS_ROUND,
344            author,
345            timestamp_ms: context.epoch_start_timestamp_ms,
346            ancestors: vec![],
347            transactions: vec![],
348            transaction_votes: vec![],
349            transaction_votes_cutoff_round: GENESIS_ROUND,
350            commit_votes: vec![],
351            misbehavior_reports: vec![],
352        }
353    }
354}
355
356impl BlockAPI for BlockV3 {
357    fn epoch(&self) -> Epoch {
358        self.epoch
359    }
360
361    fn round(&self) -> Round {
362        self.round
363    }
364
365    fn author(&self) -> AuthorityIndex {
366        self.author
367    }
368
369    fn slot(&self) -> Slot {
370        Slot::new(self.round, self.author)
371    }
372
373    fn timestamp_ms(&self) -> BlockTimestampMs {
374        self.timestamp_ms
375    }
376
377    fn ancestors(&self) -> &[BlockRef] {
378        &self.ancestors
379    }
380
381    fn transactions(&self) -> &[Transaction] {
382        &self.transactions
383    }
384
385    fn transactions_data(&self) -> Vec<&[u8]> {
386        self.transactions.iter().map(|t| t.data()).collect()
387    }
388
389    fn transaction_votes(&self) -> &[BlockTransactionVotes] {
390        &self.transaction_votes
391    }
392
393    fn transaction_votes_cutoff_round(&self) -> Round {
394        self.transaction_votes_cutoff_round
395    }
396
397    fn commit_votes(&self) -> &[CommitVote] {
398        &self.commit_votes
399    }
400
401    fn misbehavior_reports(&self) -> &[MisbehaviorReport] {
402        &self.misbehavior_reports
403    }
404}
405
406/// Slot is the position of blocks in the DAG. It can contain 0, 1 or multiple blocks
407/// from the same authority at the same round.
408#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
409pub struct Slot {
410    pub round: Round,
411    pub authority: AuthorityIndex,
412}
413
414impl Slot {
415    pub fn new(round: Round, authority: AuthorityIndex) -> Self {
416        Self { round, authority }
417    }
418
419    pub fn new_for_test(round: Round, authority: u32) -> Self {
420        Self {
421            round,
422            authority: AuthorityIndex::new_for_test(authority),
423        }
424    }
425}
426
427impl From<BlockRef> for Slot {
428    fn from(value: BlockRef) -> Self {
429        Slot::new(value.round, value.author)
430    }
431}
432
433// TODO: re-evaluate formats for production debugging.
434impl fmt::Display for Slot {
435    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
436        write!(f, "{}{}", self.authority, self.round)
437    }
438}
439
440impl fmt::Debug for Slot {
441    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
442        write!(f, "{}", self)
443    }
444}
445
446/// A Block with its signature, before they are verified.
447///
448/// Note: `BlockDigest` is computed over this struct, so any added field (without `#[serde(skip)]`)
449/// will affect the values of `BlockDigest` and `BlockRef`.
450#[derive(Deserialize, Serialize)]
451pub(crate) struct SignedBlock {
452    inner: Block,
453    signature: Bytes,
454}
455
456impl SignedBlock {
457    /// Should only be used when constructing the genesis blocks
458    pub(crate) fn new_genesis(block: Block) -> Self {
459        Self {
460            inner: block,
461            signature: Bytes::default(),
462        }
463    }
464
465    pub(crate) fn new(block: Block, protocol_keypair: &ProtocolKeyPair) -> ConsensusResult<Self> {
466        let signature = compute_block_signature(&block, protocol_keypair)?;
467        Ok(Self {
468            inner: block,
469            signature: Bytes::copy_from_slice(signature.to_bytes()),
470        })
471    }
472
473    pub(crate) fn signature(&self) -> &Bytes {
474        &self.signature
475    }
476
477    /// This method only verifies this block's signature. Verification of the full block
478    /// should be done via BlockVerifier.
479    pub(crate) fn verify_signature(&self, context: &Context) -> ConsensusResult<()> {
480        let block = &self.inner;
481        let committee = &context.committee;
482        ensure!(
483            committee.is_valid_index(block.author()),
484            ConsensusError::InvalidAuthorityIndex {
485                index: block.author(),
486                max: committee.size() - 1
487            }
488        );
489        let authority = committee.authority(block.author());
490        verify_block_signature(block, self.signature(), &authority.protocol_key)
491    }
492
493    /// Serialises the block using the bcs serializer
494    pub(crate) fn serialize(&self) -> Result<Bytes, bcs::Error> {
495        let bytes = bcs::to_bytes(self)?;
496        Ok(bytes.into())
497    }
498
499    /// Clears signature for testing.
500    #[cfg(test)]
501    pub(crate) fn clear_signature(&mut self) {
502        self.signature = Bytes::default();
503    }
504}
505
506/// Digest of a block, covering all `Block` fields without its signature.
507/// This is used during Block signing and signature verification.
508/// This should never be used outside of this file, to avoid confusion with `BlockDigest`.
509#[derive(Serialize, Deserialize)]
510struct InnerBlockDigest([u8; consensus_config::DIGEST_LENGTH]);
511
512/// Computes the digest of a Block, only for signing and verifications.
513fn compute_inner_block_digest(block: &Block) -> ConsensusResult<InnerBlockDigest> {
514    let mut hasher = DefaultHashFunction::new();
515    bcs::serialize_into(&mut hasher, block).map_err(ConsensusError::SerializationFailure)?;
516    Ok(InnerBlockDigest(hasher.finalize().into()))
517}
518
519/// Wrap a InnerBlockDigest in the intent message.
520fn to_consensus_block_intent(digest: InnerBlockDigest) -> IntentMessage<InnerBlockDigest> {
521    IntentMessage::new(Intent::consensus_app(IntentScope::ConsensusBlock), digest)
522}
523
524/// Process for signing a block & verifying a block signature:
525/// 1. Compute the digest of `Block`.
526/// 2. Wrap the digest in `IntentMessage`.
527/// 3. Sign the serialized `IntentMessage`, or verify signature against it.
528fn compute_block_signature(
529    block: &Block,
530    protocol_keypair: &ProtocolKeyPair,
531) -> ConsensusResult<ProtocolKeySignature> {
532    let digest = compute_inner_block_digest(block)?;
533    let message = bcs::to_bytes(&to_consensus_block_intent(digest))
534        .map_err(ConsensusError::SerializationFailure)?;
535    Ok(protocol_keypair.sign(&message))
536}
537
538fn verify_block_signature(
539    block: &Block,
540    signature: &[u8],
541    protocol_pubkey: &ProtocolPublicKey,
542) -> ConsensusResult<()> {
543    let digest = compute_inner_block_digest(block)?;
544    let message = bcs::to_bytes(&to_consensus_block_intent(digest))
545        .map_err(ConsensusError::SerializationFailure)?;
546    let sig =
547        ProtocolKeySignature::from_bytes(signature).map_err(ConsensusError::MalformedSignature)?;
548    protocol_pubkey
549        .verify(&message, &sig)
550        .map_err(ConsensusError::SignatureVerificationFailure)
551}
552
553/// Allow quick access on the underlying Block without having to always refer to the inner block ref.
554impl Deref for SignedBlock {
555    type Target = Block;
556
557    fn deref(&self) -> &Self::Target {
558        &self.inner
559    }
560}
561
562/// VerifiedBlock allows full access to its content.
563/// Note: clone() is relatively cheap with most underlying data refcounted.
564#[derive(Clone)]
565pub struct VerifiedBlock {
566    block: Arc<SignedBlock>,
567
568    // Cached Block digest and serialized SignedBlock, to avoid re-computing these values.
569    digest: BlockDigest,
570    serialized: Bytes,
571}
572
573impl VerifiedBlock {
574    /// Creates VerifiedBlock from a verified SignedBlock and its serialized bytes.
575    pub(crate) fn new_verified(signed_block: SignedBlock, serialized: Bytes) -> Self {
576        let digest = Self::compute_digest(&serialized);
577        VerifiedBlock {
578            block: Arc::new(signed_block),
579            digest,
580            serialized,
581        }
582    }
583
584    /// This method is public for testing in other crates.
585    pub fn new_for_test(block: Block) -> Self {
586        // Use empty signature in test.
587        let signed_block = SignedBlock {
588            inner: block,
589            signature: Default::default(),
590        };
591        let serialized: Bytes = bcs::to_bytes(&signed_block)
592            .expect("Serialization should not fail")
593            .into();
594        let digest = Self::compute_digest(&serialized);
595        VerifiedBlock {
596            block: Arc::new(signed_block),
597            digest,
598            serialized,
599        }
600    }
601
602    /// Returns reference to the block.
603    pub fn reference(&self) -> BlockRef {
604        BlockRef {
605            round: self.round(),
606            author: self.author(),
607            digest: self.digest(),
608        }
609    }
610
611    pub(crate) fn digest(&self) -> BlockDigest {
612        self.digest
613    }
614
615    /// Returns the serialized block with signature.
616    pub(crate) fn serialized(&self) -> &Bytes {
617        &self.serialized
618    }
619
620    /// Computes digest from the serialized block with signature.
621    pub(crate) fn compute_digest(serialized: &[u8]) -> BlockDigest {
622        let mut hasher = DefaultHashFunction::new();
623        hasher.update(serialized);
624        BlockDigest(hasher.finalize().into())
625    }
626}
627
628/// Allow quick access on the underlying Block without having to always refer to the inner block ref.
629impl Deref for VerifiedBlock {
630    type Target = Block;
631
632    fn deref(&self) -> &Self::Target {
633        &self.block.inner
634    }
635}
636
637impl PartialEq for VerifiedBlock {
638    fn eq(&self, other: &Self) -> bool {
639        self.digest() == other.digest()
640    }
641}
642
643impl fmt::Display for VerifiedBlock {
644    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
645        write!(f, "{}", self.reference())
646    }
647}
648
649// TODO: re-evaluate formats for production debugging.
650impl fmt::Debug for VerifiedBlock {
651    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
652        write!(
653            f,
654            "{:?}([{}];{}ms;{}t;{}c)",
655            self.reference(),
656            self.ancestors().iter().map(|a| a.to_string()).join(", "),
657            self.timestamp_ms(),
658            self.transactions().len(),
659            self.commit_votes().len(),
660        )
661    }
662}
663
664/// Block with extended additional information, such as
665/// local blocks that are excluded from the block's ancestors.
666/// The extended information do not need to be certified or forwarded to other authorities.
667#[derive(Clone, Debug)]
668pub(crate) struct ExtendedBlock {
669    pub block: VerifiedBlock,
670    pub excluded_ancestors: Vec<BlockRef>,
671}
672
673/// Generates the genesis blocks for the current Committee.
674/// The blocks are returned in authority index order.
675pub(crate) fn genesis_blocks(context: &Context) -> Vec<VerifiedBlock> {
676    context
677        .committee
678        .authorities()
679        .map(|(authority_index, _)| {
680            let block = if context.protocol_config.transaction_voting_enabled() {
681                Block::V2(BlockV2::genesis_block(context, authority_index))
682            } else {
683                Block::V1(BlockV1::genesis_block(context, authority_index))
684            };
685            let signed_block = SignedBlock::new_genesis(block);
686            let serialized = signed_block
687                .serialize()
688                .expect("Genesis block serialization failed.");
689            // Unnecessary to verify genesis blocks.
690            VerifiedBlock::new_verified(signed_block, serialized)
691        })
692        .collect::<Vec<VerifiedBlock>>()
693}
694
695/// Creates fake blocks for testing.
696/// This struct is public for testing in other crates.
697#[derive(Clone)]
698pub struct TestBlock {
699    block: BlockV2,
700}
701
702impl TestBlock {
703    pub fn new(round: Round, author: u32) -> Self {
704        Self {
705            block: BlockV2 {
706                round,
707                author: AuthorityIndex::new_for_test(author),
708                ..Default::default()
709            },
710        }
711    }
712
713    pub fn set_epoch(mut self, epoch: Epoch) -> Self {
714        self.block.epoch = epoch;
715        self
716    }
717
718    pub fn set_round(mut self, round: Round) -> Self {
719        self.block.round = round;
720        self
721    }
722
723    pub fn set_author(mut self, author: AuthorityIndex) -> Self {
724        self.block.author = author;
725        self
726    }
727
728    pub fn set_timestamp_ms(mut self, timestamp_ms: BlockTimestampMs) -> Self {
729        self.block.timestamp_ms = timestamp_ms;
730        self
731    }
732
733    /// Sorts then sets ancestors in the TestBlock.
734    /// Author's own block is always first, which is expected by BlockVerifier and
735    /// the rest of the system.
736    pub fn set_ancestors(mut self, mut ancestors: Vec<BlockRef>) -> Self {
737        ancestors.sort_by(|a, b| {
738            if a.author == self.block.author {
739                return std::cmp::Ordering::Less;
740            }
741            if b.author == self.block.author {
742                return std::cmp::Ordering::Greater;
743            }
744            a.author.cmp(&b.author)
745        });
746        self.block.ancestors = ancestors;
747        self
748    }
749
750    /// Sets ancestors in the TestBlock exactly as provided.
751    pub fn set_ancestors_raw(mut self, ancestors: Vec<BlockRef>) -> Self {
752        self.block.ancestors = ancestors;
753        self
754    }
755
756    pub fn set_transactions(mut self, transactions: Vec<Transaction>) -> Self {
757        self.block.transactions = transactions;
758        self
759    }
760
761    pub(crate) fn set_transaction_votes(mut self, votes: Vec<BlockTransactionVotes>) -> Self {
762        self.block.transaction_votes = votes;
763        self
764    }
765
766    #[cfg(test)]
767    pub(crate) fn set_commit_votes(mut self, commit_votes: Vec<CommitVote>) -> Self {
768        self.block.commit_votes = commit_votes;
769        self
770    }
771
772    pub fn build(self) -> Block {
773        Block::V2(self.block)
774    }
775}
776
777/// A block can attach reports of misbehavior by other authorities.
778#[derive(Clone, Serialize, Deserialize, Debug)]
779pub struct MisbehaviorReport {
780    pub target: AuthorityIndex,
781    pub proof: MisbehaviorProof,
782}
783
784/// Proof of misbehavior are usually signed block(s) from the misbehaving authority.
785#[derive(Clone, Serialize, Deserialize, Debug)]
786pub enum MisbehaviorProof {
787    InvalidBlock(BlockRef),
788}
789
790// TODO: add basic verification for BlockRef and BlockDigest.
791// TODO: add tests for SignedBlock and VerifiedBlock conversion.
792
793#[cfg(test)]
794mod tests {
795    use std::sync::Arc;
796
797    use fastcrypto::error::FastCryptoError;
798
799    use crate::{
800        block::{BlockAPI, SignedBlock, TestBlock, genesis_blocks},
801        context::Context,
802        error::ConsensusError,
803    };
804
805    #[tokio::test]
806    async fn test_sign_and_verify() {
807        let (context, key_pairs) = Context::new_for_test(4);
808        let context = Arc::new(context);
809
810        // Create a block that authority 2 has created
811        let block = TestBlock::new(10, 2).build();
812
813        // Create a signed block with authority's 2 private key
814        let author_two_key = &key_pairs[2].1;
815        let signed_block = SignedBlock::new(block, author_two_key).expect("Shouldn't fail signing");
816
817        // Now verify the block's signature
818        let result = signed_block.verify_signature(&context);
819        assert!(result.is_ok());
820
821        // Try to sign authority's 2 block with authority's 1 key
822        let block = TestBlock::new(10, 2).build();
823        let author_one_key = &key_pairs[1].1;
824        let signed_block = SignedBlock::new(block, author_one_key).expect("Shouldn't fail signing");
825
826        // Now verify the block, it should fail
827        let result = signed_block.verify_signature(&context);
828        match result.err().unwrap() {
829            ConsensusError::SignatureVerificationFailure(err) => {
830                assert_eq!(err, FastCryptoError::InvalidSignature);
831            }
832            err => panic!("Unexpected error: {err:?}"),
833        }
834    }
835
836    #[tokio::test]
837    async fn test_genesis_blocks() {
838        let (context, _) = Context::new_for_test(4);
839        const TIMESTAMP_MS: u64 = 1000;
840        let context = Arc::new(context.with_epoch_start_timestamp_ms(TIMESTAMP_MS));
841        let blocks = genesis_blocks(&context);
842        for (i, block) in blocks.into_iter().enumerate() {
843            assert_eq!(block.author().value(), i);
844            assert_eq!(block.round(), 0);
845            assert_eq!(block.timestamp_ms(), TIMESTAMP_MS);
846        }
847    }
848}