1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

// Copyright 2021 Conflux Foundation. All rights reserved.
// Conflux is free software and distributed under GNU General Public License.
// See http://www.gnu.org/licenses/

use crate::{block::Block, common::Author, sync_info::SyncInfo};
use anyhow::{anyhow, ensure, format_err, Context, Result};
use diem_types::validator_verifier::ValidatorVerifier;
use serde::{Deserialize, Serialize};
use short_hex_str::AsShortHexStr;
use std::fmt;

/// ProposalMsg contains the required information for the proposer election
/// protocol to make its choice (typically depends on round and proposer info).
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct ProposalMsg {
    proposal: Block,
    sync_info: SyncInfo,
}

impl ProposalMsg {
    /// Creates a new proposal.
    pub fn new(proposal: Block, sync_info: SyncInfo) -> Self {
        Self {
            proposal,
            sync_info,
        }
    }

    pub fn epoch(&self) -> u64 { self.proposal.epoch() }

    /// Verifies that the ProposalMsg is well-formed.
    pub fn verify_well_formed(&self) -> Result<()> {
        ensure!(
            !self.proposal.is_nil_block(),
            "Proposal {} for a NIL block",
            self.proposal
        );
        self.proposal
            .verify_well_formed()
            .context("Fail to verify ProposalMsg's block")?;
        ensure!(
            self.proposal.round() > 0,
            "Proposal for {} has an incorrect round of 0",
            self.proposal,
        );
        ensure!(
            self.proposal.epoch() == self.sync_info.epoch(),
            "ProposalMsg has different epoch number from SyncInfo"
        );
        ensure!(
            self.proposal.parent_id()
                == self.sync_info.highest_quorum_cert().certified_block().id(),
            "Proposal HQC in SyncInfo certifies {}, but block parent id is {}",
            self.sync_info.highest_quorum_cert().certified_block().id(),
            self.proposal.parent_id(),
        );
        let previous_round = self
            .proposal
            .round()
            .checked_sub(1)
            .ok_or_else(|| anyhow!("proposal round overflowed!"))?;

        let highest_certified_round = std::cmp::max(
            self.proposal.quorum_cert().certified_block().round(),
            self.sync_info
                .highest_timeout_certificate()
                .map_or(0, |tc| tc.round()),
        );
        ensure!(
            previous_round == highest_certified_round,
            "Proposal {} does not have a certified round {}",
            self.proposal,
            previous_round
        );
        ensure!(
            self.proposal.author().is_some(),
            "Proposal {} does not define an author",
            self.proposal
        );
        Ok(())
    }

    pub fn verify(
        &self, validator: &ValidatorVerifier, epoch_vrf_seed: &[u8],
    ) -> Result<()> {
        self.proposal
            .validate_signature(validator)
            .map_err(|e| format_err!("{:?}", e))?;

        if let Some(vrf_proof) = self.proposal.vrf_proof() {
            validator.verify_vrf(
                self.proposal.author().unwrap(),
                &self.proposal.block_data().vrf_round_seed(epoch_vrf_seed),
                vrf_proof,
            )?;
        }
        // if there is a timeout certificate, verify its signatures
        if let Some(tc) = self.sync_info.highest_timeout_certificate() {
            tc.verify(validator).map_err(|e| format_err!("{:?}", e))?;
        }
        // Note that we postpone the verification of SyncInfo until it's being
        // used.
        self.verify_well_formed()
    }

    pub fn proposal(&self) -> &Block { &self.proposal }

    pub fn take_proposal(self) -> Block { self.proposal }

    pub fn sync_info(&self) -> &SyncInfo { &self.sync_info }

    pub fn proposer(&self) -> Author {
        self.proposal
            .author()
            .expect("Proposal should be verified having an author")
    }
}

impl fmt::Display for ProposalMsg {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[proposal {} from ", self.proposal)?;
        match self.proposal.author() {
            Some(author) => write!(f, "{}]", author.short_str()),
            None => write!(f, "NIL]"),
        }
    }
}