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