consensus_types/
vote.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::{common::Author, timeout::Timeout, vote_data::VoteData};
9use anyhow::{ensure, Context};
10use diem_crypto::hash::CryptoHash;
11use diem_types::{
12    ledger_info::LedgerInfo, validator_config::ConsensusSignature,
13    validator_signer::ValidatorSigner, validator_verifier::ValidatorVerifier,
14};
15use serde::{Deserialize, Serialize};
16use short_hex_str::AsShortHexStr;
17use std::fmt::{Debug, Display, Formatter};
18
19/// Vote is the struct that is ultimately sent by the voter in response for
20/// receiving a proposal.
21/// Vote carries the `LedgerInfo` of a block that is going to be committed in
22/// case this vote is gathers QuorumCertificate (see the detailed explanation in
23/// the comments of `LedgerInfo`).
24#[derive(Deserialize, Serialize, Clone, PartialEq, Eq)]
25pub struct Vote {
26    /// The data of the vote
27    vote_data: VoteData,
28    /// The identity of the voter.
29    author: Author,
30    /// LedgerInfo of a block that is going to be committed in case this vote
31    /// gathers QC.
32    ledger_info: LedgerInfo,
33    /// Signature of the LedgerInfo
34    signature: ConsensusSignature,
35    /// The round signatures can be aggregated into a timeout certificate if
36    /// present.
37    timeout_signature: Option<ConsensusSignature>,
38}
39
40// this is required by structured log
41impl Debug for Vote {
42    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
43        write!(f, "{}", self)
44    }
45}
46
47impl Display for Vote {
48    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
49        write!(
50            f,
51            "Vote: [vote data: {}, author: {}, is_timeout: {}, {}]",
52            self.vote_data,
53            self.author.short_str(),
54            self.is_timeout(),
55            self.ledger_info
56        )
57    }
58}
59
60impl Vote {
61    /// Generates a new Vote corresponding to the "fast-vote" path without the
62    /// round signatures that can be aggregated into a timeout certificate
63    pub fn new(
64        vote_data: VoteData, author: Author,
65        mut ledger_info_placeholder: LedgerInfo,
66        validator_signer: &ValidatorSigner,
67    ) -> Self {
68        ledger_info_placeholder.set_consensus_data_hash(vote_data.hash());
69        let signature = validator_signer.sign(&ledger_info_placeholder);
70        Self::new_with_signature(
71            vote_data,
72            author,
73            ledger_info_placeholder,
74            signature,
75        )
76    }
77
78    /// Generates a new Vote using a signature over the specified ledger_info
79    pub fn new_with_signature(
80        vote_data: VoteData, author: Author, ledger_info: LedgerInfo,
81        signature: ConsensusSignature,
82    ) -> Self {
83        Self {
84            vote_data,
85            author,
86            ledger_info,
87            signature,
88            timeout_signature: None,
89        }
90    }
91
92    /// Generates a round signature, which can then be used for aggregating a
93    /// timeout certificate. Typically called for generating vote messages
94    /// that are sent upon timeouts.
95    pub fn add_timeout_signature(&mut self, signature: ConsensusSignature) {
96        if self.timeout_signature.is_some() {
97            return; // round signature is already set
98        }
99
100        self.timeout_signature.replace(signature);
101    }
102
103    pub fn vote_data(&self) -> &VoteData { &self.vote_data }
104
105    /// Return the author of the vote
106    pub fn author(&self) -> Author { self.author }
107
108    /// Return the LedgerInfo associated with this vote
109    pub fn ledger_info(&self) -> &LedgerInfo { &self.ledger_info }
110
111    /// Return the signature of the vote
112    pub fn signature(&self) -> &ConsensusSignature { &self.signature }
113
114    /// Returns the hash of the data represent by a timeout proposal
115    pub fn timeout(&self) -> Timeout {
116        Timeout::new(
117            self.vote_data().proposed().epoch(),
118            self.vote_data().proposed().round(),
119        )
120    }
121
122    /// Return the epoch of the vote
123    pub fn epoch(&self) -> u64 { self.vote_data.proposed().epoch() }
124
125    /// Returns the signature for the vote_data().proposed().round() that can be
126    /// aggregated for TimeoutCertificate.
127    pub fn timeout_signature(&self) -> Option<&ConsensusSignature> {
128        self.timeout_signature.as_ref()
129    }
130
131    /// The vote message is considered a timeout vote message if it carries a
132    /// signature on the round, which can then be used for aggregating it to
133    /// the TimeoutCertificate.
134    pub fn is_timeout(&self) -> bool { self.timeout_signature.is_some() }
135
136    /// Verifies that the consensus data hash of LedgerInfo corresponds to the
137    /// vote info, and then verifies the signature.
138    pub fn verify(&self, validator: &ValidatorVerifier) -> anyhow::Result<()> {
139        ensure!(
140            self.ledger_info.consensus_data_hash() == self.vote_data.hash(),
141            "Vote's hash mismatch with LedgerInfo"
142        );
143        validator
144            .verify(self.author(), &self.ledger_info, &self.signature)
145            .context("Failed to verify Vote")?;
146        if let Some(timeout_signature) = &self.timeout_signature {
147            validator
148                .verify(self.author(), &self.timeout(), timeout_signature)
149                .context("Failed to verify Timeout Vote")?;
150        }
151        // Let us verify the vote data as well
152        self.vote_data().verify()?;
153        Ok(())
154    }
155}
156
157#[test]
158fn test() {
159    use rustc_hex::FromHex;
160    let data1= "01000000000000000200000000000000202bc30b4ba1d2f7a62c8d4141d0477cd33b6f46f2c566a815096455bb87b8f85b20000000000000000000000000000000000000000000000000000000000000000003000000000000004a39e438c5cf0500000110ae050000000000423078613064656239346661613932326665316663373362386466303061353236323530343266353262643562306234376339373230616133626334636366663462330100000000000000010000000000000020fc41c6da6b188a28803dd8e082f44494fbdc2af9a57dcdad206ad5e18449f20e20000000000000000000000000000000000000000000000000000000000000000002000000000000004a39e438c5cf0500000110ae05000000000042307861306465623934666161393232666531666337336238646630306135323632353034326635326264356230623437633937323061613362633463636666346233046ca462890f25ed9394ca9f92c979ff48e1738a81822ecab96d83813c1a433c0100000000000000000000000000000020c9533883c69d23423b7e0cf8839ab576190e7afe421283e1d7490301f749acbf200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000042307835326466636539376464656435326536633665386634356638346462303366666139373533393265626333376436376466333332643430373238383166366665208c0d17c8d9bafab28641bf731b84db92ae4222c299d5df4bfb72fdbe86b35555c001032e102a54068dd95f1d5c2c578b273dd35169f6f37490f4f732d503edc5957fafdb40e232456110f10bff94fc8700b316c47d9aa54c2c090572b837844187d87689542fa21133a873e78a3ade6ce0c1bb4897d026c889224827e00bb77572680ce7d8fde8f72b8c1993565dbff0ed8df8e65b8b7cd60aaf57f37ab1bbc3bee449d7a1e26d655c10500bc75003aea86d19a64b66ab42a0b8dcdadbc49cfea17eea1ba44f47460f0bd487a19c1cbd034576aa3ffc757628aa9541db8771ef56f201c0010f034cf25b711bea069675e0031f8018a7ed63ae32748f90fda15676df08d38d72309ced2ff3ce6cd4d2aaa3491a825808879672691d3a8109fd328ef8db8a590c02ef9172dab53b4b8f3a5214acf566d4382ec3f07ec3ef6ec7966a14f0297f0230442b11c21e90bc6bf067cc08e26cc395f12894fb829d98b9b933af834a21779e6cb484bb370243be298d366e0d9e177600b2c6d7a0bddfefd468c3f3f22c0e74c6d0c5d0908460d7914f25b306445953cc26154b4a5c74df2f672d8be574";
161    let data2= "01000000000000000200000000000000202cc47aaf1d7b8ce520d59572cc2070e766048c9ecf90325df89f5816069a9f3b2000000000000000000000000000000000000000000000000000000000000000002b0000000000000022658238b8cf0500000124f504000000000042307863643562313232373066616535316165376533636635666136326564356335666261303263633761653561633230656261376132623738346139326537663334010000000000000001000000000000002003e356be377d4673b9e985551c1ec7f7806c58ff8e0550bc954367cbdd3824d12000000000000000000000000000000000000000000000000000000000000000000200000000000000a648ee34b8cf0500000124f504000000000042307863643562313232373066616535316165376533636635666136326564356335666261303263633761653561633230656261376132623738346139326537663334046ca462890f25ed9394ca9f92c979ff48e1738a81822ecab96d83813c1a433c0100000000000000000000000000000020c9533883c69d23423b7e0cf8839ab576190e7afe421283e1d7490301f749acbf20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000004230783532646663653937646465643532653663366538663435663834646230336666613937353339326562633337643637646633333264343037323838316636666520dfeb4989d67645535415b51aa5f4f64e49b5fb0223092df4fc73d1586d77aaaac0010cab4dc39f875cba05396f532e0ebbf2ea793ac672163952b4b5299cf0f3af8e9e983a338b11d39e0404dfcd2274d61a106806c28ed48fcec1e9447b6d84f004eb12b858b76e62b6b6543c91ba121dffd6eebd019dce130821d397fb3b862cdb0d2d852d4d14b6978dd8f2fd1bbc8aca5c6789dbac9c96aabf3b73c005bfbe623a54eaee9d3c452fa81586941388347212c16cf932b99ceb249ecbda94a75198fb1149b580f7ec6446c81d783345414e9e3aacdf559ee3c291849e37f367f1a100";
162    let vote1: Vote =
163        bcs::from_bytes(&data1.from_hex::<Vec<u8>>().unwrap()).unwrap();
164    let vote2: Vote =
165        bcs::from_bytes(&data2.from_hex::<Vec<u8>>().unwrap()).unwrap();
166    assert_ne!(vote1, vote2)
167}