consensus_types/
proposal_msg.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::{block::Block, common::Author, sync_info::SyncInfo};
9use anyhow::{anyhow, ensure, format_err, Context, Result};
10use diem_types::validator_verifier::ValidatorVerifier;
11use serde::{Deserialize, Serialize};
12use short_hex_str::AsShortHexStr;
13use std::fmt;
14
15/// ProposalMsg contains the required information for the proposer election
16/// protocol to make its choice (typically depends on round and proposer info).
17#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
18pub struct ProposalMsg {
19    proposal: Block,
20    sync_info: SyncInfo,
21}
22
23impl ProposalMsg {
24    /// Creates a new proposal.
25    pub fn new(proposal: Block, sync_info: SyncInfo) -> Self {
26        Self {
27            proposal,
28            sync_info,
29        }
30    }
31
32    pub fn epoch(&self) -> u64 { self.proposal.epoch() }
33
34    /// Verifies that the ProposalMsg is well-formed.
35    pub fn verify_well_formed(&self) -> Result<()> {
36        ensure!(
37            !self.proposal.is_nil_block(),
38            "Proposal {} for a NIL block",
39            self.proposal
40        );
41        self.proposal
42            .verify_well_formed()
43            .context("Fail to verify ProposalMsg's block")?;
44        ensure!(
45            self.proposal.round() > 0,
46            "Proposal for {} has an incorrect round of 0",
47            self.proposal,
48        );
49        ensure!(
50            self.proposal.epoch() == self.sync_info.epoch(),
51            "ProposalMsg has different epoch number from SyncInfo"
52        );
53        ensure!(
54            self.proposal.parent_id()
55                == self.sync_info.highest_quorum_cert().certified_block().id(),
56            "Proposal HQC in SyncInfo certifies {}, but block parent id is {}",
57            self.sync_info.highest_quorum_cert().certified_block().id(),
58            self.proposal.parent_id(),
59        );
60        let previous_round = self
61            .proposal
62            .round()
63            .checked_sub(1)
64            .ok_or_else(|| anyhow!("proposal round overflowed!"))?;
65
66        let highest_certified_round = std::cmp::max(
67            self.proposal.quorum_cert().certified_block().round(),
68            self.sync_info
69                .highest_timeout_certificate()
70                .map_or(0, |tc| tc.round()),
71        );
72        ensure!(
73            previous_round == highest_certified_round,
74            "Proposal {} does not have a certified round {}",
75            self.proposal,
76            previous_round
77        );
78        ensure!(
79            self.proposal.author().is_some(),
80            "Proposal {} does not define an author",
81            self.proposal
82        );
83        Ok(())
84    }
85
86    pub fn verify(
87        &self, validator: &ValidatorVerifier, epoch_vrf_seed: &[u8],
88    ) -> Result<()> {
89        self.proposal
90            .validate_signature(validator)
91            .map_err(|e| format_err!("{:?}", e))?;
92
93        if let Some(vrf_proof) = self.proposal.vrf_proof() {
94            validator.verify_vrf(
95                self.proposal.author().unwrap(),
96                &self.proposal.block_data().vrf_round_seed(epoch_vrf_seed),
97                vrf_proof,
98            )?;
99        }
100        // if there is a timeout certificate, verify its signatures
101        if let Some(tc) = self.sync_info.highest_timeout_certificate() {
102            tc.verify(validator).map_err(|e| format_err!("{:?}", e))?;
103        }
104        // Note that we postpone the verification of SyncInfo until it's being
105        // used.
106        self.verify_well_formed()
107    }
108
109    pub fn proposal(&self) -> &Block { &self.proposal }
110
111    pub fn take_proposal(self) -> Block { self.proposal }
112
113    pub fn sync_info(&self) -> &SyncInfo { &self.sync_info }
114
115    pub fn proposer(&self) -> Author {
116        self.proposal
117            .author()
118            .expect("Proposal should be verified having an author")
119    }
120}
121
122impl fmt::Display for ProposalMsg {
123    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
124        write!(f, "[proposal {} from ", self.proposal)?;
125        match self.proposal.author() {
126            Some(author) => write!(f, "{}]", author.short_str()),
127            None => write!(f, "NIL]"),
128        }
129    }
130}