1use serde::{Deserialize, Serialize, Serializer, ser::SerializeTuple};
2use std::cmp::Ordering;
3use std::hash::Hash;
4
5use crate::{RegisterError, RegistrationEntry, Stake, StmResult, VerificationKeyForConcatenation};
6
7#[cfg(feature = "future_snark")]
8use crate::{LotteryTargetValue, VerificationKeyForSnark};
9
10#[derive(PartialEq, Eq, Clone, Debug, Copy, Deserialize)]
12pub struct ClosedRegistrationEntry {
13 verification_key_for_concatenation: VerificationKeyForConcatenation,
14 stake: Stake,
15 #[cfg(feature = "future_snark")]
16 #[serde(skip_serializing_if = "Option::is_none", default)]
17 verification_key_for_snark: Option<VerificationKeyForSnark>,
18 #[cfg(feature = "future_snark")]
19 #[serde(skip_serializing_if = "Option::is_none", default)]
20 lottery_target_value: Option<LotteryTargetValue>,
21}
22
23impl ClosedRegistrationEntry {
24 pub fn new(
26 verification_key_for_concatenation: VerificationKeyForConcatenation,
27 stake: Stake,
28 #[cfg(feature = "future_snark")] verification_key_for_snark: Option<
29 VerificationKeyForSnark,
30 >,
31 #[cfg(feature = "future_snark")] lottery_target_value: Option<LotteryTargetValue>,
32 ) -> Self {
33 ClosedRegistrationEntry {
34 verification_key_for_concatenation,
35 stake,
36 #[cfg(feature = "future_snark")]
37 verification_key_for_snark,
38 #[cfg(feature = "future_snark")]
39 lottery_target_value,
40 }
41 }
42
43 pub fn get_verification_key_for_concatenation(&self) -> VerificationKeyForConcatenation {
45 self.verification_key_for_concatenation
46 }
47
48 pub fn get_stake(&self) -> Stake {
50 self.stake
51 }
52
53 #[cfg(feature = "future_snark")]
54 pub fn get_verification_key_for_snark(&self) -> Option<VerificationKeyForSnark> {
56 self.verification_key_for_snark
57 }
58
59 #[cfg(feature = "future_snark")]
60 pub fn get_lottery_target_value(&self) -> Option<LotteryTargetValue> {
62 self.lottery_target_value
63 }
64
65 pub(crate) fn to_bytes(self) -> Vec<u8> {
72 #[cfg(feature = "future_snark")]
73 let capacity = 200;
74 #[cfg(not(feature = "future_snark"))]
75 let capacity = 104;
76
77 let mut result = Vec::with_capacity(capacity);
78 result.extend_from_slice(&self.verification_key_for_concatenation.to_bytes());
79 result.extend_from_slice(&self.stake.to_be_bytes());
80
81 #[cfg(feature = "future_snark")]
82 if let (Some(schnorr_vk), Some(target_value)) =
83 (&self.verification_key_for_snark, &self.lottery_target_value)
84 {
85 result.extend_from_slice(&schnorr_vk.to_bytes());
86 result.extend_from_slice(&target_value.to_bytes());
87 }
88
89 result
90 }
91
92 pub(crate) fn from_bytes(bytes: &[u8]) -> StmResult<Self> {
99 let verification_key_for_concatenation = VerificationKeyForConcatenation::from_bytes(
100 bytes.get(..96).ok_or(RegisterError::SerializationError)?,
101 )?;
102 let mut u64_bytes = [0u8; 8];
103 u64_bytes.copy_from_slice(bytes.get(96..104).ok_or(RegisterError::SerializationError)?);
104 let stake = Stake::from_be_bytes(u64_bytes);
105
106 #[cfg(feature = "future_snark")]
107 let (verification_key_for_snark, lottery_target_value) = {
108 let schnorr_verification_key = bytes
109 .get(104..168)
110 .map(VerificationKeyForSnark::from_bytes)
111 .transpose()?;
112
113 let lottery_target_value =
114 bytes.get(168..200).map(LotteryTargetValue::from_bytes).transpose()?;
115
116 match (schnorr_verification_key, lottery_target_value) {
117 (Some(_), None) | (None, Some(_)) => {
118 return Err(RegisterError::SerializationError.into());
119 }
120 _ => {}
121 }
122 (schnorr_verification_key, lottery_target_value)
123 };
124
125 Ok(ClosedRegistrationEntry {
126 verification_key_for_concatenation,
127 stake,
128 #[cfg(feature = "future_snark")]
129 verification_key_for_snark,
130 #[cfg(feature = "future_snark")]
131 lottery_target_value,
132 })
133 }
134}
135
136impl Serialize for ClosedRegistrationEntry {
137 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
138 #[cfg(not(feature = "future_snark"))]
139 {
140 let mut tuple = serializer.serialize_tuple(2)?;
141 tuple.serialize_element(&self.verification_key_for_concatenation)?;
142 tuple.serialize_element(&self.stake)?;
143 tuple.end()
144 }
145 #[cfg(feature = "future_snark")]
146 {
147 let has_snark_fields = self.verification_key_for_snark.is_some()
148 && self.lottery_target_value.is_some()
149 && cfg!(feature = "future_snark");
150 let tuples_number = if has_snark_fields { 4 } else { 2 };
151 let mut tuple = serializer.serialize_tuple(tuples_number)?;
152 tuple.serialize_element(&self.verification_key_for_concatenation)?;
153 tuple.serialize_element(&self.stake)?;
154 if has_snark_fields {
155 tuple.serialize_element(&self.verification_key_for_snark)?;
156 tuple.serialize_element(&self.lottery_target_value)?;
157 }
158 tuple.end()
159 }
160 }
161}
162
163impl From<(RegistrationEntry, Stake)> for ClosedRegistrationEntry {
168 fn from(entry_total_stake: (RegistrationEntry, Stake)) -> Self {
169 let (entry, _total_stake) = entry_total_stake;
170 #[cfg(feature = "future_snark")]
171 let (schnorr_verification_key, target_value) = {
172 let vk = entry.get_verification_key_for_snark();
173 let target =
174 vk.map(|_| &LotteryTargetValue::default() - &LotteryTargetValue::get_one());
175 (vk, target)
176 };
177
178 ClosedRegistrationEntry::new(
179 entry.get_verification_key_for_concatenation(),
180 entry.get_stake(),
181 #[cfg(feature = "future_snark")]
182 schnorr_verification_key,
183 #[cfg(feature = "future_snark")]
184 target_value,
185 )
186 }
187}
188
189impl Hash for ClosedRegistrationEntry {
190 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
192 self.stake.hash(state);
193 self.verification_key_for_concatenation.hash(state);
194 #[cfg(feature = "future_snark")]
195 {
196 self.verification_key_for_snark.hash(state);
197 self.lottery_target_value.hash(state);
198 }
199 }
200
201 fn hash_slice<H: std::hash::Hasher>(data: &[Self], state: &mut H)
202 where
203 Self: Sized,
204 {
205 for piece in data {
206 piece.hash(state)
207 }
208 }
209}
210
211impl PartialOrd for ClosedRegistrationEntry {
212 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
213 Some(std::cmp::Ord::cmp(self, other))
214 }
215}
216
217impl Ord for ClosedRegistrationEntry {
218 fn cmp(&self, other: &Self) -> Ordering {
224 self.stake.cmp(&other.stake).then(
225 self.verification_key_for_concatenation
226 .cmp(&other.verification_key_for_concatenation),
227 )
228 }
229}
230
231#[cfg(test)]
232mod tests {
233 use rand_chacha::ChaCha20Rng;
234 use rand_core::SeedableRng;
235 use std::cmp::Ordering;
236
237 use crate::{
238 VerificationKeyProofOfPossessionForConcatenation, signature_scheme::BlsSigningKey,
239 };
240
241 #[cfg(feature = "future_snark")]
242 use crate::{VerificationKeyForSnark, signature_scheme::SchnorrSigningKey};
243
244 use super::*;
245
246 fn create_closed_registration_entry(
247 rng: &mut ChaCha20Rng,
248 stake: Stake,
249 ) -> ClosedRegistrationEntry {
250 let bls_sk = BlsSigningKey::generate(rng);
251 let bls_pk = VerificationKeyProofOfPossessionForConcatenation::from(&bls_sk);
252
253 #[cfg(feature = "future_snark")]
254 let schnorr_verification_key = {
255 let sk = SchnorrSigningKey::generate(rng);
256 VerificationKeyForSnark::new_from_signing_key(sk.clone())
257 };
258 ClosedRegistrationEntry::new(
259 bls_pk.vk,
260 stake,
261 #[cfg(feature = "future_snark")]
262 Some(schnorr_verification_key),
263 #[cfg(feature = "future_snark")]
264 Some(LotteryTargetValue::get_one()),
265 )
266 }
267
268 #[test]
269 fn test_ord_different_stakes() {
270 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
271
272 let entry_low_stake = create_closed_registration_entry(&mut rng, 100);
273 let entry_high_stake = create_closed_registration_entry(&mut rng, 200);
274
275 assert_eq!(entry_low_stake.cmp(&entry_high_stake), Ordering::Less);
276 assert_eq!(entry_high_stake.cmp(&entry_low_stake), Ordering::Greater);
277 }
278
279 #[test]
280 fn test_ord_same_stake_different_keys() {
281 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
282
283 let entry1 = create_closed_registration_entry(&mut rng, 100);
284 let entry2 = create_closed_registration_entry(&mut rng, 100);
285
286 let cmp_result = entry1.cmp(&entry2);
287 assert!(cmp_result == Ordering::Less || cmp_result == Ordering::Greater);
288
289 assert_eq!(entry2.cmp(&entry1), cmp_result.reverse());
290 }
291
292 mod golden {
293 use super::*;
294
295 #[cfg(not(feature = "future_snark"))]
296 const GOLDEN_BYTES: &[u8; 104] = &[
297 143, 161, 255, 48, 78, 57, 204, 220, 25, 221, 164, 252, 248, 14, 56, 126, 186, 135,
298 228, 188, 145, 181, 52, 200, 97, 99, 213, 46, 0, 199, 193, 89, 187, 88, 29, 135, 173,
299 244, 86, 36, 83, 54, 67, 164, 6, 137, 94, 72, 6, 105, 128, 128, 93, 48, 176, 11, 4,
300 246, 138, 48, 180, 133, 90, 142, 192, 24, 193, 111, 142, 31, 76, 111, 110, 234, 153,
301 90, 208, 192, 31, 124, 95, 102, 49, 158, 99, 52, 220, 165, 94, 251, 68, 69, 121, 16,
302 224, 194, 0, 0, 0, 0, 0, 0, 0, 1,
303 ];
304
305 #[cfg(feature = "future_snark")]
306 const GOLDEN_BYTES: &[u8; 200] = &[
307 143, 161, 255, 48, 78, 57, 204, 220, 25, 221, 164, 252, 248, 14, 56, 126, 186, 135,
308 228, 188, 145, 181, 52, 200, 97, 99, 213, 46, 0, 199, 193, 89, 187, 88, 29, 135, 173,
309 244, 86, 36, 83, 54, 67, 164, 6, 137, 94, 72, 6, 105, 128, 128, 93, 48, 176, 11, 4,
310 246, 138, 48, 180, 133, 90, 142, 192, 24, 193, 111, 142, 31, 76, 111, 110, 234, 153,
311 90, 208, 192, 31, 124, 95, 102, 49, 158, 99, 52, 220, 165, 94, 251, 68, 69, 121, 16,
312 224, 194, 0, 0, 0, 0, 0, 0, 0, 1, 200, 194, 6, 212, 77, 254, 23, 111, 33, 34, 139, 71,
313 131, 196, 108, 13, 217, 75, 187, 131, 158, 77, 197, 163, 30, 123, 151, 237, 157, 232,
314 167, 10, 45, 121, 194, 155, 110, 46, 240, 74, 141, 138, 78, 228, 92, 179, 58, 63, 233,
315 239, 84, 114, 149, 77, 188, 93, 8, 22, 11, 12, 45, 186, 211, 56, 1, 0, 0, 0, 0, 0, 0,
316 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
317 ];
318
319 fn golden_value() -> ClosedRegistrationEntry {
320 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
321 let bls_sk = BlsSigningKey::generate(&mut rng);
322 let bls_pk = VerificationKeyProofOfPossessionForConcatenation::from(&bls_sk);
323
324 #[cfg(feature = "future_snark")]
325 let schnorr_verification_key = {
326 let sk = SchnorrSigningKey::generate(&mut rng);
327 VerificationKeyForSnark::new_from_signing_key(sk.clone())
328 };
329 ClosedRegistrationEntry::new(
330 bls_pk.vk,
331 1,
332 #[cfg(feature = "future_snark")]
333 Some(schnorr_verification_key),
334 #[cfg(feature = "future_snark")]
335 Some(LotteryTargetValue::get_one()),
336 )
337 }
338
339 #[test]
340 fn golden_conversions() {
341 let value = ClosedRegistrationEntry::from_bytes(GOLDEN_BYTES)
342 .expect("This from bytes should not fail");
343 assert_eq!(golden_value(), value);
344
345 let serialized = ClosedRegistrationEntry::to_bytes(value);
346 let golden_serialized = ClosedRegistrationEntry::to_bytes(golden_value());
347 assert_eq!(golden_serialized, serialized);
348 }
349 }
350}