1use bytes::BufMut;
2use celestia_core_proto::Error as ProtobufError;
3
4use crate::{chain, prelude::*, privval::RemoteSignerError, Vote};
5
6#[derive(Clone, PartialEq, Eq, Debug)]
8pub struct SignVoteRequest {
9 pub vote: Vote,
11 pub chain_id: chain::Id,
13}
14
15impl SignVoteRequest {
16 pub fn to_signable_bytes<B>(&self, sign_bytes: &mut B) -> Result<bool, ProtobufError>
18 where
19 B: BufMut,
20 {
21 self.vote
22 .to_signable_bytes(self.chain_id.clone(), sign_bytes)
23 }
24
25 pub fn to_signable_vec(&self) -> Result<Vec<u8>, ProtobufError> {
27 self.vote.to_signable_vec(self.chain_id.clone())
28 }
29}
30
31#[derive(Clone, PartialEq, Eq, Debug)]
33pub struct SignedVoteResponse {
34 pub vote: Option<Vote>,
36 pub error: Option<RemoteSignerError>,
38}
39
40tendermint_pb_modules! {
45 use super::{SignVoteRequest, SignedVoteResponse};
46 use crate::{Error, prelude::*};
47 use pb::privval::{
48 SignVoteRequest as RawSignVoteRequest, SignedVoteResponse as RawSignedVoteResponse,
49 };
50
51 impl Protobuf<RawSignVoteRequest> for SignVoteRequest {}
52
53 impl TryFrom<RawSignVoteRequest> for SignVoteRequest {
54 type Error = Error;
55
56 fn try_from(value: RawSignVoteRequest) -> Result<Self, Self::Error> {
57 let vote = value.vote.ok_or_else(Error::no_vote_found)?.try_into()?;
58
59 let chain_id = value.chain_id.try_into()?;
60
61 Ok(SignVoteRequest { vote, chain_id })
62 }
63 }
64
65 impl From<SignVoteRequest> for RawSignVoteRequest {
66 fn from(value: SignVoteRequest) -> Self {
67 RawSignVoteRequest {
68 vote: Some(value.vote.into()),
69 chain_id: value.chain_id.as_str().to_owned(),
70 }
71 }
72 }
73
74 impl Protobuf<RawSignedVoteResponse> for SignedVoteResponse {}
75
76 impl TryFrom<RawSignedVoteResponse> for SignedVoteResponse {
77 type Error = Error;
78
79 fn try_from(value: RawSignedVoteResponse) -> Result<Self, Self::Error> {
80 Ok(SignedVoteResponse {
81 vote: value.vote.map(TryFrom::try_from).transpose()?,
82 error: value.error.map(TryFrom::try_from).transpose()?,
83 })
84 }
85 }
86
87 impl From<SignedVoteResponse> for RawSignedVoteResponse {
88 fn from(value: SignedVoteResponse) -> Self {
89 RawSignedVoteResponse {
90 vote: value.vote.map(Into::into),
91 error: value.error.map(Into::into),
92 }
93 }
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use core::{
100 convert::{TryFrom, TryInto},
101 str::FromStr,
102 };
103 use std::println;
104
105 use time::macros::datetime;
106
107 use crate::{
108 account::Id as AccountId,
109 block::{parts::Header, Height, Id as BlockId, Round},
110 chain::Id as ChainId,
111 hash::Algorithm,
112 prelude::*,
113 signature::{Ed25519Signature, Signature},
114 vote::{CanonicalVote, SignVoteRequest, Type, ValidatorIndex},
115 Hash, Vote,
116 };
117
118 #[test]
119 fn test_vote_serialization() {
120 let dt = datetime!(2017-12-25 03:00:01.234 UTC);
121 let vote = Vote {
122 vote_type: Type::Prevote,
123 height: Height::from(12345_u32),
124 round: Round::from(2_u16),
125 timestamp: Some(dt.try_into().unwrap()),
126 block_id: Some(BlockId {
127 hash: Hash::try_from(b"DEADBEEFDEADBEEFBAFBAFBAFBAFBAFA".to_vec()).unwrap(),
128 part_set_header: Header::new(
129 1_000_000,
130 Hash::try_from(b"0022446688AACCEE1133557799BBDDFF".to_vec()).unwrap(),
131 )
132 .unwrap(),
133 }),
134 validator_address: AccountId::try_from(vec![
135 0xa3, 0xb2, 0xcc, 0xdd, 0x71, 0x86, 0xf1, 0x68, 0x5f, 0x21, 0xf2, 0x48, 0x2a, 0xf4,
136 0xfb, 0x34, 0x46, 0xa8, 0x4b, 0x35,
137 ])
138 .unwrap(),
139 validator_index: ValidatorIndex::try_from(56789).unwrap(),
140 signature: Signature::new(vec![
141 130u8, 246, 183, 50, 153, 248, 28, 57, 51, 142, 55, 217, 194, 24, 134, 212, 233,
142 100, 211, 10, 24, 174, 179, 117, 41, 65, 141, 134, 149, 239, 65, 174, 217, 42, 6,
143 184, 112, 17, 7, 97, 255, 221, 252, 16, 60, 144, 30, 212, 167, 39, 67, 35, 118,
144 192, 133, 130, 193, 115, 32, 206, 152, 91, 173, 10,
145 ])
146 .unwrap(),
147 };
148
149 let mut got = vec![];
150
151 let request = SignVoteRequest {
152 vote,
153 chain_id: ChainId::from_str("test_chain_id").unwrap(),
154 };
155
156 let _have = request.to_signable_bytes(&mut got);
158 let got2 = request.to_signable_vec().unwrap();
160
161 let want = vec![
190 124, 8, 1, 17, 57, 48, 0, 0, 0, 0, 0, 0, 25, 2, 0, 0, 0, 0, 0, 0, 0, 34, 74, 10, 32,
191 68, 69, 65, 68, 66, 69, 69, 70, 68, 69, 65, 68, 66, 69, 69, 70, 66, 65, 70, 66, 65, 70,
192 66, 65, 70, 66, 65, 70, 66, 65, 70, 65, 18, 38, 8, 192, 132, 61, 18, 32, 48, 48, 50,
193 50, 52, 52, 54, 54, 56, 56, 65, 65, 67, 67, 69, 69, 49, 49, 51, 51, 53, 53, 55, 55, 57,
194 57, 66, 66, 68, 68, 70, 70, 42, 11, 8, 177, 211, 129, 210, 5, 16, 128, 157, 202, 111,
195 50, 13, 116, 101, 115, 116, 95, 99, 104, 97, 105, 110, 95, 105, 100,
196 ];
197 assert_eq!(got, want);
198 assert_eq!(got2, want);
199 }
200
201 #[test]
202 fn test_vote_encoding_with_empty_block_id() {
204 let dt = datetime!(2017-12-25 03:00:01.234 UTC);
205 let vote = Vote {
206 vote_type: Type::Prevote,
207 height: Height::from(12345_u32),
208 round: Round::from(2_u16),
209 timestamp: Some(dt.try_into().unwrap()),
210 block_id: Some(BlockId {
211 hash: Hash::try_from(b"".to_vec()).unwrap(),
212 part_set_header: Header::new(
213 1_000_000,
214 Hash::try_from(b"0022446688AACCEE1133557799BBDDFF".to_vec()).unwrap(),
215 )
216 .unwrap(),
217 }),
218 validator_address: AccountId::try_from(vec![
219 0xa3, 0xb2, 0xcc, 0xdd, 0x71, 0x86, 0xf1, 0x68, 0x5f, 0x21, 0xf2, 0x48, 0x2a, 0xf4,
220 0xfb, 0x34, 0x46, 0xa8, 0x4b, 0x35,
221 ])
222 .unwrap(),
223 validator_index: ValidatorIndex::try_from(56789).unwrap(),
224 signature: Signature::new(vec![
225 130u8, 246, 183, 50, 153, 248, 28, 57, 51, 142, 55, 217, 194, 24, 134, 212, 233,
226 100, 211, 10, 24, 174, 179, 117, 41, 65, 141, 134, 149, 239, 65, 174, 217, 42, 6,
227 184, 112, 17, 7, 97, 255, 221, 252, 16, 60, 144, 30, 212, 167, 39, 67, 35, 118,
228 192, 133, 130, 193, 115, 32, 206, 152, 91, 173, 10,
229 ])
230 .unwrap(),
231 };
232
233 let request = SignVoteRequest {
234 vote,
235 chain_id: ChainId::from_str("test_chain_id").unwrap(),
236 };
237
238 let got = request.to_signable_vec().unwrap();
239
240 let want = vec![
269 90, 8, 1, 17, 57, 48, 0, 0, 0, 0, 0, 0, 25, 2, 0, 0, 0, 0, 0, 0, 0, 34, 40, 18, 38, 8,
270 192, 132, 61, 18, 32, 48, 48, 50, 50, 52, 52, 54, 54, 56, 56, 65, 65, 67, 67, 69, 69,
271 49, 49, 51, 51, 53, 53, 55, 55, 57, 57, 66, 66, 68, 68, 70, 70, 42, 11, 8, 177, 211,
272 129, 210, 5, 16, 128, 157, 202, 111, 50, 13, 116, 101, 115, 116, 95, 99, 104, 97, 105,
273 110, 95, 105, 100,
274 ];
275 assert_eq!(got, want);
276 }
277
278 tendermint_pb_modules! {
279 use super::*;
280 use pb::types::CanonicalVote as RawCanonicalVote;
281
282 #[test]
283 fn test_sign_bytes_compatibility() {
284 let cv = CanonicalVote::new(Vote::default(), ChainId::try_from("A").unwrap());
285 let mut got = vec![];
286 Protobuf::<RawCanonicalVote>::encode_length_delimited(&cv, &mut got).unwrap();
288 let want = vec![
289 0x10, 0x8, 0x1, 0x11, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x32, 0x1,
290 0x41,
291 ]; assert_eq!(got, want);
293
294 {
296 let vt_precommit = Vote {
297 height: Height::from(1_u32),
298 round: Round::from(1_u16),
299 vote_type: Type::Precommit,
300 ..Default::default()
301 };
302 println!("{vt_precommit:?}");
303 let cv_precommit = CanonicalVote::new(vt_precommit, ChainId::try_from("A").unwrap());
304 let got = Protobuf::<RawCanonicalVote>::encode_vec(&cv_precommit).unwrap();
305 let want = vec![
306 0x8, 0x2, 0x11, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x19, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x32, 0x1, 0x41,
317 ];
318 assert_eq!(got, want);
319 }
320 {
322 let vt_prevote = Vote {
323 height: Height::from(1_u32),
324 round: Round::from(1_u16),
325 vote_type: Type::Prevote,
326 ..Default::default()
327 };
328
329 let cv_prevote = CanonicalVote::new(vt_prevote, ChainId::try_from("A").unwrap());
330
331 let got = Protobuf::<RawCanonicalVote>::encode_vec(&cv_prevote).unwrap();
332
333 let want = vec![
334 0x8, 0x1, 0x11, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x19, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x32, 0x1, 0x41,
345 ];
346 assert_eq!(got, want);
347 }
348 }
349
350 #[test]
351 fn test_deserialization() {
352 let encoded = vec![
353 10, 188, 1, 8, 1, 16, 185, 96, 24, 2, 34, 74, 10, 32, 222, 173, 190, 239, 222, 173,
354 190, 239, 186, 251, 175, 186, 251, 175, 186, 250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
355 0, 0, 0, 0, 18, 38, 8, 192, 132, 61, 18, 32, 0, 34, 68, 102, 136, 170, 204, 238, 17,
356 51, 85, 119, 153, 187, 221, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42,
357 11, 8, 177, 211, 129, 210, 5, 16, 128, 157, 202, 111, 50, 20, 163, 178, 204, 221, 113,
358 134, 241, 104, 95, 33, 242, 72, 42, 244, 251, 52, 70, 168, 75, 53, 56, 213, 187, 3, 66,
359 64, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
360 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
361 1, 1, 1, 1, 1, 1, 1, 18, 13, 116, 101, 115, 116, 95, 99, 104, 97, 105, 110, 95, 105,
362 100,
363 ]; let dt = datetime!(2017-12-25 03:00:01.234 UTC);
365 let vote = Vote {
366 validator_address: AccountId::try_from(vec![
367 0xa3, 0xb2, 0xcc, 0xdd, 0x71, 0x86, 0xf1, 0x68, 0x5f, 0x21, 0xf2, 0x48, 0x2a, 0xf4,
368 0xfb, 0x34, 0x46, 0xa8, 0x4b, 0x35,
369 ])
370 .unwrap(),
371 validator_index: ValidatorIndex::try_from(56789).unwrap(),
372 height: Height::from(12345_u32),
373 round: Round::from(2_u16),
374 timestamp: Some(dt.try_into().unwrap()),
375 vote_type: Type::Prevote,
376 block_id: Some(BlockId {
377 hash: Hash::from_hex_upper(Algorithm::Sha256, "DEADBEEFDEADBEEFBAFBAFBAFBAFBAFA")
378 .unwrap(),
379 part_set_header: Header::new(
380 1_000_000,
381 Hash::from_hex_upper(Algorithm::Sha256, "0022446688AACCEE1133557799BBDDFF")
382 .unwrap(),
383 )
384 .unwrap(),
385 }),
386 signature: Signature::new(vec![1; Ed25519Signature::BYTE_SIZE]).unwrap(),
387 };
388 let want = SignVoteRequest {
389 vote,
390 chain_id: ChainId::from_str("test_chain_id").unwrap(),
391 };
392 let got = <SignVoteRequest as Protobuf<pb::privval::SignVoteRequest>>::decode_vec(
393 &encoded
394 ).unwrap();
395 assert_eq!(got, want);
396 }
397
398 #[test]
399 fn test_vote_rountrip_with_sig() {
400 let dt = datetime!(2017-12-25 03:00:01.234 UTC);
401 let vote = Vote {
402 validator_address: AccountId::try_from(vec![
403 0xa3, 0xb2, 0xcc, 0xdd, 0x71, 0x86, 0xf1, 0x68, 0x5f, 0x21, 0xf2, 0x48, 0x2a, 0xf4,
404 0xfb, 0x34, 0x46, 0xa8, 0x4b, 0x35,
405 ])
406 .unwrap(),
407 validator_index: ValidatorIndex::try_from(56789).unwrap(),
408 height: Height::from(12345_u32),
409 round: Round::from(2_u16),
410 timestamp: Some(dt.try_into().unwrap()),
411 vote_type: Type::Prevote,
412 block_id: Some(BlockId {
413 hash: Hash::from_hex_upper(Algorithm::Sha256, "DEADBEEFDEADBEEFBAFBAFBAFBAFBAFA")
414 .unwrap(), part_set_header: Header::new(
417 1_000_000,
418 Hash::from_hex_upper(Algorithm::Sha256, "DEADBEEFDEADBEEFBAFBAFBAFBAFBAFA")
419 .unwrap(),
420 )
421 .unwrap(),
422 }),
423 signature: Signature::new(vec![
425 130u8, 246, 183, 50, 153, 248, 28, 57, 51, 142, 55, 217, 194, 24, 134, 212, 233,
426 100, 211, 10, 24, 174, 179, 117, 41, 65, 141, 134, 149, 239, 65, 174, 217, 42, 6,
427 184, 112, 17, 7, 97, 255, 221, 252, 16, 60, 144, 30, 212, 167, 39, 67, 35, 118,
428 192, 133, 130, 193, 115, 32, 206, 152, 91, 173, 10,
429 ])
430 .unwrap(),
431 };
432 let got = Protobuf::<pb::types::Vote>::encode_vec(&vote).unwrap();
433 let v = <Vote as Protobuf::<pb::types::Vote>>::decode_vec(&got).unwrap();
434
435 assert_eq!(v, vote);
436 {
438 let svr = SignVoteRequest {
439 vote,
440 chain_id: ChainId::from_str("test_chain_id").unwrap(),
441 };
442 let mut got = vec![];
443 let _have = Protobuf::<pb::privval::SignVoteRequest>::encode(&svr, &mut got);
444
445 let svr2 = <SignVoteRequest as Protobuf<pb::privval::SignVoteRequest>>::decode(
446 got.as_ref()
447 ).unwrap();
448 assert_eq!(svr, svr2);
449 }
450 }
451 }
452}