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 pub fn compute_hash(&self) -> String {
264 let mut hasher = Sha256::new();
265 hasher.update(self.party_id.as_bytes());
266 hasher.update(
267 self.verification_key_for_concatenation
268 .to_json_hex()
269 .unwrap()
270 .as_bytes(),
271 );
272
273 if let Some(verification_key_signature) = &self.verification_key_signature_for_concatenation
274 {
275 hasher.update(verification_key_signature.to_json_hex().unwrap().as_bytes());
276 }
277 if let Some(operational_certificate) = &self.operational_certificate {
278 hasher.update(operational_certificate.to_json_hex().unwrap().as_bytes());
279 }
280 hasher.update(self.stake.to_be_bytes());
281
282 #[cfg(feature = "future_snark")]
283 if let Some(verification_key_for_snark) = &self.verification_key_for_snark {
284 hasher.update(verification_key_for_snark.to_json_hex().unwrap().as_bytes());
285 }
286 #[cfg(feature = "future_snark")]
287 if let Some(verification_key_signature_for_snark) =
288 &self.verification_key_signature_for_snark
289 {
290 hasher.update(verification_key_signature_for_snark.to_json_hex().unwrap().as_bytes());
291 }
292
293 hex::encode(hasher.finalize())
294 }
295}
296
297impl Debug for SignerWithStake {
298 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
299 let should_be_exhaustive = f.alternate();
300 let mut debug = f.debug_struct("SignerWithStake");
301 debug.field("party_id", &self.party_id).field("stake", &self.stake);
302
303 match should_be_exhaustive {
304 true => {
305 debug
306 .field(
307 "verification_key_for_concatenation",
308 &format_args!("{:?}", self.verification_key_for_concatenation),
309 )
310 .field(
311 "verification_key_signature_for_concatenation",
312 &format_args!("{:?}", self.verification_key_signature_for_concatenation),
313 )
314 .field(
315 "operational_certificate",
316 &format_args!("{:?}", self.operational_certificate),
317 )
318 .field("kes_evolutions", &format_args!("{:?}", self.kes_evolutions));
319
320 #[cfg(feature = "future_snark")]
321 {
322 debug
323 .field(
324 "verification_key_for_snark",
325 &format_args!("{:?}", self.verification_key_for_snark),
326 )
327 .field(
328 "verification_key_signature_for_snark",
329 &format_args!("{:?}", self.verification_key_signature_for_snark),
330 );
331 }
332
333 debug.finish()
334 }
335 false => debug.finish_non_exhaustive(),
336 }
337 }
338}
339
340#[cfg(test)]
341mod tests {
342 use crate::test::{builder::MithrilFixtureBuilder, double::fake_keys};
343
344 use super::*;
345
346 #[test]
347 fn test_stake_signers_from_into() {
348 let verification_key = MithrilFixtureBuilder::default()
349 .with_signers(1)
350 .build()
351 .signers_with_stake()[0]
352 .verification_key_for_concatenation;
353 let signer_expected = Signer {
354 party_id: "1".to_string(),
355 verification_key_for_concatenation: verification_key,
356 verification_key_signature_for_concatenation: None,
357 operational_certificate: None,
358 kes_evolutions: None,
359 #[cfg(feature = "future_snark")]
360 verification_key_for_snark: None,
361 #[cfg(feature = "future_snark")]
362 verification_key_signature_for_snark: None,
363 };
364 let signer_with_stake = SignerWithStake {
365 party_id: "1".to_string(),
366 verification_key_for_concatenation: verification_key,
367 verification_key_signature_for_concatenation: None,
368 operational_certificate: None,
369 kes_evolutions: None,
370 stake: 100,
371 #[cfg(feature = "future_snark")]
372 verification_key_for_snark: None,
373 #[cfg(feature = "future_snark")]
374 verification_key_signature_for_snark: None,
375 };
376
377 let signer_into: Signer = signer_with_stake.into();
378 assert_eq!(signer_expected, signer_into);
379 }
380
381 #[test]
382 fn test_signer_compute_hash() {
383 const HASH_EXPECTED: &str =
384 "02778791113dcd8647b019366e223bfe3aa8a054fa6d9d1918b6b669de485f1c";
385
386 let build_signer = |party_id: &str, key_index: usize| Signer {
387 party_id: party_id.to_string(),
388 verification_key_for_concatenation: fake_keys::signer_verification_key()[key_index]
389 .try_into()
390 .unwrap(),
391 verification_key_signature_for_concatenation: None,
392 operational_certificate: None,
393 kes_evolutions: None,
394 #[cfg(feature = "future_snark")]
395 verification_key_for_snark: None,
396 #[cfg(feature = "future_snark")]
397 verification_key_signature_for_snark: None,
398 };
399
400 assert_eq!(HASH_EXPECTED, build_signer("1", 3).compute_hash());
401 assert_ne!(HASH_EXPECTED, build_signer("0", 3).compute_hash());
402 assert_ne!(HASH_EXPECTED, build_signer("1", 0).compute_hash());
403 }
404
405 #[test]
406 fn test_signer_with_stake_compute_hash() {
407 #[cfg(not(feature = "future_snark"))]
408 const EXPECTED_HASH: &str =
409 "9a832baccd04aabfc419f57319e3831a1655a95bf3bf5ed96a1167d1e81b5085";
410 #[cfg(feature = "future_snark")]
411 const EXPECTED_HASH: &str =
412 "6158c4f514b1e15dc745845dac9014e710ee6b2f0c5b2b1023d5207cf6b75db9";
413 let signers = MithrilFixtureBuilder::default()
414 .with_signers(2)
415 .build()
416 .signers_with_stake();
417 let signer = signers[0].clone();
418
419 assert_eq!(EXPECTED_HASH, signer.compute_hash());
420
421 {
422 let mut signer_different_party_id = signer.clone();
423 signer_different_party_id.party_id = "whatever".to_string();
424
425 assert_ne!(EXPECTED_HASH, signer_different_party_id.compute_hash());
426 }
427 {
428 let mut signer_different_verification_key = signer.clone();
429 signer_different_verification_key.verification_key_for_concatenation =
430 signers[1].verification_key_for_concatenation;
431
432 assert_ne!(
433 EXPECTED_HASH,
434 signer_different_verification_key.compute_hash()
435 );
436 }
437 {
438 let mut signer_different_stake = signer.clone();
439 signer_different_stake.stake += 20;
440
441 assert_ne!(EXPECTED_HASH, signer_different_stake.compute_hash());
442 }
443
444 #[cfg(feature = "future_snark")]
445 {
446 let mut signer_different_verification_key_for_snark = signer.clone();
447 signer_different_verification_key_for_snark.verification_key_for_snark =
448 signers[1].verification_key_for_snark;
449
450 assert_ne!(
451 EXPECTED_HASH,
452 signer_different_verification_key_for_snark.compute_hash()
453 );
454 }
455
456 #[cfg(feature = "future_snark")]
457 {
458 let mut signer_different_verification_key_signature_for_snark = signer.clone();
459 signer_different_verification_key_signature_for_snark
460 .verification_key_signature_for_snark =
461 signers[1].verification_key_signature_for_snark;
462
463 assert_ne!(
464 EXPECTED_HASH,
465 signer_different_verification_key_signature_for_snark.compute_hash()
466 );
467 }
468 }
469}