celestia_tendermint/
vote.rs

1//! Votes from validators
2
3mod canonical_vote;
4mod power;
5mod sign_vote;
6mod validator_index;
7
8use core::convert::Infallible;
9use core::{fmt, str::FromStr};
10
11use bytes::BufMut;
12use celestia_tendermint_proto::v0_37::types::{CanonicalVote as RawCanonicalVote, Vote as RawVote};
13use celestia_tendermint_proto::{Error as ProtobufError, Protobuf};
14use serde::{Deserialize, Serialize};
15
16pub use self::{
17    canonical_vote::CanonicalVote, power::Power, sign_vote::*, validator_index::ValidatorIndex,
18};
19use crate::{
20    account, block, chain::Id as ChainId, consensus::State, error::Error, hash, prelude::*,
21    signature::Ed25519Signature, Signature, Time,
22};
23
24/// Votes are signed messages from validators for a particular block which
25/// include information about the validator signing it.
26///
27/// <https://github.com/tendermint/spec/blob/d46cd7f573a2c6a2399fcab2cde981330aa63f37/spec/core/data_structures.md#vote>
28#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
29#[serde(try_from = "RawVote", into = "RawVote")]
30pub struct Vote {
31    /// Type of vote (prevote or precommit)
32    pub vote_type: Type,
33
34    /// Block height
35    pub height: block::Height,
36
37    /// Round
38    pub round: block::Round,
39
40    /// Block ID
41    pub block_id: Option<block::Id>,
42
43    /// Timestamp
44    pub timestamp: Option<Time>,
45
46    /// Validator address
47    pub validator_address: account::Id,
48
49    /// Validator index
50    pub validator_index: ValidatorIndex,
51
52    /// Signature
53    pub signature: Option<Signature>,
54}
55
56tendermint_pb_modules! {
57    use super::Vote;
58    use crate::{prelude::*, block, Error, Signature};
59    use pb::types::Vote as RawVote;
60
61    impl Protobuf<RawVote> for Vote {}
62
63    impl TryFrom<RawVote> for Vote {
64        type Error = Error;
65
66        fn try_from(value: RawVote) -> Result<Self, Self::Error> {
67            if value.timestamp.is_none() {
68                return Err(Error::missing_timestamp());
69            }
70            Ok(Vote {
71                vote_type: value.r#type.try_into()?,
72                height: value.height.try_into()?,
73                round: value.round.try_into()?,
74                // block_id can be nil in the Go implementation
75                block_id: value
76                    .block_id
77                    .map(TryInto::try_into)
78                    .transpose()?
79                    .filter(|i| i != &block::Id::default()),
80                timestamp: value.timestamp.map(|t| t.try_into()).transpose()?,
81                validator_address: value.validator_address.try_into()?,
82                validator_index: value.validator_index.try_into()?,
83                signature: Signature::new(value.signature)?,
84            })
85        }
86    }
87
88    impl From<Vote> for RawVote {
89        fn from(value: Vote) -> Self {
90            RawVote {
91                r#type: value.vote_type.into(),
92                height: value.height.into(),
93                round: value.round.into(),
94                block_id: value.block_id.map(Into::into),
95                timestamp: value.timestamp.map(Into::into),
96                validator_address: value.validator_address.into(),
97                validator_index: value.validator_index.into(),
98                signature: value.signature.map(|s| s.to_bytes()).unwrap_or_default(),
99            }
100        }
101    }
102}
103
104impl Vote {
105    /// Is this vote a prevote?
106    pub fn is_prevote(&self) -> bool {
107        match self.vote_type {
108            Type::Prevote => true,
109            Type::Precommit => false,
110        }
111    }
112
113    /// Is this vote a precommit?
114    pub fn is_precommit(&self) -> bool {
115        match self.vote_type {
116            Type::Precommit => true,
117            Type::Prevote => false,
118        }
119    }
120
121    /// Returns block_id.hash
122    pub fn header_hash(&self) -> Option<hash::Hash> {
123        self.block_id.map(|b| b.hash)
124    }
125
126    /// Create signable bytes from Vote.
127    pub fn to_signable_bytes<B>(
128        &self,
129        chain_id: ChainId,
130        sign_bytes: &mut B,
131    ) -> Result<bool, ProtobufError>
132    where
133        B: BufMut,
134    {
135        let canonical = CanonicalVote::new(self.clone(), chain_id);
136        Protobuf::<RawCanonicalVote>::encode_length_delimited(&canonical, sign_bytes)?;
137        Ok(true)
138    }
139
140    /// Create signable vector from Vote.
141    pub fn to_signable_vec(&self, chain_id: ChainId) -> Result<Vec<u8>, ProtobufError> {
142        let canonical = CanonicalVote::new(self.clone(), chain_id);
143        let encoded: Result<_, Infallible> =
144            Protobuf::<RawCanonicalVote>::encode_length_delimited_vec(&canonical);
145        Ok(encoded.unwrap())
146    }
147
148    /// Consensus state from this vote - This doesn't seem to be used anywhere.
149    #[deprecated(
150        since = "0.17.0",
151        note = "This seems unnecessary, please raise it to the team, if you need it."
152    )]
153    pub fn consensus_state(&self) -> State {
154        State {
155            height: self.height,
156            round: self.round,
157            step: 6,
158            block_id: self.block_id,
159        }
160    }
161}
162
163/// Default trait. Used in tests.
164// FIXME: Does it need to be in public crate API? If not, replace with a helper fn in crate::test?
165impl Default for Vote {
166    fn default() -> Self {
167        Vote {
168            vote_type: Type::Prevote,
169            height: Default::default(),
170            round: Default::default(),
171            block_id: None,
172            timestamp: Some(Time::unix_epoch()),
173            validator_address: account::Id::new([0; account::LENGTH]),
174            validator_index: ValidatorIndex::try_from(0_i32).unwrap(),
175            // Could have reused crate::test::dummy_signature, except that
176            // this Default impl is defined outside of #[cfg(test)].
177            signature: Some(Signature::from(Ed25519Signature::from_bytes(
178                &[0; Ed25519Signature::BYTE_SIZE],
179            ))),
180        }
181    }
182}
183/// SignedVote is the union of a canonicalized vote, the signature on
184/// the sign bytes of that vote and the id of the validator who signed it.
185pub struct SignedVote {
186    vote: CanonicalVote,
187    validator_address: account::Id,
188    signature: Signature,
189}
190
191impl SignedVote {
192    /// Create new `SignedVote` from provided canonicalized vote, validator id, and
193    /// the signature of that validator.
194    pub fn new(
195        vote: Vote,
196        chain_id: ChainId,
197        validator_address: account::Id,
198        signature: Signature,
199    ) -> SignedVote {
200        let canonical_vote = CanonicalVote::new(vote, chain_id);
201        SignedVote {
202            vote: canonical_vote,
203            signature,
204            validator_address,
205        }
206    }
207
208    /// Create a new `SignedVote` from the provided `Vote`, which may or may not be signed.
209    /// If the vote is not signed, this function will return `None`.
210    pub fn from_vote(vote: Vote, chain_id: ChainId) -> Option<Self> {
211        let validator_address = vote.validator_address;
212        vote.signature
213            .clone()
214            .map(|signature| Self::new(vote, chain_id, validator_address, signature))
215    }
216
217    /// Return the id of the validator that signed this vote.
218    pub fn validator_id(&self) -> account::Id {
219        self.validator_address
220    }
221
222    /// Return the bytes (of the canonicalized vote) that were signed.
223    pub fn sign_bytes(&self) -> Vec<u8> {
224        Protobuf::<RawCanonicalVote>::encode_length_delimited_vec(&self.vote).unwrap()
225    }
226
227    /// Return the actual signature on the canonicalized vote.
228    pub fn signature(&self) -> &Signature {
229        &self.signature
230    }
231}
232
233/// Types of votes
234#[repr(u8)]
235#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
236pub enum Type {
237    /// Votes for blocks which validators observe are valid for a given round
238    Prevote = 1,
239
240    /// Votes to commit to a particular block for a given round
241    Precommit = 2,
242}
243
244impl Protobuf<i32> for Type {}
245
246impl TryFrom<i32> for Type {
247    type Error = Error;
248
249    fn try_from(value: i32) -> Result<Self, Self::Error> {
250        match value {
251            1 => Ok(Type::Prevote),
252            2 => Ok(Type::Precommit),
253            _ => Err(Error::invalid_message_type()),
254        }
255    }
256}
257
258impl From<Type> for i32 {
259    fn from(value: Type) -> Self {
260        value as i32
261    }
262}
263
264impl fmt::Display for Type {
265    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266        let id = match self {
267            Type::Prevote => "Prevote",
268            Type::Precommit => "Precommit",
269        };
270        write!(f, "{id}")
271    }
272}
273
274impl FromStr for Type {
275    type Err = Error;
276
277    fn from_str(s: &str) -> Result<Self, Self::Err> {
278        match s {
279            "Prevote" => Ok(Self::Prevote),
280            "Precommit" => Ok(Self::Precommit),
281            _ => Err(Error::invalid_message_type()),
282        }
283    }
284}