1mod 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#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
29#[serde(try_from = "RawVote", into = "RawVote")]
30pub struct Vote {
31 pub vote_type: Type,
33
34 pub height: block::Height,
36
37 pub round: block::Round,
39
40 pub block_id: Option<block::Id>,
42
43 pub timestamp: Option<Time>,
45
46 pub validator_address: account::Id,
48
49 pub validator_index: ValidatorIndex,
51
52 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: 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 pub fn is_prevote(&self) -> bool {
107 match self.vote_type {
108 Type::Prevote => true,
109 Type::Precommit => false,
110 }
111 }
112
113 pub fn is_precommit(&self) -> bool {
115 match self.vote_type {
116 Type::Precommit => true,
117 Type::Prevote => false,
118 }
119 }
120
121 pub fn header_hash(&self) -> Option<hash::Hash> {
123 self.block_id.map(|b| b.hash)
124 }
125
126 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 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 #[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
163impl 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 signature: Some(Signature::from(Ed25519Signature::from_bytes(
178 &[0; Ed25519Signature::BYTE_SIZE],
179 ))),
180 }
181 }
182}
183pub struct SignedVote {
186 vote: CanonicalVote,
187 validator_address: account::Id,
188 signature: Signature,
189}
190
191impl SignedVote {
192 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 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 pub fn validator_id(&self) -> account::Id {
219 self.validator_address
220 }
221
222 pub fn sign_bytes(&self) -> Vec<u8> {
224 Protobuf::<RawCanonicalVote>::encode_length_delimited_vec(&self.vote).unwrap()
225 }
226
227 pub fn signature(&self) -> &Signature {
229 &self.signature
230 }
231}
232
233#[repr(u8)]
235#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
236pub enum Type {
237 Prevote = 1,
239
240 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}