1#[cfg(feature = "future_snark")]
2use crate::crypto_helper::{
3 ProtocolSignerVerificationKeyForSnark, ProtocolSignerVerificationKeySignatureForSnark,
4};
5use crate::{
6 crypto_helper::{
7 KesEvolutions, ProtocolOpCert, ProtocolSignerVerificationKeyForConcatenation,
8 ProtocolSignerVerificationKeySignatureForConcatenation,
9 },
10 entities::{PartyId, Stake},
11};
12use std::fmt::{Debug, Formatter};
13
14use serde::{Deserialize, Serialize};
15use sha2::{Digest, Sha256};
16
17#[derive(Clone, Eq, Serialize, Deserialize)]
19pub struct Signer {
20 pub party_id: PartyId,
24
25 #[serde(rename = "verification_key")]
27 pub verification_key_for_concatenation: ProtocolSignerVerificationKeyForConcatenation,
28
29 #[serde(
33 skip_serializing_if = "Option::is_none",
34 rename = "verification_key_signature"
35 )]
36 pub verification_key_signature_for_concatenation:
37 Option<ProtocolSignerVerificationKeySignatureForConcatenation>,
38
39 #[serde(skip_serializing_if = "Option::is_none")]
43 pub operational_certificate: Option<ProtocolOpCert>,
44
45 #[serde(rename = "kes_period", skip_serializing_if = "Option::is_none")]
47 pub kes_evolutions: Option<KesEvolutions>,
48
49 #[cfg(feature = "future_snark")]
51 #[serde(skip_serializing_if = "Option::is_none", default)]
52 pub verification_key_for_snark: Option<ProtocolSignerVerificationKeyForSnark>,
53
54 #[cfg(feature = "future_snark")]
56 #[serde(skip_serializing_if = "Option::is_none", default)]
57 pub verification_key_signature_for_snark:
58 Option<ProtocolSignerVerificationKeySignatureForSnark>,
59}
60
61impl PartialEq for Signer {
62 fn eq(&self, other: &Self) -> bool {
63 self.party_id.eq(&other.party_id)
64 }
65}
66
67impl PartialOrd for Signer {
68 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
69 Some(self.cmp(other))
70 }
71}
72
73impl Ord for Signer {
74 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
75 self.party_id.cmp(&other.party_id)
76 }
77}
78
79impl Signer {
80 pub fn vec_from<T: Into<Signer>>(from: Vec<T>) -> Vec<Self> {
82 from.into_iter().map(|f| f.into()).collect()
83 }
84
85 pub fn compute_hash(&self) -> String {
87 let mut hasher = Sha256::new();
88 hasher.update(self.party_id.as_bytes());
89 hasher.update(
90 self.verification_key_for_concatenation
91 .to_json_hex()
92 .unwrap()
93 .as_bytes(),
94 );
95
96 if let Some(verification_key_signature) = &self.verification_key_signature_for_concatenation
97 {
98 hasher.update(verification_key_signature.to_json_hex().unwrap().as_bytes());
99 }
100 if let Some(operational_certificate) = &self.operational_certificate {
101 hasher.update(operational_certificate.to_json_hex().unwrap().as_bytes());
102 }
103
104 #[cfg(feature = "future_snark")]
105 if let Some(verification_key_for_snark) = &self.verification_key_for_snark {
106 hasher.update(verification_key_for_snark.to_json_hex().unwrap().as_bytes());
107 }
108 #[cfg(feature = "future_snark")]
109 if let Some(verification_key_signature_for_snark) =
110 &self.verification_key_signature_for_snark
111 {
112 hasher.update(verification_key_signature_for_snark.to_json_hex().unwrap().as_bytes());
113 }
114
115 hex::encode(hasher.finalize())
116 }
117}
118
119impl Debug for Signer {
120 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
121 let should_be_exhaustive = f.alternate();
122 let mut debug = f.debug_struct("Signer");
123 debug.field("party_id", &self.party_id);
124
125 match should_be_exhaustive {
126 true => {
127 debug
128 .field(
129 "verification_key_for_concatenation",
130 &format_args!("{:?}", self.verification_key_for_concatenation),
131 )
132 .field(
133 "verification_key_signature_for_concatenation",
134 &format_args!("{:?}", self.verification_key_signature_for_concatenation),
135 )
136 .field(
137 "operational_certificate",
138 &format_args!("{:?}", self.operational_certificate),
139 )
140 .field("kes_evolutions", &format_args!("{:?}", self.kes_evolutions));
141
142 #[cfg(feature = "future_snark")]
143 {
144 debug
145 .field(
146 "verification_key_for_snark",
147 &format_args!("{:?}", self.verification_key_for_snark),
148 )
149 .field(
150 "verification_key_signature_for_snark",
151 &format_args!("{:?}", self.verification_key_signature_for_snark),
152 );
153 }
154
155 debug.finish()
156 }
157 false => debug.finish_non_exhaustive(),
158 }
159 }
160}
161
162impl From<SignerWithStake> for Signer {
163 fn from(other: SignerWithStake) -> Self {
164 Self {
165 party_id: other.party_id,
166 verification_key_for_concatenation: other.verification_key_for_concatenation,
167 verification_key_signature_for_concatenation: other
168 .verification_key_signature_for_concatenation,
169 operational_certificate: other.operational_certificate,
170 kes_evolutions: other.kes_evolutions,
171 #[cfg(feature = "future_snark")]
172 verification_key_for_snark: other.verification_key_for_snark,
173 #[cfg(feature = "future_snark")]
174 verification_key_signature_for_snark: other.verification_key_signature_for_snark,
175 }
176 }
177}
178
179#[derive(Clone, Eq, Serialize, Deserialize)]
181pub struct SignerWithStake {
182 pub party_id: PartyId,
186
187 #[serde(rename = "verification_key")]
189 pub verification_key_for_concatenation: ProtocolSignerVerificationKeyForConcatenation,
190
191 #[serde(
195 skip_serializing_if = "Option::is_none",
196 rename = "verification_key_signature"
197 )]
198 pub verification_key_signature_for_concatenation:
199 Option<ProtocolSignerVerificationKeySignatureForConcatenation>,
200
201 #[serde(skip_serializing_if = "Option::is_none")]
205 pub operational_certificate: Option<ProtocolOpCert>,
206
207 #[serde(rename = "kes_period", skip_serializing_if = "Option::is_none")]
209 pub kes_evolutions: Option<KesEvolutions>,
210
211 pub stake: Stake,
213
214 #[cfg(feature = "future_snark")]
216 #[serde(skip_serializing_if = "Option::is_none", default)]
217 pub verification_key_for_snark: Option<ProtocolSignerVerificationKeyForSnark>,
218
219 #[cfg(feature = "future_snark")]
221 #[serde(skip_serializing_if = "Option::is_none", default)]
222 pub verification_key_signature_for_snark:
223 Option<ProtocolSignerVerificationKeySignatureForSnark>,
224}
225
226impl PartialEq for SignerWithStake {
227 fn eq(&self, other: &Self) -> bool {
228 self.party_id.eq(&other.party_id)
229 }
230}
231
232impl PartialOrd for SignerWithStake {
233 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
234 Some(self.cmp(other))
235 }
236}
237
238impl Ord for SignerWithStake {
239 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
240 self.party_id.cmp(&other.party_id)
241 }
242}
243
244impl SignerWithStake {
245 pub fn from_signer(signer: Signer, stake: Stake) -> Self {
247 Self {
248 party_id: signer.party_id,
249 verification_key_for_concatenation: signer.verification_key_for_concatenation,
250 verification_key_signature_for_concatenation: signer
251 .verification_key_signature_for_concatenation,
252 operational_certificate: signer.operational_certificate,
253 kes_evolutions: signer.kes_evolutions,
254 stake,
255 #[cfg(feature = "future_snark")]
256 verification_key_for_snark: signer.verification_key_for_snark,
257 #[cfg(feature = "future_snark")]
258 verification_key_signature_for_snark: signer.verification_key_signature_for_snark,
259 }
260 }
261
262 #[cfg(feature = "future_snark")]
268 pub fn without_snark_fields(mut self) -> Self {
269 self.verification_key_for_snark = None;
270 self.verification_key_signature_for_snark = None;
271 self
272 }
273
274 #[cfg(feature = "future_snark")]
276 pub fn strip_snark_fields(signers: Vec<Self>) -> Vec<Self> {
277 signers.into_iter().map(Self::without_snark_fields).collect()
278 }
279
280 pub fn compute_hash(&self) -> String {
282 let mut hasher = Sha256::new();
283 hasher.update(self.party_id.as_bytes());
284 hasher.update(
285 self.verification_key_for_concatenation
286 .to_json_hex()
287 .unwrap()
288 .as_bytes(),
289 );
290
291 if let Some(verification_key_signature) = &self.verification_key_signature_for_concatenation
292 {
293 hasher.update(verification_key_signature.to_json_hex().unwrap().as_bytes());
294 }
295 if let Some(operational_certificate) = &self.operational_certificate {
296 hasher.update(operational_certificate.to_json_hex().unwrap().as_bytes());
297 }
298 hasher.update(self.stake.to_be_bytes());
299
300 #[cfg(feature = "future_snark")]
301 if let Some(verification_key_for_snark) = &self.verification_key_for_snark {
302 hasher.update(verification_key_for_snark.to_json_hex().unwrap().as_bytes());
303 }
304 #[cfg(feature = "future_snark")]
305 if let Some(verification_key_signature_for_snark) =
306 &self.verification_key_signature_for_snark
307 {
308 hasher.update(verification_key_signature_for_snark.to_json_hex().unwrap().as_bytes());
309 }
310
311 hex::encode(hasher.finalize())
312 }
313}
314
315impl Debug for SignerWithStake {
316 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
317 let should_be_exhaustive = f.alternate();
318 let mut debug = f.debug_struct("SignerWithStake");
319 debug.field("party_id", &self.party_id).field("stake", &self.stake);
320
321 match should_be_exhaustive {
322 true => {
323 debug
324 .field(
325 "verification_key_for_concatenation",
326 &format_args!("{:?}", self.verification_key_for_concatenation),
327 )
328 .field(
329 "verification_key_signature_for_concatenation",
330 &format_args!("{:?}", self.verification_key_signature_for_concatenation),
331 )
332 .field(
333 "operational_certificate",
334 &format_args!("{:?}", self.operational_certificate),
335 )
336 .field("kes_evolutions", &format_args!("{:?}", self.kes_evolutions));
337
338 #[cfg(feature = "future_snark")]
339 {
340 debug
341 .field(
342 "verification_key_for_snark",
343 &format_args!("{:?}", self.verification_key_for_snark),
344 )
345 .field(
346 "verification_key_signature_for_snark",
347 &format_args!("{:?}", self.verification_key_signature_for_snark),
348 );
349 }
350
351 debug.finish()
352 }
353 false => debug.finish_non_exhaustive(),
354 }
355 }
356}
357
358#[cfg(test)]
359mod tests {
360 use crate::test::{builder::MithrilFixtureBuilder, double::fake_keys};
361
362 use super::*;
363
364 #[test]
365 fn test_stake_signers_from_into() {
366 let verification_key = MithrilFixtureBuilder::default()
367 .with_signers(1)
368 .build()
369 .signers_with_stake()[0]
370 .verification_key_for_concatenation;
371 let signer_expected = Signer {
372 party_id: "1".to_string(),
373 verification_key_for_concatenation: verification_key,
374 verification_key_signature_for_concatenation: None,
375 operational_certificate: None,
376 kes_evolutions: None,
377 #[cfg(feature = "future_snark")]
378 verification_key_for_snark: None,
379 #[cfg(feature = "future_snark")]
380 verification_key_signature_for_snark: None,
381 };
382 let signer_with_stake = SignerWithStake {
383 party_id: "1".to_string(),
384 verification_key_for_concatenation: verification_key,
385 verification_key_signature_for_concatenation: None,
386 operational_certificate: None,
387 kes_evolutions: None,
388 stake: 100,
389 #[cfg(feature = "future_snark")]
390 verification_key_for_snark: None,
391 #[cfg(feature = "future_snark")]
392 verification_key_signature_for_snark: None,
393 };
394
395 let signer_into: Signer = signer_with_stake.into();
396 assert_eq!(signer_expected, signer_into);
397 }
398
399 #[test]
400 fn test_signer_compute_hash() {
401 const HASH_EXPECTED: &str =
402 "02778791113dcd8647b019366e223bfe3aa8a054fa6d9d1918b6b669de485f1c";
403
404 let build_signer = |party_id: &str, key_index: usize| Signer {
405 party_id: party_id.to_string(),
406 verification_key_for_concatenation: fake_keys::signer_verification_key()[key_index]
407 .try_into()
408 .unwrap(),
409 verification_key_signature_for_concatenation: None,
410 operational_certificate: None,
411 kes_evolutions: None,
412 #[cfg(feature = "future_snark")]
413 verification_key_for_snark: None,
414 #[cfg(feature = "future_snark")]
415 verification_key_signature_for_snark: None,
416 };
417
418 assert_eq!(HASH_EXPECTED, build_signer("1", 3).compute_hash());
419 assert_ne!(HASH_EXPECTED, build_signer("0", 3).compute_hash());
420 assert_ne!(HASH_EXPECTED, build_signer("1", 0).compute_hash());
421 }
422
423 #[test]
424 fn test_signer_with_stake_compute_hash() {
425 #[cfg(not(feature = "future_snark"))]
426 const EXPECTED_HASH: &str =
427 "9a832baccd04aabfc419f57319e3831a1655a95bf3bf5ed96a1167d1e81b5085";
428 #[cfg(feature = "future_snark")]
429 const EXPECTED_HASH: &str =
430 "6158c4f514b1e15dc745845dac9014e710ee6b2f0c5b2b1023d5207cf6b75db9";
431 let signers = MithrilFixtureBuilder::default()
432 .with_signers(2)
433 .build()
434 .signers_with_stake();
435 let signer = signers[0].clone();
436
437 assert_eq!(EXPECTED_HASH, signer.compute_hash());
438
439 {
440 let mut signer_different_party_id = signer.clone();
441 signer_different_party_id.party_id = "whatever".to_string();
442
443 assert_ne!(EXPECTED_HASH, signer_different_party_id.compute_hash());
444 }
445 {
446 let mut signer_different_verification_key = signer.clone();
447 signer_different_verification_key.verification_key_for_concatenation =
448 signers[1].verification_key_for_concatenation;
449
450 assert_ne!(
451 EXPECTED_HASH,
452 signer_different_verification_key.compute_hash()
453 );
454 }
455 {
456 let mut signer_different_stake = signer.clone();
457 signer_different_stake.stake += 20;
458
459 assert_ne!(EXPECTED_HASH, signer_different_stake.compute_hash());
460 }
461
462 #[cfg(feature = "future_snark")]
463 {
464 let mut signer_different_verification_key_for_snark = signer.clone();
465 signer_different_verification_key_for_snark.verification_key_for_snark =
466 signers[1].verification_key_for_snark;
467
468 assert_ne!(
469 EXPECTED_HASH,
470 signer_different_verification_key_for_snark.compute_hash()
471 );
472 }
473
474 #[cfg(feature = "future_snark")]
475 {
476 let mut signer_different_verification_key_signature_for_snark = signer.clone();
477 signer_different_verification_key_signature_for_snark
478 .verification_key_signature_for_snark =
479 signers[1].verification_key_signature_for_snark;
480
481 assert_ne!(
482 EXPECTED_HASH,
483 signer_different_verification_key_signature_for_snark.compute_hash()
484 );
485 }
486 }
487
488 #[cfg(feature = "future_snark")]
489 mod strip_snark_fields {
490 use super::*;
491
492 #[test]
493 fn snark_fields_are_cleared_by_without_snark_fields() {
494 let signers = MithrilFixtureBuilder::default()
495 .with_signers(1)
496 .build()
497 .signers_with_stake();
498 let signer = signers[0].clone();
499 assert!(signer.verification_key_for_snark.is_some());
500 assert!(signer.verification_key_signature_for_snark.is_some());
501
502 let stripped = signer.without_snark_fields();
503
504 assert!(stripped.verification_key_for_snark.is_none());
505 assert!(stripped.verification_key_signature_for_snark.is_none());
506 }
507
508 #[test]
509 fn without_snark_fields_preserves_non_snark_data() {
510 let signers = MithrilFixtureBuilder::default()
511 .with_signers(1)
512 .build()
513 .signers_with_stake();
514 let signer = signers[0].clone();
515
516 let stripped = signer.clone().without_snark_fields();
517
518 assert_eq!(signer.party_id, stripped.party_id);
519 assert_eq!(
520 signer.verification_key_for_concatenation,
521 stripped.verification_key_for_concatenation
522 );
523 assert_eq!(signer.stake, stripped.stake);
524 }
525
526 #[test]
527 fn without_snark_fields_preserves_none_values() {
528 let signers = MithrilFixtureBuilder::default()
529 .with_signers(1)
530 .build()
531 .signers_with_stake();
532 let mut signer = signers[0].clone();
533 signer.verification_key_for_snark = None;
534 signer.verification_key_signature_for_snark = None;
535
536 let stripped = signer.without_snark_fields();
537
538 assert!(stripped.verification_key_for_snark.is_none());
539 assert!(stripped.verification_key_signature_for_snark.is_none());
540 }
541
542 #[test]
543 fn strip_snark_fields_clears_all_entries() {
544 let signers = MithrilFixtureBuilder::default()
545 .with_signers(3)
546 .build()
547 .signers_with_stake();
548 assert!(signers.iter().all(|s| s.verification_key_for_snark.is_some()));
549
550 let stripped = SignerWithStake::strip_snark_fields(signers);
551
552 assert!(stripped.iter().all(|s| s.verification_key_for_snark.is_none()));
553 assert!(
554 stripped
555 .iter()
556 .all(|s| s.verification_key_signature_for_snark.is_none())
557 );
558 }
559
560 #[test]
561 fn strip_snark_fields_handles_empty_list() {
562 let stripped = SignerWithStake::strip_snark_fields(vec![]);
563 assert!(stripped.is_empty());
564 }
565 }
566}