1use serde::{Deserialize, Serialize};
2
3use crate::block::BlockHash;
4use crate::crypto::Signature;
5use crate::epoch::EpochNumber;
6use crate::validator::ValidatorId;
7use crate::view::ViewNumber;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub enum VoteType {
11 Vote,
13 Vote2,
15}
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct Vote {
19 pub block_hash: BlockHash,
20 pub view: ViewNumber,
21 pub validator: ValidatorId,
22 pub signature: Signature,
23 pub vote_type: VoteType,
24 #[serde(default, skip_serializing_if = "Option::is_none")]
27 pub extension: Option<Vec<u8>>,
28}
29
30impl Vote {
31 pub fn signing_bytes(
36 chain_id_hash: &[u8; 32],
37 epoch: EpochNumber,
38 view: ViewNumber,
39 block_hash: &BlockHash,
40 vote_type: VoteType,
41 ) -> Vec<u8> {
42 let tag = b"HOTMINT_VOTE_V2\0";
43 let mut buf = Vec::with_capacity(tag.len() + 32 + 8 + 8 + 32 + 1);
44 buf.extend_from_slice(tag);
45 buf.extend_from_slice(chain_id_hash);
46 buf.extend_from_slice(&epoch.as_u64().to_le_bytes());
47 buf.extend_from_slice(&view.as_u64().to_le_bytes());
48 buf.extend_from_slice(&block_hash.0);
49 buf.push(vote_type as u8);
50 buf
51 }
52}
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57 use crate::epoch::EpochNumber;
58
59 const TEST_CHAIN: [u8; 32] = [0u8; 32];
60
61 #[test]
62 fn test_signing_bytes_deterministic() {
63 let hash = BlockHash([42u8; 32]);
64 let a = Vote::signing_bytes(
65 &TEST_CHAIN,
66 EpochNumber(0),
67 ViewNumber(5),
68 &hash,
69 VoteType::Vote,
70 );
71 let b = Vote::signing_bytes(
72 &TEST_CHAIN,
73 EpochNumber(0),
74 ViewNumber(5),
75 &hash,
76 VoteType::Vote,
77 );
78 assert_eq!(a, b);
79 }
80
81 #[test]
82 fn test_signing_bytes_differ_by_type() {
83 let hash = BlockHash([1u8; 32]);
84 let a = Vote::signing_bytes(
85 &TEST_CHAIN,
86 EpochNumber(0),
87 ViewNumber(1),
88 &hash,
89 VoteType::Vote,
90 );
91 let b = Vote::signing_bytes(
92 &TEST_CHAIN,
93 EpochNumber(0),
94 ViewNumber(1),
95 &hash,
96 VoteType::Vote2,
97 );
98 assert_ne!(a, b);
99 }
100
101 #[test]
102 fn test_signing_bytes_differ_by_view() {
103 let hash = BlockHash([1u8; 32]);
104 let a = Vote::signing_bytes(
105 &TEST_CHAIN,
106 EpochNumber(0),
107 ViewNumber(1),
108 &hash,
109 VoteType::Vote,
110 );
111 let b = Vote::signing_bytes(
112 &TEST_CHAIN,
113 EpochNumber(0),
114 ViewNumber(2),
115 &hash,
116 VoteType::Vote,
117 );
118 assert_ne!(a, b);
119 }
120
121 #[test]
122 fn test_signing_bytes_differ_by_chain() {
123 let hash = BlockHash([1u8; 32]);
124 let chain_a = [1u8; 32];
125 let chain_b = [2u8; 32];
126 let a = Vote::signing_bytes(
127 &chain_a,
128 EpochNumber(0),
129 ViewNumber(1),
130 &hash,
131 VoteType::Vote,
132 );
133 let b = Vote::signing_bytes(
134 &chain_b,
135 EpochNumber(0),
136 ViewNumber(1),
137 &hash,
138 VoteType::Vote,
139 );
140 assert_ne!(a, b);
141 }
142
143 #[test]
144 fn test_signing_bytes_differ_by_epoch() {
145 let hash = BlockHash([1u8; 32]);
146 let a = Vote::signing_bytes(
147 &TEST_CHAIN,
148 EpochNumber(0),
149 ViewNumber(1),
150 &hash,
151 VoteType::Vote,
152 );
153 let b = Vote::signing_bytes(
154 &TEST_CHAIN,
155 EpochNumber(1),
156 ViewNumber(1),
157 &hash,
158 VoteType::Vote,
159 );
160 assert_ne!(a, b);
161 }
162}