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}
66
67#[allow(private_interfaces)]
68#[enum_dispatch]
69pub trait BlockAPI {
70    fn epoch(&self) -> Epoch;
71    fn round(&self) -> Round;
72    fn author(&self) -> AuthorityIndex;
73    fn slot(&self) -> Slot;
74    fn timestamp_ms(&self) -> BlockTimestampMs;
75    fn ancestors(&self) -> &[BlockRef];
76    fn transactions(&self) -> &[Transaction];
77    fn transactions_data(&self) -> Vec<&[u8]>;
78    fn commit_votes(&self) -> &[CommitVote];
79    fn transaction_votes(&self) -> &[BlockTransactionVotes];
80    fn misbehavior_reports(&self) -> &[MisbehaviorReport];
81}
82
83#[derive(Clone, Default, Deserialize, Serialize)]
84pub(crate) struct BlockV1 {
85    epoch: Epoch,
86    round: Round,
87    author: AuthorityIndex,
88    timestamp_ms: BlockTimestampMs,
89    ancestors: Vec<BlockRef>,
90    transactions: Vec<Transaction>,
91    commit_votes: Vec<CommitVote>,
92    misbehavior_reports: Vec<MisbehaviorReport>,
93}
94
95impl BlockV1 {
96    pub(crate) fn new(
97        epoch: Epoch,
98        round: Round,
99        author: AuthorityIndex,
100        timestamp_ms: BlockTimestampMs,
101        ancestors: Vec<BlockRef>,
102        transactions: Vec<Transaction>,
103        commit_votes: Vec<CommitVote>,
104        misbehavior_reports: Vec<MisbehaviorReport>,
105    ) -> BlockV1 {
106        Self {
107            epoch,
108            round,
109            author,
110            timestamp_ms,
111            ancestors,
112            transactions,
113            commit_votes,
114            misbehavior_reports,
115        }
116    }
117
118    fn genesis_block(context: &Context, author: AuthorityIndex) -> Self {
119        Self {
120            epoch: context.committee.epoch(),
121            round: GENESIS_ROUND,
122            author,
123            timestamp_ms: context.epoch_start_timestamp_ms,
124            ancestors: vec![],
125            transactions: vec![],
126            commit_votes: vec![],
127            misbehavior_reports: vec![],
128        }
129    }
130}
131
132impl BlockAPI for BlockV1 {
133    fn epoch(&self) -> Epoch {
134        self.epoch
135    }
136
137    fn round(&self) -> Round {
138        self.round
139    }
140
141    fn author(&self) -> AuthorityIndex {
142        self.author
143    }
144
145    fn slot(&self) -> Slot {
146        Slot::new(self.round, self.author)
147    }
148
149    fn timestamp_ms(&self) -> BlockTimestampMs {
150        self.timestamp_ms
151    }
152
153    fn ancestors(&self) -> &[BlockRef] {
154        &self.ancestors
155    }
156
157    fn transactions(&self) -> &[Transaction] {
158        &self.transactions
159    }
160
161    fn transactions_data(&self) -> Vec<&[u8]> {
162        self.transactions.iter().map(|t| t.data()).collect()
163    }
164
165    fn commit_votes(&self) -> &[CommitVote] {
166        &self.commit_votes
167    }
168
169    fn transaction_votes(&self) -> &[BlockTransactionVotes] {
170        &[]
171    }
172
173    fn misbehavior_reports(&self) -> &[MisbehaviorReport] {
174        &self.misbehavior_reports
175    }
176}
177
178#[derive(Clone, Default, Deserialize, Serialize)]
179pub(crate) struct BlockV2 {
180    epoch: Epoch,
181    round: Round,
182    author: AuthorityIndex,
183    timestamp_ms: BlockTimestampMs,
184    ancestors: Vec<BlockRef>,
185    transactions: Vec<Transaction>,
186    transaction_votes: Vec<BlockTransactionVotes>,
187    commit_votes: Vec<CommitVote>,
188    misbehavior_reports: Vec<MisbehaviorReport>,
189}
190
191#[allow(unused)]
192impl BlockV2 {
193    pub(crate) fn new(
194        epoch: Epoch,
195        round: Round,
196        author: AuthorityIndex,
197        timestamp_ms: BlockTimestampMs,
198        ancestors: Vec<BlockRef>,
199        transactions: Vec<Transaction>,
200        commit_votes: Vec<CommitVote>,
201        transaction_votes: Vec<BlockTransactionVotes>,
202        misbehavior_reports: Vec<MisbehaviorReport>,
203    ) -> BlockV2 {
204        Self {
205            epoch,
206            round,
207            author,
208            timestamp_ms,
209            ancestors,
210            transactions,
211            commit_votes,
212            transaction_votes,
213            misbehavior_reports,
214        }
215    }
216
217    fn genesis_block(context: &Context, author: AuthorityIndex) -> Self {
218        Self {
219            epoch: context.committee.epoch(),
220            round: GENESIS_ROUND,
221            author,
222            timestamp_ms: context.epoch_start_timestamp_ms,
223            ancestors: vec![],
224            transactions: vec![],
225            commit_votes: vec![],
226            transaction_votes: vec![],
227            misbehavior_reports: vec![],
228        }
229    }
230}
231
232impl BlockAPI for BlockV2 {
233    fn epoch(&self) -> Epoch {
234        self.epoch
235    }
236
237    fn round(&self) -> Round {
238        self.round
239    }
240
241    fn author(&self) -> AuthorityIndex {
242        self.author
243    }
244
245    fn slot(&self) -> Slot {
246        Slot::new(self.round, self.author)
247    }
248
249    fn timestamp_ms(&self) -> BlockTimestampMs {
250        self.timestamp_ms
251    }
252
253    fn ancestors(&self) -> &[BlockRef] {
254        &self.ancestors
255    }
256
257    fn transactions(&self) -> &[Transaction] {
258        &self.transactions
259    }
260
261    fn transactions_data(&self) -> Vec<&[u8]> {
262        self.transactions.iter().map(|t| t.data()).collect()
263    }
264
265    fn transaction_votes(&self) -> &[BlockTransactionVotes] {
266        &self.transaction_votes
267    }
268
269    fn commit_votes(&self) -> &[CommitVote] {
270        &self.commit_votes
271    }
272
273    fn misbehavior_reports(&self) -> &[MisbehaviorReport] {
274        &self.misbehavior_reports
275    }
276}
277
278/// Slot is the position of blocks in the DAG. It can contain 0, 1 or multiple blocks
279/// from the same authority at the same round.
280#[derive(Clone, Copy, PartialEq, PartialOrd, Default, Hash)]
281pub struct Slot {
282    pub round: Round,
283    pub authority: AuthorityIndex,
284}
285
286impl Slot {
287    pub fn new(round: Round, authority: AuthorityIndex) -> Self {
288        Self { round, authority }
289    }
290
291    #[cfg(test)]
292    pub fn new_for_test(round: Round, authority: u32) -> Self {
293        Self {
294            round,
295            authority: AuthorityIndex::new_for_test(authority),
296        }
297    }
298}
299
300impl From<BlockRef> for Slot {
301    fn from(value: BlockRef) -> Self {
302        Slot::new(value.round, value.author)
303    }
304}
305
306// TODO: re-evaluate formats for production debugging.
307impl fmt::Display for Slot {
308    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
309        write!(f, "{}{}", self.authority, self.round)
310    }
311}
312
313impl fmt::Debug for Slot {
314    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
315        write!(f, "{}", self)
316    }
317}
318
319/// A Block with its signature, before they are verified.
320///
321/// Note: `BlockDigest` is computed over this struct, so any added field (without `#[serde(skip)]`)
322/// will affect the values of `BlockDigest` and `BlockRef`.
323#[derive(Deserialize, Serialize)]
324pub(crate) struct SignedBlock {
325    inner: Block,
326    signature: Bytes,
327}
328
329impl SignedBlock {
330    /// Should only be used when constructing the genesis blocks
331    pub(crate) fn new_genesis(block: Block) -> Self {
332        Self {
333            inner: block,
334            signature: Bytes::default(),
335        }
336    }
337
338    pub(crate) fn new(block: Block, protocol_keypair: &ProtocolKeyPair) -> ConsensusResult<Self> {
339        let signature = compute_block_signature(&block, protocol_keypair)?;
340        Ok(Self {
341            inner: block,
342            signature: Bytes::copy_from_slice(signature.to_bytes()),
343        })
344    }
345
346    pub(crate) fn signature(&self) -> &Bytes {
347        &self.signature
348    }
349
350    /// This method only verifies this block's signature. Verification of the full block
351    /// should be done via BlockVerifier.
352    pub(crate) fn verify_signature(&self, context: &Context) -> ConsensusResult<()> {
353        let block = &self.inner;
354        let committee = &context.committee;
355        ensure!(
356            committee.is_valid_index(block.author()),
357            ConsensusError::InvalidAuthorityIndex {
358                index: block.author(),
359                max: committee.size() - 1
360            }
361        );
362        let authority = committee.authority(block.author());
363        verify_block_signature(block, self.signature(), &authority.protocol_key)
364    }
365
366    /// Serialises the block using the bcs serializer
367    pub(crate) fn serialize(&self) -> Result<Bytes, bcs::Error> {
368        let bytes = bcs::to_bytes(self)?;
369        Ok(bytes.into())
370    }
371
372    /// Clears signature for testing.
373    #[cfg(test)]
374    pub(crate) fn clear_signature(&mut self) {
375        self.signature = Bytes::default();
376    }
377}
378
379/// Digest of a block, covering all `Block` fields without its signature.
380/// This is used during Block signing and signature verification.
381/// This should never be used outside of this file, to avoid confusion with `BlockDigest`.
382#[derive(Serialize, Deserialize)]
383struct InnerBlockDigest([u8; consensus_config::DIGEST_LENGTH]);
384
385/// Computes the digest of a Block, only for signing and verifications.
386fn compute_inner_block_digest(block: &Block) -> ConsensusResult<InnerBlockDigest> {
387    let mut hasher = DefaultHashFunction::new();
388    hasher.update(bcs::to_bytes(block).map_err(ConsensusError::SerializationFailure)?);
389    Ok(InnerBlockDigest(hasher.finalize().into()))
390}
391
392/// Wrap a InnerBlockDigest in the intent message.
393fn to_consensus_block_intent(digest: InnerBlockDigest) -> IntentMessage<InnerBlockDigest> {
394    IntentMessage::new(Intent::consensus_app(IntentScope::ConsensusBlock), digest)
395}
396
397/// Process for signing a block & verifying a block signature:
398/// 1. Compute the digest of `Block`.
399/// 2. Wrap the digest in `IntentMessage`.
400/// 3. Sign the serialized `IntentMessage`, or verify signature against it.
401fn compute_block_signature(
402    block: &Block,
403    protocol_keypair: &ProtocolKeyPair,
404) -> ConsensusResult<ProtocolKeySignature> {
405    let digest = compute_inner_block_digest(block)?;
406    let message = bcs::to_bytes(&to_consensus_block_intent(digest))
407        .map_err(ConsensusError::SerializationFailure)?;
408    Ok(protocol_keypair.sign(&message))
409}
410
411fn verify_block_signature(
412    block: &Block,
413    signature: &[u8],
414    protocol_pubkey: &ProtocolPublicKey,
415) -> ConsensusResult<()> {
416    let digest = compute_inner_block_digest(block)?;
417    let message = bcs::to_bytes(&to_consensus_block_intent(digest))
418        .map_err(ConsensusError::SerializationFailure)?;
419    let sig =
420        ProtocolKeySignature::from_bytes(signature).map_err(ConsensusError::MalformedSignature)?;
421    protocol_pubkey
422        .verify(&message, &sig)
423        .map_err(ConsensusError::SignatureVerificationFailure)
424}
425
426/// Allow quick access on the underlying Block without having to always refer to the inner block ref.
427impl Deref for SignedBlock {
428    type Target = Block;
429
430    fn deref(&self) -> &Self::Target {
431        &self.inner
432    }
433}
434
435/// VerifiedBlock allows full access to its content.
436/// Note: clone() is relatively cheap with most underlying data refcounted.
437#[derive(Clone)]
438pub struct VerifiedBlock {
439    block: Arc<SignedBlock>,
440
441    // Cached Block digest and serialized SignedBlock, to avoid re-computing these values.
442    digest: BlockDigest,
443    serialized: Bytes,
444}
445
446impl VerifiedBlock {
447    /// Creates VerifiedBlock from a verified SignedBlock and its serialized bytes.
448    pub(crate) fn new_verified(signed_block: SignedBlock, serialized: Bytes) -> Self {
449        let digest = Self::compute_digest(&serialized);
450        VerifiedBlock {
451            block: Arc::new(signed_block),
452            digest,
453            serialized,
454        }
455    }
456
457    /// This method is public for testing in other crates.
458    pub fn new_for_test(block: Block) -> Self {
459        // Use empty signature in test.
460        let signed_block = SignedBlock {
461            inner: block,
462            signature: Default::default(),
463        };
464        let serialized: Bytes = bcs::to_bytes(&signed_block)
465            .expect("Serialization should not fail")
466            .into();
467        let digest = Self::compute_digest(&serialized);
468        VerifiedBlock {
469            block: Arc::new(signed_block),
470            digest,
471            serialized,
472        }
473    }
474
475    /// Returns reference to the block.
476    pub fn reference(&self) -> BlockRef {
477        BlockRef {
478            round: self.round(),
479            author: self.author(),
480            digest: self.digest(),
481        }
482    }
483
484    pub(crate) fn digest(&self) -> BlockDigest {
485        self.digest
486    }
487
488    /// Returns the serialized block with signature.
489    pub(crate) fn serialized(&self) -> &Bytes {
490        &self.serialized
491    }
492
493    /// Computes digest from the serialized block with signature.
494    pub(crate) fn compute_digest(serialized: &[u8]) -> BlockDigest {
495        let mut hasher = DefaultHashFunction::new();
496        hasher.update(serialized);
497        BlockDigest(hasher.finalize().into())
498    }
499}
500
501/// Allow quick access on the underlying Block without having to always refer to the inner block ref.
502impl Deref for VerifiedBlock {
503    type Target = Block;
504
505    fn deref(&self) -> &Self::Target {
506        &self.block.inner
507    }
508}
509
510impl PartialEq for VerifiedBlock {
511    fn eq(&self, other: &Self) -> bool {
512        self.digest() == other.digest()
513    }
514}
515
516impl fmt::Display for VerifiedBlock {
517    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
518        write!(f, "{}", self.reference())
519    }
520}
521
522// TODO: re-evaluate formats for production debugging.
523impl fmt::Debug for VerifiedBlock {
524    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
525        write!(
526            f,
527            "{:?}([{}];{}ms;{}t;{}c)",
528            self.reference(),
529            self.ancestors().iter().map(|a| a.to_string()).join(", "),
530            self.timestamp_ms(),
531            self.transactions().len(),
532            self.commit_votes().len(),
533        )
534    }
535}
536
537/// Block with extended additional information, such as
538/// local blocks that are excluded from the block's ancestors.
539/// The extended information do not need to be certified or forwarded to other authorities.
540#[derive(Clone, Debug)]
541pub(crate) struct ExtendedBlock {
542    pub block: VerifiedBlock,
543    pub excluded_ancestors: Vec<BlockRef>,
544}
545
546/// Generates the genesis blocks for the current Committee.
547/// The blocks are returned in authority index order.
548pub(crate) fn genesis_blocks(context: &Context) -> Vec<VerifiedBlock> {
549    context
550        .committee
551        .authorities()
552        .map(|(authority_index, _)| {
553            let block = if context.protocol_config.mysticeti_fastpath() {
554                Block::V2(BlockV2::genesis_block(context, authority_index))
555            } else {
556                Block::V1(BlockV1::genesis_block(context, authority_index))
557            };
558            let signed_block = SignedBlock::new_genesis(block);
559            let serialized = signed_block
560                .serialize()
561                .expect("Genesis block serialization failed.");
562            // Unnecessary to verify genesis blocks.
563            VerifiedBlock::new_verified(signed_block, serialized)
564        })
565        .collect::<Vec<VerifiedBlock>>()
566}
567
568/// A block certified by consensus for fast path execution.
569#[derive(Clone)]
570pub struct CertifiedBlock {
571    /// All transactions in the block have a quorum of accept or reject votes.
572    pub block: VerifiedBlock,
573    /// Sorted transaction indices that indicate the transactions rejected by a quorum.
574    pub rejected: Vec<TransactionIndex>,
575}
576
577impl CertifiedBlock {
578    pub fn new(block: VerifiedBlock, rejected: Vec<TransactionIndex>) -> Self {
579        Self { block, rejected }
580    }
581}
582
583/// A batch of certified blocks output by consensus for processing.
584pub struct CertifiedBlocksOutput {
585    pub blocks: Vec<CertifiedBlock>,
586}
587
588/// Creates fake blocks for testing.
589/// This struct is public for testing in other crates.
590#[derive(Clone)]
591pub struct TestBlock {
592    block: BlockV2,
593}
594
595impl TestBlock {
596    pub fn new(round: Round, author: u32) -> Self {
597        Self {
598            block: BlockV2 {
599                round,
600                author: AuthorityIndex::new_for_test(author),
601                ..Default::default()
602            },
603        }
604    }
605
606    pub fn set_epoch(mut self, epoch: Epoch) -> Self {
607        self.block.epoch = epoch;
608        self
609    }
610
611    pub fn set_round(mut self, round: Round) -> Self {
612        self.block.round = round;
613        self
614    }
615
616    pub fn set_author(mut self, author: AuthorityIndex) -> Self {
617        self.block.author = author;
618        self
619    }
620
621    pub fn set_timestamp_ms(mut self, timestamp_ms: BlockTimestampMs) -> Self {
622        self.block.timestamp_ms = timestamp_ms;
623        self
624    }
625
626    pub fn set_ancestors(mut self, ancestors: Vec<BlockRef>) -> Self {
627        self.block.ancestors = ancestors;
628        self
629    }
630
631    pub fn set_transactions(mut self, transactions: Vec<Transaction>) -> Self {
632        self.block.transactions = transactions;
633        self
634    }
635
636    pub(crate) fn set_transaction_votes(mut self, votes: Vec<BlockTransactionVotes>) -> Self {
637        self.block.transaction_votes = votes;
638        self
639    }
640
641    #[cfg(test)]
642    pub(crate) fn set_commit_votes(mut self, commit_votes: Vec<CommitVote>) -> Self {
643        self.block.commit_votes = commit_votes;
644        self
645    }
646
647    pub fn build(self) -> Block {
648        Block::V2(self.block)
649    }
650}
651
652/// A block can attach reports of misbehavior by other authorities.
653#[derive(Clone, Serialize, Deserialize, Debug)]
654pub struct MisbehaviorReport {
655    pub target: AuthorityIndex,
656    pub proof: MisbehaviorProof,
657}
658
659/// Proof of misbehavior are usually signed block(s) from the misbehaving authority.
660#[derive(Clone, Serialize, Deserialize, Debug)]
661pub enum MisbehaviorProof {
662    InvalidBlock(BlockRef),
663}
664
665// TODO: add basic verification for BlockRef and BlockDigest.
666// TODO: add tests for SignedBlock and VerifiedBlock conversion.
667
668#[cfg(test)]
669mod tests {
670    use std::sync::Arc;
671
672    use fastcrypto::error::FastCryptoError;
673
674    use crate::{
675        block::{BlockAPI, SignedBlock, TestBlock, genesis_blocks},
676        context::Context,
677        error::ConsensusError,
678    };
679
680    #[tokio::test]
681    async fn test_sign_and_verify() {
682        let (context, key_pairs) = Context::new_for_test(4);
683        let context = Arc::new(context);
684
685        // Create a block that authority 2 has created
686        let block = TestBlock::new(10, 2).build();
687
688        // Create a signed block with authority's 2 private key
689        let author_two_key = &key_pairs[2].1;
690        let signed_block = SignedBlock::new(block, author_two_key).expect("Shouldn't fail signing");
691
692        // Now verify the block's signature
693        let result = signed_block.verify_signature(&context);
694        assert!(result.is_ok());
695
696        // Try to sign authority's 2 block with authority's 1 key
697        let block = TestBlock::new(10, 2).build();
698        let author_one_key = &key_pairs[1].1;
699        let signed_block = SignedBlock::new(block, author_one_key).expect("Shouldn't fail signing");
700
701        // Now verify the block, it should fail
702        let result = signed_block.verify_signature(&context);
703        match result.err().unwrap() {
704            ConsensusError::SignatureVerificationFailure(err) => {
705                assert_eq!(err, FastCryptoError::InvalidSignature);
706            }
707            err => panic!("Unexpected error: {err:?}"),
708        }
709    }
710
711    #[tokio::test]
712    async fn test_genesis_blocks() {
713        let (context, _) = Context::new_for_test(4);
714        const TIMESTAMP_MS: u64 = 1000;
715        let context = Arc::new(context.with_epoch_start_timestamp_ms(TIMESTAMP_MS));
716        let blocks = genesis_blocks(&context);
717        for (i, block) in blocks.into_iter().enumerate() {
718            assert_eq!(block.author().value(), i);
719            assert_eq!(block.round(), 0);
720            assert_eq!(block.timestamp_ms(), TIMESTAMP_MS);
721        }
722    }
723}