1use bytes::BufMut;
2use tendermint_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 into_signable_vec(self) -> Vec<u8> {
27 self.vote.into_signable_vec(self.chain_id)
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::str::FromStr;
100 use std::println;
101
102 use time::macros::datetime;
103
104 use crate::{
105 account::Id as AccountId,
106 block::{parts::Header, Height, Id as BlockId, Round},
107 chain::Id as ChainId,
108 hash::Algorithm,
109 prelude::*,
110 signature::Signature,
111 vote::{CanonicalVote, SignVoteRequest, Type, ValidatorIndex},
112 Hash, Vote,
113 };
114
115 #[test]
116 fn test_vote_serialization() {
117 let dt = datetime!(2017-12-25 03:00:01.234 UTC);
118 let vote = Vote {
119 vote_type: Type::Prevote,
120 height: Height::from(12345_u32),
121 round: Round::from(2_u16),
122 timestamp: Some(dt.try_into().unwrap()),
123 block_id: Some(BlockId {
124 hash: Hash::try_from(b"DEADBEEFDEADBEEFBAFBAFBAFBAFBAFA".to_vec()).unwrap(),
125 part_set_header: Header::new(
126 1_000_000,
127 Hash::try_from(b"0022446688AACCEE1133557799BBDDFF".to_vec()).unwrap(),
128 )
129 .unwrap(),
130 }),
131 validator_address: AccountId::try_from(vec![
132 0xa3, 0xb2, 0xcc, 0xdd, 0x71, 0x86, 0xf1, 0x68, 0x5f, 0x21, 0xf2, 0x48, 0x2a, 0xf4,
133 0xfb, 0x34, 0x46, 0xa8, 0x4b, 0x35,
134 ])
135 .unwrap(),
136 validator_index: ValidatorIndex::try_from(56789).unwrap(),
137 signature: Signature::new(vec![
138 130u8, 246, 183, 50, 153, 248, 28, 57, 51, 142, 55, 217, 194, 24, 134, 212, 233,
139 100, 211, 10, 24, 174, 179, 117, 41, 65, 141, 134, 149, 239, 65, 174, 217, 42, 6,
140 184, 112, 17, 7, 97, 255, 221, 252, 16, 60, 144, 30, 212, 167, 39, 67, 35, 118,
141 192, 133, 130, 193, 115, 32, 206, 152, 91, 173, 10,
142 ])
143 .unwrap(),
144 extension: vec![],
146 extension_signature: None,
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.into_signable_vec();
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 extension: vec![],
233 extension_signature: None,
234 };
235
236 let request = SignVoteRequest {
237 vote,
238 chain_id: ChainId::from_str("test_chain_id").unwrap(),
239 };
240
241 let got = request.into_signable_vec();
242
243 let want = vec![
272 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,
273 192, 132, 61, 18, 32, 48, 48, 50, 50, 52, 52, 54, 54, 56, 56, 65, 65, 67, 67, 69, 69,
274 49, 49, 51, 51, 53, 53, 55, 55, 57, 57, 66, 66, 68, 68, 70, 70, 42, 11, 8, 177, 211,
275 129, 210, 5, 16, 128, 157, 202, 111, 50, 13, 116, 101, 115, 116, 95, 99, 104, 97, 105,
276 110, 95, 105, 100,
277 ];
278 assert_eq!(got, want);
279 }
280
281 tendermint_pb_modules! {
282 use super::*;
283 use pb::types::CanonicalVote as RawCanonicalVote;
284 use crate::{Time, account, signature::Ed25519Signature};
285
286 pub fn dummy_vote() -> Vote {
288 Vote {
289 vote_type: Type::Prevote,
290 height: Default::default(),
291 round: Default::default(),
292 block_id: None,
293 timestamp: Some(Time::unix_epoch()),
294 validator_address: account::Id::new([0; account::LENGTH]),
295 validator_index: ValidatorIndex::try_from(0_i32).unwrap(),
296 signature: Some(Signature::from(Ed25519Signature::from_bytes(
299 &[0; Ed25519Signature::BYTE_SIZE],
300 ))),
301 extension: Default::default(),
302 extension_signature: None,
303 }
304 }
305
306 #[test]
307 fn test_sign_bytes_compatibility() {
308 let cv = CanonicalVote::new(dummy_vote(), ChainId::try_from("A").unwrap());
309 let mut got = vec![];
310 Protobuf::<RawCanonicalVote>::encode_length_delimited(cv, &mut got).unwrap();
312 let want = vec![
313 0x10, 0x8, 0x1, 0x11, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x32, 0x1,
314 0x41,
315 ]; assert_eq!(got, want);
317
318 {
320 let vt_precommit = Vote {
321 height: Height::from(1_u32),
322 round: Round::from(1_u16),
323 vote_type: Type::Precommit,
324 ..dummy_vote()
325 };
326 println!("{vt_precommit:?}");
327 let cv_precommit = CanonicalVote::new(vt_precommit, ChainId::try_from("A").unwrap());
328 let got = Protobuf::<RawCanonicalVote>::encode_vec(cv_precommit);
329 let want = vec![
330 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,
341 ];
342 assert_eq!(got, want);
343 }
344 {
346 let vt_prevote = Vote {
347 height: Height::from(1_u32),
348 round: Round::from(1_u16),
349 vote_type: Type::Prevote,
350 ..dummy_vote()
351 };
352
353 let cv_prevote = CanonicalVote::new(vt_prevote, ChainId::try_from("A").unwrap());
354
355 let got = Protobuf::<RawCanonicalVote>::encode_vec(cv_prevote);
356
357 let want = vec![
358 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,
369 ];
370 assert_eq!(got, want);
371 }
372 }
373
374 #[test]
375 fn test_deserialization() {
376 let encoded = vec![
377 10, 188, 1, 8, 1, 16, 185, 96, 24, 2, 34, 74, 10, 32, 222, 173, 190, 239, 222, 173,
378 190, 239, 186, 251, 175, 186, 251, 175, 186, 250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
379 0, 0, 0, 0, 18, 38, 8, 192, 132, 61, 18, 32, 0, 34, 68, 102, 136, 170, 204, 238, 17,
380 51, 85, 119, 153, 187, 221, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42,
381 11, 8, 177, 211, 129, 210, 5, 16, 128, 157, 202, 111, 50, 20, 163, 178, 204, 221, 113,
382 134, 241, 104, 95, 33, 242, 72, 42, 244, 251, 52, 70, 168, 75, 53, 56, 213, 187, 3, 66,
383 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,
384 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,
385 1, 1, 1, 1, 1, 1, 1, 18, 13, 116, 101, 115, 116, 95, 99, 104, 97, 105, 110, 95, 105,
386 100,
387 ]; let dt = datetime!(2017-12-25 03:00:01.234 UTC);
389 let vote = Vote {
390 validator_address: AccountId::try_from(vec![
391 0xa3, 0xb2, 0xcc, 0xdd, 0x71, 0x86, 0xf1, 0x68, 0x5f, 0x21, 0xf2, 0x48, 0x2a, 0xf4,
392 0xfb, 0x34, 0x46, 0xa8, 0x4b, 0x35,
393 ])
394 .unwrap(),
395 validator_index: ValidatorIndex::try_from(56789).unwrap(),
396 height: Height::from(12345_u32),
397 round: Round::from(2_u16),
398 timestamp: Some(dt.try_into().unwrap()),
399 vote_type: Type::Prevote,
400 block_id: Some(BlockId {
401 hash: Hash::from_hex_upper(Algorithm::Sha256, "DEADBEEFDEADBEEFBAFBAFBAFBAFBAFA")
402 .unwrap(),
403 part_set_header: Header::new(
404 1_000_000,
405 Hash::from_hex_upper(Algorithm::Sha256, "0022446688AACCEE1133557799BBDDFF")
406 .unwrap(),
407 )
408 .unwrap(),
409 }),
410 signature: Signature::new(vec![1; Ed25519Signature::BYTE_SIZE]).unwrap(),
411 extension: vec![],
413 extension_signature: None,
414 };
415 let want = SignVoteRequest {
416 vote,
417 chain_id: ChainId::from_str("test_chain_id").unwrap(),
418 };
419 let got = <SignVoteRequest as Protobuf<pb::privval::SignVoteRequest>>::decode_vec(
420 &encoded
421 ).unwrap();
422 assert_eq!(got, want);
423 }
424
425 #[test]
426 fn test_vote_rountrip_with_sig() {
427 let dt = datetime!(2017-12-25 03:00:01.234 UTC);
428 let vote = Vote {
429 validator_address: AccountId::try_from(vec![
430 0xa3, 0xb2, 0xcc, 0xdd, 0x71, 0x86, 0xf1, 0x68, 0x5f, 0x21, 0xf2, 0x48, 0x2a, 0xf4,
431 0xfb, 0x34, 0x46, 0xa8, 0x4b, 0x35,
432 ])
433 .unwrap(),
434 validator_index: ValidatorIndex::try_from(56789).unwrap(),
435 height: Height::from(12345_u32),
436 round: Round::from(2_u16),
437 timestamp: Some(dt.try_into().unwrap()),
438 vote_type: Type::Prevote,
439 block_id: Some(BlockId {
440 hash: Hash::from_hex_upper(Algorithm::Sha256, "DEADBEEFDEADBEEFBAFBAFBAFBAFBAFA")
441 .unwrap(), part_set_header: Header::new(
444 1_000_000,
445 Hash::from_hex_upper(Algorithm::Sha256, "DEADBEEFDEADBEEFBAFBAFBAFBAFBAFA")
446 .unwrap(),
447 )
448 .unwrap(),
449 }),
450 signature: Signature::new(vec![
452 130u8, 246, 183, 50, 153, 248, 28, 57, 51, 142, 55, 217, 194, 24, 134, 212, 233,
453 100, 211, 10, 24, 174, 179, 117, 41, 65, 141, 134, 149, 239, 65, 174, 217, 42, 6,
454 184, 112, 17, 7, 97, 255, 221, 252, 16, 60, 144, 30, 212, 167, 39, 67, 35, 118,
455 192, 133, 130, 193, 115, 32, 206, 152, 91, 173, 10,
456 ])
457 .unwrap(),
458 extension: vec![],
460 extension_signature: None,
461 };
462 let got = Protobuf::<pb::types::Vote>::encode_vec(vote.clone());
463 let v = <Vote as Protobuf::<pb::types::Vote>>::decode_vec(&got).unwrap();
464
465 assert_eq!(v, vote);
466 {
468 let svr = SignVoteRequest {
469 vote,
470 chain_id: ChainId::from_str("test_chain_id").unwrap(),
471 };
472 let mut got = vec![];
473 let _have = Protobuf::<pb::privval::SignVoteRequest>::encode(svr.clone(), &mut got);
474
475 let svr2 = <SignVoteRequest as Protobuf<pb::privval::SignVoteRequest>>::decode(
476 got.as_ref()
477 ).unwrap();
478 assert_eq!(svr, svr2);
479 }
480 }
481 }
482}