consensus_types/
block_data.rs

1// Copyright (c) The Diem Core Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4// Copyright 2021 Conflux Foundation. All rights reserved.
5// Conflux is free software and distributed under GNU General Public License.
6// See http://www.gnu.org/licenses/
7
8use crate::{
9    common::{Author, Payload, Round},
10    quorum_cert::{QuorumCert, QuorumCertUnchecked},
11    vote_data::VoteData,
12};
13use diem_crypto::hash::HashValue;
14use diem_crypto_derive::{BCSCryptoHash, CryptoHasher};
15use diem_types::{
16    block_info::BlockInfo,
17    ledger_info::{LedgerInfo, LedgerInfoWithSignatures},
18    transaction::SignedTransactionUnchecked,
19};
20use mirai_annotations::*;
21use serde::{Deserialize, Serialize};
22use std::collections::BTreeMap;
23
24#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
25pub enum BlockType {
26    Proposal {
27        /// T of the block (e.g. one or more transaction(s)
28        payload: Payload,
29        /// Author of the block that can be validated by the author's public
30        /// key and the signature
31        author: Author,
32    },
33    /// NIL blocks don't have authors or signatures: they're generated upon
34    /// timeouts to fill in the gaps in the rounds.
35    NilBlock,
36    /// A genesis block is the first committed block in any epoch that is
37    /// identically constructed on all validators by any (potentially
38    /// different) LedgerInfo that justifies the epoch change
39    /// from the previous epoch.  The genesis block is used as the first
40    /// root block of the BlockTree for all epochs.
41    Genesis,
42}
43
44#[derive(Deserialize)]
45pub enum BlockTypeUnchecked {
46    Proposal {
47        payload: Vec<SignedTransactionUnchecked>,
48        author: Author,
49    },
50    NilBlock,
51    Genesis,
52}
53
54impl From<BlockTypeUnchecked> for BlockType {
55    fn from(t: BlockTypeUnchecked) -> Self {
56        match t {
57            BlockTypeUnchecked::Proposal { payload, author } => {
58                Self::Proposal {
59                    payload: payload.into_iter().map(Into::into).collect(),
60                    author,
61                }
62            }
63            BlockTypeUnchecked::NilBlock => Self::NilBlock,
64            BlockTypeUnchecked::Genesis => Self::Genesis,
65        }
66    }
67}
68
69#[derive(
70    Deserialize,
71    Serialize,
72    Clone,
73    Debug,
74    PartialEq,
75    Eq,
76    CryptoHasher,
77    BCSCryptoHash,
78)]
79/// Block has the core data of a consensus block that should be persistent when
80/// necessary. Each block must know the id of its parent and keep the
81/// QuorurmCertificate to that parent.
82pub struct BlockData {
83    /// Epoch number corresponds to the set of validators that are active for
84    /// this block.
85    epoch: u64,
86    /// The round of a block is an internal monotonically increasing counter
87    /// used by Consensus protocol.
88    round: Round,
89    /// The approximate physical time a block is proposed by a proposer.  This
90    /// timestamp is used for
91    /// * Time-dependent logic in smart contracts (the current time of
92    ///   execution)
93    /// * Clients determining if they are relatively up-to-date with respect to
94    ///   the block chain.
95    ///
96    /// It makes the following guarantees:
97    ///   1. Time Monotonicity: Time is monotonically increasing in the block
98    /// chain.      (i.e. If H1 < H2, H1.Time < H2.Time).
99    ///   2. If a block of transactions B is agreed on with timestamp T, then
100    /// at least      f+1 honest validators think that T is in the past. An
101    /// honest validator will      only vote on a block when its own clock
102    /// >= timestamp T.   3. If a block of transactions B has a QC with
103    /// timestamp T, an honest validator      will not serve such a block
104    /// to other validators until its own clock >= timestamp T.
105    ///   4. Current: an honest validator is not issuing blocks with a
106    /// timestamp in the       future. Currently we consider a block is
107    /// malicious if it was issued more       that 5 minutes in the future.
108    timestamp_usecs: u64,
109    /// Contains the quorum certified ancestor and whether the quorum certified
110    /// ancestor was voted on successfully
111    quorum_cert: QuorumCert,
112    /// If a block is a real proposal, contains its author and signature.
113    block_type: BlockType,
114}
115
116#[derive(Deserialize)]
117pub struct BlockDataUnchecked {
118    pub epoch: u64,
119    pub round: Round,
120    pub timestamp_usecs: u64,
121    pub quorum_cert: QuorumCertUnchecked,
122    pub block_type: BlockTypeUnchecked,
123}
124
125impl From<BlockDataUnchecked> for BlockData {
126    fn from(b: BlockDataUnchecked) -> Self {
127        Self {
128            epoch: b.epoch,
129            round: b.round,
130            timestamp_usecs: b.timestamp_usecs,
131            quorum_cert: b.quorum_cert.into(),
132            block_type: b.block_type.into(),
133        }
134    }
135}
136
137impl BlockData {
138    pub fn author(&self) -> Option<Author> {
139        if let BlockType::Proposal { author, .. } = self.block_type {
140            Some(author)
141        } else {
142            None
143        }
144    }
145
146    pub fn block_type(&self) -> &BlockType { &self.block_type }
147
148    pub fn epoch(&self) -> u64 { self.epoch }
149
150    pub fn parent_id(&self) -> HashValue {
151        self.quorum_cert.certified_block().id()
152    }
153
154    pub fn payload(&self) -> Option<&Payload> {
155        if let BlockType::Proposal { payload, .. } = &self.block_type {
156            Some(payload)
157        } else {
158            None
159        }
160    }
161
162    pub fn round(&self) -> Round { self.round }
163
164    pub fn timestamp_usecs(&self) -> u64 { self.timestamp_usecs }
165
166    pub fn quorum_cert(&self) -> &QuorumCert { &self.quorum_cert }
167
168    pub fn is_genesis_block(&self) -> bool {
169        matches!(self.block_type, BlockType::Genesis)
170    }
171
172    pub fn is_nil_block(&self) -> bool {
173        matches!(self.block_type, BlockType::NilBlock)
174    }
175
176    pub fn vrf_round_seed(&self, seed: &[u8]) -> Vec<u8> {
177        let mut round_seed = seed.to_vec();
178        // Make 3 continuous rounds have the same leader.
179        // Round 0 has no leader, so we use "round+1" here.
180        let leader_round = (self.round + 1) / 3;
181        round_seed.extend_from_slice(&leader_round.to_be_bytes());
182        round_seed
183    }
184
185    pub fn new_genesis_from_ledger_info(ledger_info: &LedgerInfo) -> Self {
186        assert!(ledger_info.ends_epoch());
187        let ancestor = BlockInfo::new(
188            ledger_info.epoch(),
189            0,                 /* round */
190            HashValue::zero(), /* parent block id */
191            ledger_info.transaction_accumulator_hash(),
192            ledger_info.version(),
193            ledger_info.timestamp_usecs(),
194            None,
195            ledger_info.pivot_decision().cloned(),
196        );
197
198        // Genesis carries a placeholder quorum certificate to its parent id
199        // with LedgerInfo carrying information about version from the
200        // last LedgerInfo of previous epoch.
201        let genesis_quorum_cert = QuorumCert::new(
202            VoteData::new(ancestor.clone(), ancestor.clone()),
203            LedgerInfoWithSignatures::new(
204                LedgerInfo::new(ancestor, HashValue::zero()),
205                BTreeMap::new(),
206            ),
207        );
208
209        BlockData::new_genesis(
210            ledger_info.timestamp_usecs(),
211            genesis_quorum_cert,
212        )
213    }
214
215    #[cfg(any(test, feature = "fuzzing"))]
216    // This method should only used by tests and fuzzers to produce arbitrary
217    // BlockData types.
218    pub fn new_for_testing(
219        epoch: u64, round: Round, timestamp_usecs: u64,
220        quorum_cert: QuorumCert, block_type: BlockType,
221    ) -> Self {
222        Self {
223            epoch,
224            round,
225            timestamp_usecs,
226            quorum_cert,
227            block_type,
228        }
229    }
230
231    pub fn new_genesis(timestamp_usecs: u64, quorum_cert: QuorumCert) -> Self {
232        assume!(quorum_cert.certified_block().epoch() < u64::max_value()); // unlikely to be false in this universe
233        Self {
234            epoch: quorum_cert.certified_block().epoch() + 1,
235            round: 0,
236            timestamp_usecs,
237            quorum_cert,
238            block_type: BlockType::Genesis,
239        }
240    }
241
242    pub fn new_nil(round: Round, quorum_cert: QuorumCert) -> Self {
243        // We want all the NIL blocks to agree on the timestamps even though
244        // they're generated independently by different validators,
245        // hence we're using the timestamp of a parent + 1.
246        assume!(
247            quorum_cert.certified_block().timestamp_usecs() < u64::max_value()
248        ); // unlikely to be false in this universe
249        let timestamp_usecs = quorum_cert.certified_block().timestamp_usecs();
250
251        Self {
252            epoch: quorum_cert.certified_block().epoch(),
253            round,
254            timestamp_usecs,
255            quorum_cert,
256            block_type: BlockType::NilBlock,
257        }
258    }
259
260    pub fn new_proposal(
261        payload: Payload, author: Author, round: Round, timestamp_usecs: u64,
262        quorum_cert: QuorumCert,
263    ) -> Self {
264        Self {
265            epoch: quorum_cert.certified_block().epoch(),
266            round,
267            timestamp_usecs,
268            quorum_cert,
269            block_type: BlockType::Proposal { payload, author },
270        }
271    }
272}