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 = vk.map(|_| LotteryTargetValue::get_one());
174 (vk, target)
175 };
176
177 ClosedRegistrationEntry::new(
178 entry.get_verification_key_for_concatenation(),
179 entry.get_stake(),
180 #[cfg(feature = "future_snark")]
181 schnorr_verification_key,
182 #[cfg(feature = "future_snark")]
183 target_value,
184 )
185 }
186}
187
188impl Hash for ClosedRegistrationEntry {
189 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
191 self.stake.hash(state);
192 self.verification_key_for_concatenation.hash(state);
193 #[cfg(feature = "future_snark")]
194 {
195 self.verification_key_for_snark.hash(state);
196 self.lottery_target_value.hash(state);
197 }
198 }
199
200 fn hash_slice<H: std::hash::Hasher>(data: &[Self], state: &mut H)
201 where
202 Self: Sized,
203 {
204 for piece in data {
205 piece.hash(state)
206 }
207 }
208}
209
210impl PartialOrd for ClosedRegistrationEntry {
211 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
212 Some(std::cmp::Ord::cmp(self, other))
213 }
214}
215
216impl Ord for ClosedRegistrationEntry {
217 fn cmp(&self, other: &Self) -> Ordering {
223 self.stake.cmp(&other.stake).then(
224 self.verification_key_for_concatenation
225 .cmp(&other.verification_key_for_concatenation),
226 )
227 }
228}
229
230#[cfg(test)]
231mod tests {
232 use rand_chacha::ChaCha20Rng;
233 use rand_core::SeedableRng;
234 use std::cmp::Ordering;
235
236 use crate::{
237 VerificationKeyProofOfPossessionForConcatenation, signature_scheme::BlsSigningKey,
238 };
239
240 #[cfg(feature = "future_snark")]
241 use crate::{VerificationKeyForSnark, signature_scheme::SchnorrSigningKey};
242
243 use super::*;
244
245 fn create_closed_registration_entry(
246 rng: &mut ChaCha20Rng,
247 stake: Stake,
248 ) -> ClosedRegistrationEntry {
249 let bls_sk = BlsSigningKey::generate(rng);
250 let bls_pk = VerificationKeyProofOfPossessionForConcatenation::from(&bls_sk);
251
252 #[cfg(feature = "future_snark")]
253 let schnorr_verification_key = {
254 let sk = SchnorrSigningKey::generate(rng);
255 VerificationKeyForSnark::new_from_signing_key(sk.clone())
256 };
257 ClosedRegistrationEntry::new(
258 bls_pk.vk,
259 stake,
260 #[cfg(feature = "future_snark")]
261 Some(schnorr_verification_key),
262 #[cfg(feature = "future_snark")]
263 Some(LotteryTargetValue::get_one()),
264 )
265 }
266
267 #[test]
268 fn test_ord_different_stakes() {
269 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
270
271 let entry_low_stake = create_closed_registration_entry(&mut rng, 100);
272 let entry_high_stake = create_closed_registration_entry(&mut rng, 200);
273
274 assert_eq!(entry_low_stake.cmp(&entry_high_stake), Ordering::Less);
275 assert_eq!(entry_high_stake.cmp(&entry_low_stake), Ordering::Greater);
276 }
277
278 #[test]
279 fn test_ord_same_stake_different_keys() {
280 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
281
282 let entry1 = create_closed_registration_entry(&mut rng, 100);
283 let entry2 = create_closed_registration_entry(&mut rng, 100);
284
285 let cmp_result = entry1.cmp(&entry2);
286 assert!(cmp_result == Ordering::Less || cmp_result == Ordering::Greater);
287
288 assert_eq!(entry2.cmp(&entry1), cmp_result.reverse());
289 }
290
291 mod golden {
292 use super::*;
293
294 #[cfg(not(feature = "future_snark"))]
295 const GOLDEN_BYTES: &[u8; 104] = &[
296 143, 161, 255, 48, 78, 57, 204, 220, 25, 221, 164, 252, 248, 14, 56, 126, 186, 135,
297 228, 188, 145, 181, 52, 200, 97, 99, 213, 46, 0, 199, 193, 89, 187, 88, 29, 135, 173,
298 244, 86, 36, 83, 54, 67, 164, 6, 137, 94, 72, 6, 105, 128, 128, 93, 48, 176, 11, 4,
299 246, 138, 48, 180, 133, 90, 142, 192, 24, 193, 111, 142, 31, 76, 111, 110, 234, 153,
300 90, 208, 192, 31, 124, 95, 102, 49, 158, 99, 52, 220, 165, 94, 251, 68, 69, 121, 16,
301 224, 194, 0, 0, 0, 0, 0, 0, 0, 1,
302 ];
303
304 #[cfg(feature = "future_snark")]
305 const GOLDEN_BYTES: &[u8; 200] = &[
306 143, 161, 255, 48, 78, 57, 204, 220, 25, 221, 164, 252, 248, 14, 56, 126, 186, 135,
307 228, 188, 145, 181, 52, 200, 97, 99, 213, 46, 0, 199, 193, 89, 187, 88, 29, 135, 173,
308 244, 86, 36, 83, 54, 67, 164, 6, 137, 94, 72, 6, 105, 128, 128, 93, 48, 176, 11, 4,
309 246, 138, 48, 180, 133, 90, 142, 192, 24, 193, 111, 142, 31, 76, 111, 110, 234, 153,
310 90, 208, 192, 31, 124, 95, 102, 49, 158, 99, 52, 220, 165, 94, 251, 68, 69, 121, 16,
311 224, 194, 0, 0, 0, 0, 0, 0, 0, 1, 200, 194, 6, 212, 77, 254, 23, 111, 33, 34, 139, 71,
312 131, 196, 108, 13, 217, 75, 187, 131, 158, 77, 197, 163, 30, 123, 151, 237, 157, 232,
313 167, 10, 45, 121, 194, 155, 110, 46, 240, 74, 141, 138, 78, 228, 92, 179, 58, 63, 233,
314 239, 84, 114, 149, 77, 188, 93, 8, 22, 11, 12, 45, 186, 211, 56, 1, 0, 0, 0, 0, 0, 0,
315 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,
316 ];
317
318 fn golden_value() -> ClosedRegistrationEntry {
319 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
320 let bls_sk = BlsSigningKey::generate(&mut rng);
321 let bls_pk = VerificationKeyProofOfPossessionForConcatenation::from(&bls_sk);
322
323 #[cfg(feature = "future_snark")]
324 let schnorr_verification_key = {
325 let sk = SchnorrSigningKey::generate(&mut rng);
326 VerificationKeyForSnark::new_from_signing_key(sk.clone())
327 };
328 ClosedRegistrationEntry::new(
329 bls_pk.vk,
330 1,
331 #[cfg(feature = "future_snark")]
332 Some(schnorr_verification_key),
333 #[cfg(feature = "future_snark")]
334 Some(LotteryTargetValue::get_one()),
335 )
336 }
337
338 #[test]
339 fn golden_conversions() {
340 let value = ClosedRegistrationEntry::from_bytes(GOLDEN_BYTES)
341 .expect("This from bytes should not fail");
342 assert_eq!(golden_value(), value);
343
344 let serialized = ClosedRegistrationEntry::to_bytes(value);
345 let golden_serialized = ClosedRegistrationEntry::to_bytes(golden_value());
346 assert_eq!(golden_serialized, serialized);
347 }
348 }
349}