1use serde::{Deserialize, Serialize, Serializer, ser::SerializeTuple};
2use std::cmp::Ordering;
3use std::hash::Hash;
4
5use crate::{
6 PhiFValue, RegisterError, RegistrationEntry, Stake, StmResult, VerificationKeyForConcatenation,
7 codec,
8};
9
10#[cfg(feature = "future_snark")]
11use crate::{
12 LotteryTargetValue, VerificationKeyForSnark,
13 proof_system::compute_target_value_for_snark_lottery,
14};
15
16#[derive(Serialize, Deserialize)]
22struct ClosedRegistrationEntryCborEnvelope {
23 verification_key_bytes: Vec<u8>,
24 stake: Stake,
25 #[cfg(feature = "future_snark")]
26 #[serde(skip_serializing_if = "Option::is_none", default)]
27 snark_verification_key_bytes: Option<Vec<u8>>,
28 #[cfg(feature = "future_snark")]
29 #[serde(skip_serializing_if = "Option::is_none", default)]
30 lottery_target_value_bytes: Option<Vec<u8>>,
31}
32
33#[derive(PartialEq, Eq, Clone, Debug, Deserialize)]
35pub struct ClosedRegistrationEntry {
36 verification_key_for_concatenation: VerificationKeyForConcatenation,
37 stake: Stake,
38 #[cfg(feature = "future_snark")]
39 #[serde(skip_serializing_if = "Option::is_none", default)]
40 verification_key_for_snark: Option<VerificationKeyForSnark>,
41 #[cfg(feature = "future_snark")]
42 #[serde(skip_serializing_if = "Option::is_none", default)]
43 lottery_target_value: Option<LotteryTargetValue>,
44}
45
46impl ClosedRegistrationEntry {
47 pub fn new(
49 verification_key_for_concatenation: VerificationKeyForConcatenation,
50 stake: Stake,
51 #[cfg(feature = "future_snark")] verification_key_for_snark: Option<
52 VerificationKeyForSnark,
53 >,
54 #[cfg(feature = "future_snark")] lottery_target_value: Option<LotteryTargetValue>,
55 ) -> Self {
56 ClosedRegistrationEntry {
57 verification_key_for_concatenation,
58 stake,
59 #[cfg(feature = "future_snark")]
60 verification_key_for_snark,
61 #[cfg(feature = "future_snark")]
62 lottery_target_value,
63 }
64 }
65
66 pub fn get_verification_key_for_concatenation(&self) -> VerificationKeyForConcatenation {
68 self.verification_key_for_concatenation
69 }
70
71 pub fn get_stake(&self) -> Stake {
73 self.stake
74 }
75
76 #[cfg(feature = "future_snark")]
82 pub fn without_snark_fields(&self) -> Self {
83 ClosedRegistrationEntry {
84 verification_key_for_concatenation: self.verification_key_for_concatenation,
85 stake: self.stake,
86 verification_key_for_snark: None,
87 lottery_target_value: None,
88 }
89 }
90
91 #[cfg(feature = "future_snark")]
92 pub fn get_verification_key_for_snark(&self) -> Option<VerificationKeyForSnark> {
94 self.verification_key_for_snark
95 }
96
97 #[cfg(feature = "future_snark")]
98 pub fn get_lottery_target_value(&self) -> Option<LotteryTargetValue> {
100 self.lottery_target_value
101 }
102
103 pub(crate) fn to_bytes(&self) -> StmResult<Vec<u8>> {
108 let envelope = ClosedRegistrationEntryCborEnvelope {
109 verification_key_bytes: self.verification_key_for_concatenation.to_bytes().to_vec(),
110 stake: self.stake,
111 #[cfg(feature = "future_snark")]
112 snark_verification_key_bytes: self
113 .verification_key_for_snark
114 .map(|vk| vk.to_bytes().to_vec()),
115 #[cfg(feature = "future_snark")]
116 lottery_target_value_bytes: self
117 .lottery_target_value
118 .map(|ltv| ltv.to_bytes().to_vec()),
119 };
120 codec::to_cbor_bytes(&envelope)
121 }
122
123 pub(crate) fn from_bytes(bytes: &[u8]) -> StmResult<Self> {
128 if codec::has_cbor_v1_prefix(bytes) {
129 let envelope: ClosedRegistrationEntryCborEnvelope =
130 codec::from_cbor_bytes(&bytes[1..])?;
131 let verification_key_for_concatenation =
132 VerificationKeyForConcatenation::from_bytes(&envelope.verification_key_bytes)?;
133
134 #[cfg(feature = "future_snark")]
135 let verification_key_for_snark = envelope
136 .snark_verification_key_bytes
137 .map(|b| VerificationKeyForSnark::from_bytes(&b))
138 .transpose()?;
139
140 #[cfg(feature = "future_snark")]
141 let lottery_target_value = envelope
142 .lottery_target_value_bytes
143 .map(|b| LotteryTargetValue::from_bytes(&b))
144 .transpose()?;
145
146 Ok(ClosedRegistrationEntry {
147 verification_key_for_concatenation,
148 stake: envelope.stake,
149 #[cfg(feature = "future_snark")]
150 verification_key_for_snark,
151 #[cfg(feature = "future_snark")]
152 lottery_target_value,
153 })
154 } else {
155 Self::from_bytes_legacy(bytes)
156 }
157 }
158
159 fn from_bytes_legacy(bytes: &[u8]) -> StmResult<Self> {
166 let verification_key_for_concatenation = VerificationKeyForConcatenation::from_bytes(
167 bytes.get(..96).ok_or(RegisterError::SerializationError)?,
168 )?;
169 let mut u64_bytes = [0u8; 8];
170 u64_bytes.copy_from_slice(bytes.get(96..104).ok_or(RegisterError::SerializationError)?);
171 let stake = Stake::from_be_bytes(u64_bytes);
172
173 #[cfg(feature = "future_snark")]
174 let (verification_key_for_snark, lottery_target_value) = {
175 let schnorr_verification_key = bytes
176 .get(104..168)
177 .map(VerificationKeyForSnark::from_bytes)
178 .transpose()?;
179
180 let lottery_target_value =
181 bytes.get(168..200).map(LotteryTargetValue::from_bytes).transpose()?;
182
183 match (schnorr_verification_key, lottery_target_value) {
184 (Some(_), None) | (None, Some(_)) => {
185 return Err(RegisterError::SerializationError.into());
186 }
187 _ => {}
188 }
189 (schnorr_verification_key, lottery_target_value)
190 };
191
192 Ok(ClosedRegistrationEntry {
193 verification_key_for_concatenation,
194 stake,
195 #[cfg(feature = "future_snark")]
196 verification_key_for_snark,
197 #[cfg(feature = "future_snark")]
198 lottery_target_value,
199 })
200 }
201}
202
203impl Serialize for ClosedRegistrationEntry {
204 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
205 #[cfg(not(feature = "future_snark"))]
206 {
207 let mut tuple = serializer.serialize_tuple(2)?;
208 tuple.serialize_element(&self.verification_key_for_concatenation)?;
209 tuple.serialize_element(&self.stake)?;
210 tuple.end()
211 }
212 #[cfg(feature = "future_snark")]
213 {
214 let has_snark_fields = self.verification_key_for_snark.is_some()
215 && self.lottery_target_value.is_some()
216 && cfg!(feature = "future_snark");
217 let tuples_number = if has_snark_fields { 4 } else { 2 };
218 let mut tuple = serializer.serialize_tuple(tuples_number)?;
219 tuple.serialize_element(&self.verification_key_for_concatenation)?;
220 tuple.serialize_element(&self.stake)?;
221 if has_snark_fields {
222 tuple.serialize_element(&self.verification_key_for_snark)?;
223 tuple.serialize_element(&self.lottery_target_value)?;
224 }
225 tuple.end()
226 }
227 }
228}
229
230impl TryFrom<(RegistrationEntry, Stake, PhiFValue)> for ClosedRegistrationEntry {
236 type Error = anyhow::Error;
237 fn try_from(entry_total_stake: (RegistrationEntry, Stake, PhiFValue)) -> StmResult<Self> {
238 #[cfg(not(feature = "future_snark"))]
239 let (entry, _total_stake, _phi_f) = entry_total_stake;
240 #[cfg(feature = "future_snark")]
241 let (entry, total_stake, phi_f) = entry_total_stake;
242 #[cfg(feature = "future_snark")]
243 let (schnorr_verification_key, target_value) = {
244 let vk = entry.get_verification_key_for_snark();
245 let target = vk
246 .is_some()
247 .then(|| {
248 compute_target_value_for_snark_lottery(phi_f, entry.get_stake(), total_stake)
249 })
250 .transpose()?;
251
252 (vk, target)
253 };
254
255 Ok(ClosedRegistrationEntry::new(
256 entry.get_verification_key_for_concatenation(),
257 entry.get_stake(),
258 #[cfg(feature = "future_snark")]
259 schnorr_verification_key,
260 #[cfg(feature = "future_snark")]
261 target_value,
262 ))
263 }
264}
265
266impl Hash for ClosedRegistrationEntry {
267 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
269 self.stake.hash(state);
270 self.verification_key_for_concatenation.hash(state);
271 #[cfg(feature = "future_snark")]
272 {
273 self.verification_key_for_snark.hash(state);
274 self.lottery_target_value.hash(state);
275 }
276 }
277
278 fn hash_slice<H: std::hash::Hasher>(data: &[Self], state: &mut H)
279 where
280 Self: Sized,
281 {
282 for piece in data {
283 piece.hash(state)
284 }
285 }
286}
287
288impl PartialOrd for ClosedRegistrationEntry {
289 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
290 Some(std::cmp::Ord::cmp(self, other))
291 }
292}
293
294impl Ord for ClosedRegistrationEntry {
295 fn cmp(&self, other: &Self) -> Ordering {
301 self.stake.cmp(&other.stake).then(
302 self.verification_key_for_concatenation
303 .cmp(&other.verification_key_for_concatenation),
304 )
305 }
306}
307
308#[cfg(test)]
309mod tests {
310 use rand_chacha::ChaCha20Rng;
311 use rand_core::SeedableRng;
312 use std::cmp::Ordering;
313
314 use crate::{
315 VerificationKeyProofOfPossessionForConcatenation, signature_scheme::BlsSigningKey,
316 };
317
318 #[cfg(feature = "future_snark")]
319 use crate::{VerificationKeyForSnark, signature_scheme::SchnorrSigningKey};
320
321 use super::*;
322
323 fn create_closed_registration_entry(
324 rng: &mut ChaCha20Rng,
325 stake: Stake,
326 ) -> ClosedRegistrationEntry {
327 let bls_sk = BlsSigningKey::generate(rng);
328 let bls_pk = VerificationKeyProofOfPossessionForConcatenation::from(&bls_sk);
329
330 #[cfg(feature = "future_snark")]
331 let schnorr_verification_key = {
332 let sk = SchnorrSigningKey::generate(rng);
333 VerificationKeyForSnark::new_from_signing_key(sk.clone())
334 };
335 ClosedRegistrationEntry::new(
336 bls_pk.vk,
337 stake,
338 #[cfg(feature = "future_snark")]
339 Some(schnorr_verification_key),
340 #[cfg(feature = "future_snark")]
341 Some(LotteryTargetValue::get_one()),
342 )
343 }
344
345 #[test]
346 fn test_ord_different_stakes() {
347 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
348
349 let entry_low_stake = create_closed_registration_entry(&mut rng, 100);
350 let entry_high_stake = create_closed_registration_entry(&mut rng, 200);
351
352 assert_eq!(entry_low_stake.cmp(&entry_high_stake), Ordering::Less);
353 assert_eq!(entry_high_stake.cmp(&entry_low_stake), Ordering::Greater);
354 }
355
356 #[test]
357 fn test_ord_same_stake_different_keys() {
358 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
359
360 let entry1 = create_closed_registration_entry(&mut rng, 100);
361 let entry2 = create_closed_registration_entry(&mut rng, 100);
362
363 let cmp_result = entry1.cmp(&entry2);
364 assert!(cmp_result == Ordering::Less || cmp_result == Ordering::Greater);
365
366 assert_eq!(entry2.cmp(&entry1), cmp_result.reverse());
367 }
368
369 mod golden {
370 use super::*;
371
372 #[cfg(not(feature = "future_snark"))]
373 const GOLDEN_BYTES: &[u8; 104] = &[
374 143, 161, 255, 48, 78, 57, 204, 220, 25, 221, 164, 252, 248, 14, 56, 126, 186, 135,
375 228, 188, 145, 181, 52, 200, 97, 99, 213, 46, 0, 199, 193, 89, 187, 88, 29, 135, 173,
376 244, 86, 36, 83, 54, 67, 164, 6, 137, 94, 72, 6, 105, 128, 128, 93, 48, 176, 11, 4,
377 246, 138, 48, 180, 133, 90, 142, 192, 24, 193, 111, 142, 31, 76, 111, 110, 234, 153,
378 90, 208, 192, 31, 124, 95, 102, 49, 158, 99, 52, 220, 165, 94, 251, 68, 69, 121, 16,
379 224, 194, 0, 0, 0, 0, 0, 0, 0, 1,
380 ];
381
382 #[cfg(feature = "future_snark")]
383 const GOLDEN_BYTES: &[u8; 200] = &[
384 143, 161, 255, 48, 78, 57, 204, 220, 25, 221, 164, 252, 248, 14, 56, 126, 186, 135,
385 228, 188, 145, 181, 52, 200, 97, 99, 213, 46, 0, 199, 193, 89, 187, 88, 29, 135, 173,
386 244, 86, 36, 83, 54, 67, 164, 6, 137, 94, 72, 6, 105, 128, 128, 93, 48, 176, 11, 4,
387 246, 138, 48, 180, 133, 90, 142, 192, 24, 193, 111, 142, 31, 76, 111, 110, 234, 153,
388 90, 208, 192, 31, 124, 95, 102, 49, 158, 99, 52, 220, 165, 94, 251, 68, 69, 121, 16,
389 224, 194, 0, 0, 0, 0, 0, 0, 0, 1, 200, 194, 6, 212, 77, 254, 23, 111, 33, 34, 139, 71,
390 131, 196, 108, 13, 217, 75, 187, 131, 158, 77, 197, 163, 30, 123, 151, 237, 157, 232,
391 167, 10, 45, 121, 194, 155, 110, 46, 240, 74, 141, 138, 78, 228, 92, 179, 58, 63, 233,
392 239, 84, 114, 149, 77, 188, 93, 8, 22, 11, 12, 45, 186, 211, 56, 1, 0, 0, 0, 0, 0, 0,
393 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,
394 ];
395
396 fn golden_value() -> ClosedRegistrationEntry {
397 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
398 let bls_sk = BlsSigningKey::generate(&mut rng);
399 let bls_pk = VerificationKeyProofOfPossessionForConcatenation::from(&bls_sk);
400
401 #[cfg(feature = "future_snark")]
402 let schnorr_verification_key = {
403 let sk = SchnorrSigningKey::generate(&mut rng);
404 VerificationKeyForSnark::new_from_signing_key(sk.clone())
405 };
406 ClosedRegistrationEntry::new(
407 bls_pk.vk,
408 1,
409 #[cfg(feature = "future_snark")]
410 Some(schnorr_verification_key),
411 #[cfg(feature = "future_snark")]
412 Some(LotteryTargetValue::get_one()),
413 )
414 }
415
416 #[test]
417 fn golden_conversions() {
418 let value = ClosedRegistrationEntry::from_bytes(GOLDEN_BYTES)
419 .expect("This from bytes should not fail");
420 assert_eq!(golden_value(), value);
421
422 let serialized = ClosedRegistrationEntry::to_bytes(&value)
423 .expect("ClosedRegistrationEntry serialization should not fail");
424 let golden_serialized = ClosedRegistrationEntry::to_bytes(&golden_value())
425 .expect("ClosedRegistrationEntry serialization should not fail");
426 assert_eq!(golden_serialized, serialized);
427 }
428
429 #[cfg(not(feature = "future_snark"))]
430 const GOLDEN_CBOR_BYTES: &[u8; 219] = &[
431 1, 162, 118, 118, 101, 114, 105, 102, 105, 99, 97, 116, 105, 111, 110, 95, 107, 101,
432 121, 95, 98, 121, 116, 101, 115, 152, 96, 24, 143, 24, 161, 24, 255, 24, 48, 24, 78,
433 24, 57, 24, 204, 24, 220, 24, 25, 24, 221, 24, 164, 24, 252, 24, 248, 14, 24, 56, 24,
434 126, 24, 186, 24, 135, 24, 228, 24, 188, 24, 145, 24, 181, 24, 52, 24, 200, 24, 97, 24,
435 99, 24, 213, 24, 46, 0, 24, 199, 24, 193, 24, 89, 24, 187, 24, 88, 24, 29, 24, 135, 24,
436 173, 24, 244, 24, 86, 24, 36, 24, 83, 24, 54, 24, 67, 24, 164, 6, 24, 137, 24, 94, 24,
437 72, 6, 24, 105, 24, 128, 24, 128, 24, 93, 24, 48, 24, 176, 11, 4, 24, 246, 24, 138, 24,
438 48, 24, 180, 24, 133, 24, 90, 24, 142, 24, 192, 24, 24, 24, 193, 24, 111, 24, 142, 24,
439 31, 24, 76, 24, 111, 24, 110, 24, 234, 24, 153, 24, 90, 24, 208, 24, 192, 24, 31, 24,
440 124, 24, 95, 24, 102, 24, 49, 24, 158, 24, 99, 24, 52, 24, 220, 24, 165, 24, 94, 24,
441 251, 24, 68, 24, 69, 24, 121, 16, 24, 224, 24, 194, 101, 115, 116, 97, 107, 101, 1,
442 ];
443
444 #[cfg(feature = "future_snark")]
445 const GOLDEN_CBOR_BYTES: &[u8; 433] = &[
446 1, 164, 118, 118, 101, 114, 105, 102, 105, 99, 97, 116, 105, 111, 110, 95, 107, 101,
447 121, 95, 98, 121, 116, 101, 115, 152, 96, 24, 143, 24, 161, 24, 255, 24, 48, 24, 78,
448 24, 57, 24, 204, 24, 220, 24, 25, 24, 221, 24, 164, 24, 252, 24, 248, 14, 24, 56, 24,
449 126, 24, 186, 24, 135, 24, 228, 24, 188, 24, 145, 24, 181, 24, 52, 24, 200, 24, 97, 24,
450 99, 24, 213, 24, 46, 0, 24, 199, 24, 193, 24, 89, 24, 187, 24, 88, 24, 29, 24, 135, 24,
451 173, 24, 244, 24, 86, 24, 36, 24, 83, 24, 54, 24, 67, 24, 164, 6, 24, 137, 24, 94, 24,
452 72, 6, 24, 105, 24, 128, 24, 128, 24, 93, 24, 48, 24, 176, 11, 4, 24, 246, 24, 138, 24,
453 48, 24, 180, 24, 133, 24, 90, 24, 142, 24, 192, 24, 24, 24, 193, 24, 111, 24, 142, 24,
454 31, 24, 76, 24, 111, 24, 110, 24, 234, 24, 153, 24, 90, 24, 208, 24, 192, 24, 31, 24,
455 124, 24, 95, 24, 102, 24, 49, 24, 158, 24, 99, 24, 52, 24, 220, 24, 165, 24, 94, 24,
456 251, 24, 68, 24, 69, 24, 121, 16, 24, 224, 24, 194, 101, 115, 116, 97, 107, 101, 1,
457 120, 28, 115, 110, 97, 114, 107, 95, 118, 101, 114, 105, 102, 105, 99, 97, 116, 105,
458 111, 110, 95, 107, 101, 121, 95, 98, 121, 116, 101, 115, 152, 64, 24, 200, 24, 194, 6,
459 24, 212, 24, 77, 24, 254, 23, 24, 111, 24, 33, 24, 34, 24, 139, 24, 71, 24, 131, 24,
460 196, 24, 108, 13, 24, 217, 24, 75, 24, 187, 24, 131, 24, 158, 24, 77, 24, 197, 24, 163,
461 24, 30, 24, 123, 24, 151, 24, 237, 24, 157, 24, 232, 24, 167, 10, 24, 45, 24, 121, 24,
462 194, 24, 155, 24, 110, 24, 46, 24, 240, 24, 74, 24, 141, 24, 138, 24, 78, 24, 228, 24,
463 92, 24, 179, 24, 58, 24, 63, 24, 233, 24, 239, 24, 84, 24, 114, 24, 149, 24, 77, 24,
464 188, 24, 93, 8, 22, 11, 12, 24, 45, 24, 186, 24, 211, 24, 56, 120, 26, 108, 111, 116,
465 116, 101, 114, 121, 95, 116, 97, 114, 103, 101, 116, 95, 118, 97, 108, 117, 101, 95,
466 98, 121, 116, 101, 115, 152, 32, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
467 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
468 ];
469
470 #[test]
471 fn cbor_golden_bytes_can_be_decoded() {
472 let decoded = ClosedRegistrationEntry::from_bytes(GOLDEN_CBOR_BYTES)
473 .expect("CBOR golden bytes deserialization should not fail");
474 assert_eq!(golden_value(), decoded);
475 }
476
477 #[test]
478 fn cbor_encoding_is_stable() {
479 let bytes = ClosedRegistrationEntry::to_bytes(&golden_value())
480 .expect("ClosedRegistrationEntry serialization should not fail");
481 assert_eq!(GOLDEN_CBOR_BYTES.as_slice(), bytes.as_slice());
482 }
483 }
484
485 mod envelope_compatibility {
486 use super::*;
487 use crate::codec;
488
489 #[test]
490 fn forward_compatible_with_extra_fields() {
491 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
492 let entry = create_closed_registration_entry(&mut rng, 42);
493
494 #[derive(serde::Serialize)]
495 struct EvolvedEnvelope {
496 verification_key_bytes: Vec<u8>,
497 stake: Stake,
498 new_field: String,
499 }
500
501 let evolved = EvolvedEnvelope {
502 verification_key_bytes: entry
503 .get_verification_key_for_concatenation()
504 .to_bytes()
505 .to_vec(),
506 stake: 42,
507 new_field: "extra".to_string(),
508 };
509 let evolved_bytes =
510 codec::to_cbor_bytes(&evolved).expect("evolved serialization should not fail");
511
512 let decoded = ClosedRegistrationEntry::from_bytes(&evolved_bytes)
513 .expect("decoding with extra field should succeed");
514 assert_eq!(entry.get_stake(), decoded.get_stake());
515 assert_eq!(
516 entry.get_verification_key_for_concatenation(),
517 decoded.get_verification_key_for_concatenation()
518 );
519 }
520
521 #[test]
522 fn backward_compatible_with_missing_optional_fields() {
523 #[derive(serde::Serialize)]
524 struct MinimalEnvelope {
525 verification_key_bytes: Vec<u8>,
526 stake: Stake,
527 }
528
529 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
530 let entry = create_closed_registration_entry(&mut rng, 42);
531
532 let minimal = MinimalEnvelope {
533 verification_key_bytes: entry
534 .get_verification_key_for_concatenation()
535 .to_bytes()
536 .to_vec(),
537 stake: 42,
538 };
539 let minimal_bytes =
540 codec::to_cbor_bytes(&minimal).expect("minimal serialization should not fail");
541
542 let decoded = ClosedRegistrationEntry::from_bytes(&minimal_bytes)
543 .expect("decoding with missing optional fields should succeed");
544 assert_eq!(42, decoded.get_stake());
545 assert_eq!(
546 entry.get_verification_key_for_concatenation(),
547 decoded.get_verification_key_for_concatenation()
548 );
549 }
550 }
551
552 #[cfg(feature = "future_snark")]
553 mod without_snark_fields {
554 use super::*;
555
556 #[test]
557 fn preserves_concatenation_key_and_stake() {
558 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
559 let entry = create_closed_registration_entry(&mut rng, 42);
560
561 let stripped = entry.without_snark_fields();
562
563 assert_eq!(
564 entry.get_verification_key_for_concatenation(),
565 stripped.get_verification_key_for_concatenation()
566 );
567 assert_eq!(entry.get_stake(), stripped.get_stake());
568 }
569
570 #[test]
571 fn clears_snark_fields() {
572 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
573 let entry = create_closed_registration_entry(&mut rng, 42);
574 assert!(entry.get_verification_key_for_snark().is_some());
575 assert!(entry.get_lottery_target_value().is_some());
576
577 let stripped = entry.without_snark_fields();
578
579 assert!(stripped.get_verification_key_for_snark().is_none());
580 assert!(stripped.get_lottery_target_value().is_none());
581 }
582
583 #[test]
584 fn serializes_as_two_element_json_tuple() {
585 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
586 let entry = create_closed_registration_entry(&mut rng, 42);
587
588 let stripped = entry.without_snark_fields();
589 let json: serde_json::Value =
590 serde_json::to_value(stripped).expect("JSON serialization should not fail");
591
592 let array = json.as_array().expect("should serialize as a JSON array");
593 assert_eq!(
594 2,
595 array.len(),
596 "stripped entry should serialize as a 2-element tuple"
597 );
598 }
599
600 #[test]
601 fn entry_with_snark_fields_serializes_as_four_element_json_tuple() {
602 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
603 let entry = create_closed_registration_entry(&mut rng, 42);
604
605 let json: serde_json::Value =
606 serde_json::to_value(entry).expect("JSON serialization should not fail");
607
608 let array = json.as_array().expect("should serialize as a JSON array");
609 assert_eq!(
610 4,
611 array.len(),
612 "full entry should serialize as a 4-element tuple"
613 );
614 }
615 }
616}