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