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    pub fn new_for_test(round: Round, authority: u32) -> Self {
292        Self {
293            round,
294            authority: AuthorityIndex::new_for_test(authority),
295        }
296    }
297}
298
299impl From<BlockRef> for Slot {
300    fn from(value: BlockRef) -> Self {
301        Slot::new(value.round, value.author)
302    }
303}
304
305// TODO: re-evaluate formats for production debugging.
306impl fmt::Display for Slot {
307    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
308        write!(f, "{}{}", self.authority, self.round)
309    }
310}
311
312impl fmt::Debug for Slot {
313    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314        write!(f, "{}", self)
315    }
316}
317
318/// A Block with its signature, before they are verified.
319///
320/// Note: `BlockDigest` is computed over this struct, so any added field (without `#[serde(skip)]`)
321/// will affect the values of `BlockDigest` and `BlockRef`.
322#[derive(Deserialize, Serialize)]
323pub(crate) struct SignedBlock {
324    inner: Block,
325    signature: Bytes,
326}
327
328impl SignedBlock {
329    /// Should only be used when constructing the genesis blocks
330    pub(crate) fn new_genesis(block: Block) -> Self {
331        Self {
332            inner: block,
333            signature: Bytes::default(),
334        }
335    }
336
337    pub(crate) fn new(block: Block, protocol_keypair: &ProtocolKeyPair) -> ConsensusResult<Self> {
338        let signature = compute_block_signature(&block, protocol_keypair)?;
339        Ok(Self {
340            inner: block,
341            signature: Bytes::copy_from_slice(signature.to_bytes()),
342        })
343    }
344
345    pub(crate) fn signature(&self) -> &Bytes {
346        &self.signature
347    }
348
349    /// This method only verifies this block's signature. Verification of the full block
350    /// should be done via BlockVerifier.
351    pub(crate) fn verify_signature(&self, context: &Context) -> ConsensusResult<()> {
352        let block = &self.inner;
353        let committee = &context.committee;
354        ensure!(
355            committee.is_valid_index(block.author()),
356            ConsensusError::InvalidAuthorityIndex {
357                index: block.author(),
358                max: committee.size() - 1
359            }
360        );
361        let authority = committee.authority(block.author());
362        verify_block_signature(block, self.signature(), &authority.protocol_key)
363    }
364
365    /// Serialises the block using the bcs serializer
366    pub(crate) fn serialize(&self) -> Result<Bytes, bcs::Error> {
367        let bytes = bcs::to_bytes(self)?;
368        Ok(bytes.into())
369    }
370
371    /// Clears signature for testing.
372    #[cfg(test)]
373    pub(crate) fn clear_signature(&mut self) {
374        self.signature = Bytes::default();
375    }
376}
377
378/// Digest of a block, covering all `Block` fields without its signature.
379/// This is used during Block signing and signature verification.
380/// This should never be used outside of this file, to avoid confusion with `BlockDigest`.
381#[derive(Serialize, Deserialize)]
382struct InnerBlockDigest([u8; consensus_config::DIGEST_LENGTH]);
383
384/// Computes the digest of a Block, only for signing and verifications.
385fn compute_inner_block_digest(block: &Block) -> ConsensusResult<InnerBlockDigest> {
386    let mut hasher = DefaultHashFunction::new();
387    hasher.update(bcs::to_bytes(block).map_err(ConsensusError::SerializationFailure)?);
388    Ok(InnerBlockDigest(hasher.finalize().into()))
389}
390
391/// Wrap a InnerBlockDigest in the intent message.
392fn to_consensus_block_intent(digest: InnerBlockDigest) -> IntentMessage<InnerBlockDigest> {
393    IntentMessage::new(Intent::consensus_app(IntentScope::ConsensusBlock), digest)
394}
395
396/// Process for signing a block & verifying a block signature:
397/// 1. Compute the digest of `Block`.
398/// 2. Wrap the digest in `IntentMessage`.
399/// 3. Sign the serialized `IntentMessage`, or verify signature against it.
400fn compute_block_signature(
401    block: &Block,
402    protocol_keypair: &ProtocolKeyPair,
403) -> ConsensusResult<ProtocolKeySignature> {
404    let digest = compute_inner_block_digest(block)?;
405    let message = bcs::to_bytes(&to_consensus_block_intent(digest))
406        .map_err(ConsensusError::SerializationFailure)?;
407    Ok(protocol_keypair.sign(&message))
408}
409
410fn verify_block_signature(
411    block: &Block,
412    signature: &[u8],
413    protocol_pubkey: &ProtocolPublicKey,
414) -> ConsensusResult<()> {
415    let digest = compute_inner_block_digest(block)?;
416    let message = bcs::to_bytes(&to_consensus_block_intent(digest))
417        .map_err(ConsensusError::SerializationFailure)?;
418    let sig =
419        ProtocolKeySignature::from_bytes(signature).map_err(ConsensusError::MalformedSignature)?;
420    protocol_pubkey
421        .verify(&message, &sig)
422        .map_err(ConsensusError::SignatureVerificationFailure)
423}
424
425/// Allow quick access on the underlying Block without having to always refer to the inner block ref.
426impl Deref for SignedBlock {
427    type Target = Block;
428
429    fn deref(&self) -> &Self::Target {
430        &self.inner
431    }
432}
433
434/// VerifiedBlock allows full access to its content.
435/// Note: clone() is relatively cheap with most underlying data refcounted.
436#[derive(Clone)]
437pub struct VerifiedBlock {
438    block: Arc<SignedBlock>,
439
440    // Cached Block digest and serialized SignedBlock, to avoid re-computing these values.
441    digest: BlockDigest,
442    serialized: Bytes,
443}
444
445impl VerifiedBlock {
446    /// Creates VerifiedBlock from a verified SignedBlock and its serialized bytes.
447    pub(crate) fn new_verified(signed_block: SignedBlock, serialized: Bytes) -> Self {
448        let digest = Self::compute_digest(&serialized);
449        VerifiedBlock {
450            block: Arc::new(signed_block),
451            digest,
452            serialized,
453        }
454    }
455
456    /// This method is public for testing in other crates.
457    pub fn new_for_test(block: Block) -> Self {
458        // Use empty signature in test.
459        let signed_block = SignedBlock {
460            inner: block,
461            signature: Default::default(),
462        };
463        let serialized: Bytes = bcs::to_bytes(&signed_block)
464            .expect("Serialization should not fail")
465            .into();
466        let digest = Self::compute_digest(&serialized);
467        VerifiedBlock {
468            block: Arc::new(signed_block),
469            digest,
470            serialized,
471        }
472    }
473
474    /// Returns reference to the block.
475    pub fn reference(&self) -> BlockRef {
476        BlockRef {
477            round: self.round(),
478            author: self.author(),
479            digest: self.digest(),
480        }
481    }
482
483    pub(crate) fn digest(&self) -> BlockDigest {
484        self.digest
485    }
486
487    /// Returns the serialized block with signature.
488    pub(crate) fn serialized(&self) -> &Bytes {
489        &self.serialized
490    }
491
492    /// Computes digest from the serialized block with signature.
493    pub(crate) fn compute_digest(serialized: &[u8]) -> BlockDigest {
494        let mut hasher = DefaultHashFunction::new();
495        hasher.update(serialized);
496        BlockDigest(hasher.finalize().into())
497    }
498}
499
500/// Allow quick access on the underlying Block without having to always refer to the inner block ref.
501impl Deref for VerifiedBlock {
502    type Target = Block;
503
504    fn deref(&self) -> &Self::Target {
505        &self.block.inner
506    }
507}
508
509impl PartialEq for VerifiedBlock {
510    fn eq(&self, other: &Self) -> bool {
511        self.digest() == other.digest()
512    }
513}
514
515impl fmt::Display for VerifiedBlock {
516    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
517        write!(f, "{}", self.reference())
518    }
519}
520
521// TODO: re-evaluate formats for production debugging.
522impl fmt::Debug for VerifiedBlock {
523    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
524        write!(
525            f,
526            "{:?}([{}];{}ms;{}t;{}c)",
527            self.reference(),
528            self.ancestors().iter().map(|a| a.to_string()).join(", "),
529            self.timestamp_ms(),
530            self.transactions().len(),
531            self.commit_votes().len(),
532        )
533    }
534}
535
536/// Block with extended additional information, such as
537/// local blocks that are excluded from the block's ancestors.
538/// The extended information do not need to be certified or forwarded to other authorities.
539#[derive(Clone, Debug)]
540pub(crate) struct ExtendedBlock {
541    pub block: VerifiedBlock,
542    pub excluded_ancestors: Vec<BlockRef>,
543}
544
545/// Generates the genesis blocks for the current Committee.
546/// The blocks are returned in authority index order.
547pub(crate) fn genesis_blocks(context: &Context) -> Vec<VerifiedBlock> {
548    context
549        .committee
550        .authorities()
551        .map(|(authority_index, _)| {
552            let block = if context.protocol_config.mysticeti_fastpath() {
553                Block::V2(BlockV2::genesis_block(context, authority_index))
554            } else {
555                Block::V1(BlockV1::genesis_block(context, authority_index))
556            };
557            let signed_block = SignedBlock::new_genesis(block);
558            let serialized = signed_block
559                .serialize()
560                .expect("Genesis block serialization failed.");
561            // Unnecessary to verify genesis blocks.
562            VerifiedBlock::new_verified(signed_block, serialized)
563        })
564        .collect::<Vec<VerifiedBlock>>()
565}
566
567/// A block certified by consensus for fast path execution.
568#[derive(Clone)]
569pub struct CertifiedBlock {
570    /// All transactions in the block have a quorum of accept or reject votes.
571    pub block: VerifiedBlock,
572    /// Sorted transaction indices that indicate the transactions rejected by a quorum.
573    pub rejected: Vec<TransactionIndex>,
574}
575
576impl CertifiedBlock {
577    pub fn new(block: VerifiedBlock, rejected: Vec<TransactionIndex>) -> Self {
578        Self { block, rejected }
579    }
580}
581
582/// A batch of certified blocks output by consensus for processing.
583pub struct CertifiedBlocksOutput {
584    pub blocks: Vec<CertifiedBlock>,
585}
586
587/// Creates fake blocks for testing.
588/// This struct is public for testing in other crates.
589#[derive(Clone)]
590pub struct TestBlock {
591    block: BlockV2,
592}
593
594impl TestBlock {
595    pub fn new(round: Round, author: u32) -> Self {
596        Self {
597            block: BlockV2 {
598                round,
599                author: AuthorityIndex::new_for_test(author),
600                ..Default::default()
601            },
602        }
603    }
604
605    pub fn set_epoch(mut self, epoch: Epoch) -> Self {
606        self.block.epoch = epoch;
607        self
608    }
609
610    pub fn set_round(mut self, round: Round) -> Self {
611        self.block.round = round;
612        self
613    }
614
615    pub fn set_author(mut self, author: AuthorityIndex) -> Self {
616        self.block.author = author;
617        self
618    }
619
620    pub fn set_timestamp_ms(mut self, timestamp_ms: BlockTimestampMs) -> Self {
621        self.block.timestamp_ms = timestamp_ms;
622        self
623    }
624
625    /// Sorts then sets ancestors in the TestBlock.
626    /// Author's own block is always first, which is expected by BlockVerifier and
627    /// the rest of the system.
628    pub fn set_ancestors(mut self, mut ancestors: Vec<BlockRef>) -> Self {
629        ancestors.sort_by(|a, b| {
630            if a.author == self.block.author {
631                return std::cmp::Ordering::Less;
632            }
633            if b.author == self.block.author {
634                return std::cmp::Ordering::Greater;
635            }
636            a.author.cmp(&b.author)
637        });
638        self.block.ancestors = ancestors;
639        self
640    }
641
642    /// Sets ancestors in the TestBlock exactly as provided.
643    pub fn set_ancestors_raw(mut self, ancestors: Vec<BlockRef>) -> Self {
644        self.block.ancestors = ancestors;
645        self
646    }
647
648    pub fn set_transactions(mut self, transactions: Vec<Transaction>) -> Self {
649        self.block.transactions = transactions;
650        self
651    }
652
653    pub(crate) fn set_transaction_votes(mut self, votes: Vec<BlockTransactionVotes>) -> Self {
654        self.block.transaction_votes = votes;
655        self
656    }
657
658    #[cfg(test)]
659    pub(crate) fn set_commit_votes(mut self, commit_votes: Vec<CommitVote>) -> Self {
660        self.block.commit_votes = commit_votes;
661        self
662    }
663
664    pub fn build(self) -> Block {
665        Block::V2(self.block)
666    }
667}
668
669/// A block can attach reports of misbehavior by other authorities.
670#[derive(Clone, Serialize, Deserialize, Debug)]
671pub struct MisbehaviorReport {
672    pub target: AuthorityIndex,
673    pub proof: MisbehaviorProof,
674}
675
676/// Proof of misbehavior are usually signed block(s) from the misbehaving authority.
677#[derive(Clone, Serialize, Deserialize, Debug)]
678pub enum MisbehaviorProof {
679    InvalidBlock(BlockRef),
680}
681
682// TODO: add basic verification for BlockRef and BlockDigest.
683// TODO: add tests for SignedBlock and VerifiedBlock conversion.
684
685#[cfg(test)]
686mod tests {
687    use std::sync::Arc;
688
689    use fastcrypto::error::FastCryptoError;
690
691    use crate::{
692        block::{BlockAPI, SignedBlock, TestBlock, genesis_blocks},
693        context::Context,
694        error::ConsensusError,
695    };
696
697    #[tokio::test]
698    async fn test_sign_and_verify() {
699        let (context, key_pairs) = Context::new_for_test(4);
700        let context = Arc::new(context);
701
702        // Create a block that authority 2 has created
703        let block = TestBlock::new(10, 2).build();
704
705        // Create a signed block with authority's 2 private key
706        let author_two_key = &key_pairs[2].1;
707        let signed_block = SignedBlock::new(block, author_two_key).expect("Shouldn't fail signing");
708
709        // Now verify the block's signature
710        let result = signed_block.verify_signature(&context);
711        assert!(result.is_ok());
712
713        // Try to sign authority's 2 block with authority's 1 key
714        let block = TestBlock::new(10, 2).build();
715        let author_one_key = &key_pairs[1].1;
716        let signed_block = SignedBlock::new(block, author_one_key).expect("Shouldn't fail signing");
717
718        // Now verify the block, it should fail
719        let result = signed_block.verify_signature(&context);
720        match result.err().unwrap() {
721            ConsensusError::SignatureVerificationFailure(err) => {
722                assert_eq!(err, FastCryptoError::InvalidSignature);
723            }
724            err => panic!("Unexpected error: {err:?}"),
725        }
726    }
727
728    #[tokio::test]
729    async fn test_genesis_blocks() {
730        let (context, _) = Context::new_for_test(4);
731        const TIMESTAMP_MS: u64 = 1000;
732        let context = Arc::new(context.with_epoch_start_timestamp_ms(TIMESTAMP_MS));
733        let blocks = genesis_blocks(&context);
734        for (i, block) in blocks.into_iter().enumerate() {
735            assert_eq!(block.author().value(), i);
736            assert_eq!(block.round(), 0);
737            assert_eq!(block.timestamp_ms(), TIMESTAMP_MS);
738        }
739    }
740}