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