consensus_types/
block.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    block_data::{BlockData, BlockDataUnchecked, BlockType},
10    common::{Author, Payload, Round},
11    quorum_cert::QuorumCert,
12};
13use anyhow::{bail, ensure, format_err};
14use diem_crypto::{hash::CryptoHash, HashValue};
15use diem_types::{
16    account_address::AccountAddress,
17    block_info::{BlockInfo, PivotBlockDecision},
18    block_metadata::BlockMetadata,
19    epoch_state::EpochState,
20    ledger_info::LedgerInfo,
21    transaction::Version,
22    validator_config::{ConsensusSignature, ConsensusVRFProof},
23    validator_signer::ValidatorSigner,
24    validator_verifier::ValidatorVerifier,
25};
26use mirai_annotations::debug_checked_verify_eq;
27use serde::{Deserialize, Deserializer, Serialize};
28use std::{
29    fmt::{self, Display, Formatter},
30    time::{SystemTime, UNIX_EPOCH},
31};
32
33#[path = "block_test_utils.rs"]
34#[cfg(any(test, feature = "fuzzing"))]
35pub mod block_test_utils;
36
37#[cfg(test)]
38#[path = "block_test.rs"]
39pub mod block_test;
40
41#[derive(Serialize, Clone, PartialEq, Eq)]
42/// Block has the core data of a consensus block that should be persistent when
43/// necessary. Each block must know the id of its parent and keep the
44/// QuorurmCertificate to that parent.
45pub struct Block {
46    /// This block's id as a hash value, it is generated at call time
47    #[serde(skip)]
48    id: HashValue,
49    /// The container for the actual block
50    block_data: BlockData,
51    /// Signature that the hash of this block has been authored by the owner of
52    /// the private key, this is only set within Proposal blocks
53    signature: Option<ConsensusSignature>,
54    /// Optional VRF proof tp prove the author is a valid proposer in this
55    /// round.
56    vrf_nonce_and_proof: Option<(u64, ConsensusVRFProof)>,
57}
58
59#[derive(Deserialize)]
60pub struct BlockUnchecked {
61    block_data: BlockDataUnchecked,
62    signature: Option<ConsensusSignature>,
63    vrf_nonce_and_proof: Option<(u64, ConsensusVRFProof)>,
64}
65
66impl From<BlockUnchecked> for Block {
67    fn from(b: BlockUnchecked) -> Self {
68        let block_data: BlockData = b.block_data.into();
69        Self {
70            id: block_data.hash(),
71            block_data,
72            signature: b.signature,
73            vrf_nonce_and_proof: b.vrf_nonce_and_proof,
74        }
75    }
76}
77
78impl fmt::Debug for Block {
79    fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "{}", self) }
80}
81
82impl Display for Block {
83    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
84        let nil_marker = if self.is_nil_block() { " (NIL)" } else { "" };
85        write!(
86            f,
87            "[id: {}{}, epoch: {}, round: {:02}, parent_id: {}]",
88            self.id,
89            nil_marker,
90            self.epoch(),
91            self.round(),
92            self.quorum_cert().certified_block().id(),
93        )
94    }
95}
96
97impl Block {
98    pub fn author(&self) -> Option<Author> { self.block_data.author() }
99
100    pub fn epoch(&self) -> u64 { self.block_data.epoch() }
101
102    pub fn id(&self) -> HashValue { self.id }
103
104    // Is this block a parent of the parameter block?
105    #[cfg(test)]
106    pub fn is_parent_of(&self, block: &Self) -> bool {
107        block.parent_id() == self.id
108    }
109
110    pub fn parent_id(&self) -> HashValue {
111        self.block_data.quorum_cert().certified_block().id()
112    }
113
114    pub fn payload(&self) -> Option<&Payload> { self.block_data.payload() }
115
116    pub fn quorum_cert(&self) -> &QuorumCert { self.block_data.quorum_cert() }
117
118    pub fn round(&self) -> Round { self.block_data.round() }
119
120    pub fn signature(&self) -> Option<&ConsensusSignature> {
121        self.signature.as_ref()
122    }
123
124    pub fn vrf_proof(&self) -> Option<&ConsensusVRFProof> {
125        self.vrf_nonce_and_proof
126            .as_ref()
127            .map(|(_nonce, proof)| proof)
128    }
129
130    pub fn vrf_nonce(&self) -> Option<u64> {
131        self.vrf_nonce_and_proof
132            .as_ref()
133            .map(|(nonce, _proof)| *nonce)
134    }
135
136    pub fn set_vrf_nonce_and_proof(
137        &mut self, vrf_nonce_and_proof: (u64, ConsensusVRFProof),
138    ) {
139        debug_assert!(self.vrf_nonce_and_proof.is_none());
140        self.vrf_nonce_and_proof = Some(vrf_nonce_and_proof);
141    }
142
143    pub fn timestamp_usecs(&self) -> u64 { self.block_data.timestamp_usecs() }
144
145    pub fn gen_block_info(
146        &self, executed_state_id: HashValue, version: Version,
147        next_epoch_state: Option<EpochState>,
148        pivot: Option<PivotBlockDecision>,
149    ) -> BlockInfo {
150        BlockInfo::new(
151            self.epoch(),
152            self.round(),
153            self.id(),
154            executed_state_id,
155            version,
156            self.timestamp_usecs(),
157            next_epoch_state,
158            pivot,
159        )
160    }
161
162    pub fn block_data(&self) -> &BlockData { &self.block_data }
163
164    pub fn is_genesis_block(&self) -> bool {
165        self.block_data.is_genesis_block()
166    }
167
168    pub fn is_nil_block(&self) -> bool { self.block_data.is_nil_block() }
169
170    #[cfg(any(test, feature = "fuzzing"))]
171    pub fn make_genesis_block() -> Self {
172        Self::make_genesis_block_from_ledger_info(&LedgerInfo::mock_genesis(
173            None,
174        ))
175    }
176
177    /// Construct new genesis block for next epoch deterministically from the
178    /// end-epoch LedgerInfo We carry over most fields except round and
179    /// block id
180    pub fn make_genesis_block_from_ledger_info(
181        ledger_info: &LedgerInfo,
182    ) -> Self {
183        let block_data = BlockData::new_genesis_from_ledger_info(ledger_info);
184        Block {
185            id: block_data.hash(),
186            block_data,
187            signature: None,
188            vrf_nonce_and_proof: None,
189        }
190    }
191
192    #[cfg(any(test, feature = "fuzzing"))]
193    // This method should only used by tests and fuzzers to produce arbitrary
194    // Block types.
195    pub fn new_for_testing(
196        id: HashValue, block_data: BlockData,
197        signature: Option<ConsensusSignature>,
198        vrf_proof: Option<(u64, ConsensusVRFProof)>,
199    ) -> Self {
200        Block {
201            id,
202            block_data,
203            signature,
204            vrf_nonce_and_proof: vrf_proof,
205        }
206    }
207
208    /// The NIL blocks are special: they're not carrying any real payload and
209    /// are generated independently by different validators just to fill in
210    /// the round with some QC.
211    pub fn new_nil(round: Round, quorum_cert: QuorumCert) -> Self {
212        let block_data = BlockData::new_nil(round, quorum_cert);
213
214        Block {
215            id: block_data.hash(),
216            block_data,
217            signature: None,
218            vrf_nonce_and_proof: None,
219        }
220    }
221
222    // Test only?
223    pub fn new_proposal(
224        payload: Payload, round: Round, timestamp_usecs: u64,
225        quorum_cert: QuorumCert, validator_signer: &ValidatorSigner,
226    ) -> Self {
227        let block_data = BlockData::new_proposal(
228            payload,
229            validator_signer.author(),
230            round,
231            timestamp_usecs,
232            quorum_cert,
233        );
234
235        Self::new_proposal_from_block_data(block_data, validator_signer)
236    }
237
238    // Test only?
239    pub fn new_proposal_from_block_data(
240        block_data: BlockData, validator_signer: &ValidatorSigner,
241    ) -> Self {
242        let signature = validator_signer.sign(&block_data);
243        Self::new_proposal_from_block_data_and_signature(
244            block_data, signature, None,
245        )
246    }
247
248    pub fn new_proposal_from_block_data_and_signature(
249        block_data: BlockData, signature: ConsensusSignature,
250        vrf_nonce_and_proof: Option<(u64, ConsensusVRFProof)>,
251    ) -> Self {
252        Block {
253            id: block_data.hash(),
254            block_data,
255            signature: Some(signature),
256            vrf_nonce_and_proof,
257        }
258    }
259
260    /// Verifies that the proposal and the QC are correctly signed.
261    /// If this is the genesis block, we skip these checks.
262    pub fn validate_signature(
263        &self, validator: &ValidatorVerifier,
264    ) -> anyhow::Result<()> {
265        match self.block_data.block_type() {
266            BlockType::Genesis => {
267                bail!("We should not accept genesis from others")
268            }
269            BlockType::NilBlock => self.quorum_cert().verify(validator),
270            BlockType::Proposal { author, .. } => {
271                let signature = self.signature.as_ref().ok_or_else(|| {
272                    format_err!("Missing signature in Proposal")
273                })?;
274                validator.verify(*author, &self.block_data, signature)?;
275                self.quorum_cert().verify(validator)
276            }
277        }
278    }
279
280    /// Makes sure that the proposal makes sense, independently of the current
281    /// state. If this is the genesis block, we skip these checks.
282    pub fn verify_well_formed(&self) -> anyhow::Result<()> {
283        ensure!(
284            !self.is_genesis_block(),
285            "We must not accept genesis from others"
286        );
287        let parent = self.quorum_cert().certified_block();
288        ensure!(
289            parent.round() < self.round(),
290            "Block must have a greater round than parent's block"
291        );
292        ensure!(
293            parent.epoch() == self.epoch(),
294            "block's parent should be in the same epoch"
295        );
296        if parent.has_reconfiguration() {
297            ensure!(
298                self.payload().map_or(true, |p| p.is_empty()),
299                "Reconfiguration suffix should not carry payload"
300            );
301        }
302        if self.is_nil_block() || parent.has_reconfiguration() {
303            ensure!(
304                self.timestamp_usecs() == parent.timestamp_usecs(),
305                "Nil/reconfig suffix block must have same timestamp as parent"
306            );
307        } else {
308            ensure!(
309                self.timestamp_usecs() > parent.timestamp_usecs(),
310                "Blocks must have strictly increasing timestamps"
311            );
312
313            let current_ts = SystemTime::now()
314                .duration_since(UNIX_EPOCH)
315                .expect("System time is before UNIX_EPOCH");
316
317            // we can say that too far is 5 minutes in the future
318            const TIMEBOUND: u64 = 300_000_000;
319            ensure!(
320                self.timestamp_usecs()
321                    <= (current_ts.as_micros() as u64)
322                        .saturating_add(TIMEBOUND),
323                "Blocks must not be too far in the future"
324            );
325        }
326        ensure!(
327            !self.quorum_cert().ends_epoch(),
328            "Block cannot be proposed in an epoch that has ended"
329        );
330        debug_checked_verify_eq!(
331            self.id(),
332            self.block_data.hash(),
333            "Block id mismatch the hash"
334        );
335        Ok(())
336    }
337}
338
339impl<'de> Deserialize<'de> for Block {
340    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
341    where D: Deserializer<'de> {
342        #[derive(Deserialize)]
343        #[serde(rename = "Block")]
344        struct BlockWithoutId {
345            block_data: BlockData,
346            signature: Option<ConsensusSignature>,
347            vrf_nonce_and_proof: Option<(u64, ConsensusVRFProof)>,
348        }
349
350        let BlockWithoutId {
351            block_data,
352            signature,
353            vrf_nonce_and_proof,
354        } = BlockWithoutId::deserialize(deserializer)?;
355
356        Ok(Block {
357            id: block_data.hash(),
358            block_data,
359            signature,
360            vrf_nonce_and_proof,
361        })
362    }
363}
364
365impl From<&Block> for BlockMetadata {
366    fn from(block: &Block) -> Self {
367        Self::new(
368            block.id(),
369            block.round(),
370            block.timestamp_usecs(),
371            // an ordered vector of voters' account address
372            block
373                .quorum_cert()
374                .ledger_info()
375                .signatures()
376                .keys()
377                .cloned()
378                .collect(),
379            // For nil block, we use 0x0 which is convention for nil address in
380            // move.
381            block.author().unwrap_or(AccountAddress::ZERO),
382        )
383    }
384}