mithril_common/messages/message_parts/
signer.rs

1use anyhow::Context;
2use serde::{Deserialize, Serialize};
3use std::fmt::{Debug, Formatter};
4
5use crate::{
6    StdError, StdResult,
7    crypto_helper::{
8        KesEvolutions, ProtocolOpCert, ProtocolSignerVerificationKeyForConcatenation,
9        ProtocolSignerVerificationKeySignatureForConcatenation,
10    },
11    entities::{
12        HexEncodedOpCert, HexEncodedVerificationKeyForConcatenation,
13        HexEncodedVerificationKeySignatureForConcatenation, PartyId, Signer, SignerWithStake,
14        Stake,
15    },
16};
17#[cfg(feature = "future_snark")]
18use crate::{
19    crypto_helper::{
20        ProtocolSignerVerificationKeyForSnark, ProtocolSignerVerificationKeySignatureForSnark,
21    },
22    entities::{HexEncodedVerificationKeyForSnark, HexEncodedVerificationKeySignatureForSnark},
23};
24
25/// Signer with Stake Message
26#[derive(Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
27pub struct SignerWithStakeMessagePart {
28    /// The unique identifier of the signer
29    ///
30    /// Used only for testing when SPO pool id is not certified
31    pub party_id: PartyId,
32
33    /// The encoded verification key for the Concatenation proof system
34    #[serde(rename = "verification_key")]
35    pub verification_key_for_concatenation: HexEncodedVerificationKeyForConcatenation,
36
37    /// The encoded KES signature over the verification key for Concatenation
38    ///
39    /// None is used only for testing when SPO pool id is not certified
40    #[serde(
41        skip_serializing_if = "Option::is_none",
42        rename = "verification_key_signature"
43    )]
44    pub verification_key_signature_for_concatenation:
45        Option<HexEncodedVerificationKeySignatureForConcatenation>,
46
47    /// The encoded operational certificate of stake pool operator attached to
48    /// the signer node.
49    ///
50    /// None is used only for testing when SPO pool id is not certified
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub operational_certificate: Option<HexEncodedOpCert>,
53
54    /// The number of evolutions of the KES key since the start KES period of the operational certificate at the time of signature.
55    #[serde(rename = "kes_period", skip_serializing_if = "Option::is_none")]
56    pub kes_evolutions: Option<KesEvolutions>,
57
58    /// The signer stake
59    pub stake: Stake,
60
61    /// The encoded verification key for the SNARK proof system
62    #[cfg(feature = "future_snark")]
63    #[serde(skip_serializing_if = "Option::is_none", default)]
64    pub verification_key_for_snark: Option<HexEncodedVerificationKeyForSnark>,
65
66    /// The encoded KES signature over the verification key for SNARK
67    #[cfg(feature = "future_snark")]
68    #[serde(skip_serializing_if = "Option::is_none", default)]
69    pub verification_key_signature_for_snark: Option<HexEncodedVerificationKeySignatureForSnark>,
70}
71
72impl SignerWithStakeMessagePart {
73    /// Convert a set of signers into message parts
74    pub fn from_signers(signers: Vec<SignerWithStake>) -> Vec<Self> {
75        signers.into_iter().map(|signer| signer.into()).collect()
76    }
77
78    /// Convert a set of signer message parts into a set of signers with stake
79    pub fn try_into_signers(messages: Vec<Self>) -> StdResult<Vec<SignerWithStake>> {
80        messages
81            .into_iter()
82            .map(SignerWithStakeMessagePart::try_into)
83            .collect()
84    }
85}
86
87impl TryInto<SignerWithStake> for SignerWithStakeMessagePart {
88    type Error = StdError;
89
90    fn try_into(self) -> Result<SignerWithStake, Self::Error> {
91        let verification_key_for_concatenation: ProtocolSignerVerificationKeyForConcatenation =
92            self.verification_key_for_concatenation.try_into().with_context(|| {
93                format!(
94                    "Error while parsing Concatenation verification key message, party_id = '{}'",
95                    self.party_id
96                )
97            })?;
98        let verification_key_signature_for_concatenation: Option<
99            ProtocolSignerVerificationKeySignatureForConcatenation,
100        > = self
101            .verification_key_signature_for_concatenation
102            .map(|f| f.try_into())
103            .transpose()
104            .with_context(|| {
105                format!(
106                    "Error while parsing Concatenation verification key signature message, party_id = '{}'",
107                    self.party_id
108                )
109            })?;
110        #[cfg(feature = "future_snark")]
111        let verification_key_for_snark: Option<ProtocolSignerVerificationKeyForSnark> = self
112            .verification_key_for_snark
113            .map(|f| f.try_into())
114            .transpose()
115            .with_context(|| {
116                format!(
117                    "Error while parsing SNARK verification key message, party_id = '{}'",
118                    self.party_id
119                )
120            })?;
121        #[cfg(feature = "future_snark")]
122        let verification_key_signature_for_snark: Option<
123            ProtocolSignerVerificationKeySignatureForSnark,
124        > = self
125            .verification_key_signature_for_snark
126            .map(|f| f.try_into())
127            .transpose()
128            .with_context(|| {
129                format!(
130                    "Error while parsing SNARK verification key signature message, party_id = '{}'",
131                    self.party_id
132                )
133            })?;
134        let operational_certificate: Option<ProtocolOpCert> = self
135            .operational_certificate
136            .map(|f| f.try_into())
137            .transpose()
138            .with_context(|| {
139                format!(
140                    "Error while parsing operational certificate message, party_id = '{}'.",
141                    self.party_id
142                )
143            })?;
144        let value = SignerWithStake {
145            party_id: self.party_id,
146            verification_key_for_concatenation,
147            verification_key_signature_for_concatenation,
148            kes_evolutions: self.kes_evolutions,
149            operational_certificate,
150            stake: self.stake,
151            #[cfg(feature = "future_snark")]
152            verification_key_for_snark,
153            #[cfg(feature = "future_snark")]
154            verification_key_signature_for_snark,
155        };
156        Ok(value)
157    }
158}
159
160impl From<SignerWithStake> for SignerWithStakeMessagePart {
161    fn from(value: SignerWithStake) -> Self {
162        Self {
163            party_id: value.party_id,
164            verification_key_for_concatenation: value
165                .verification_key_for_concatenation
166                .try_into()
167                .unwrap(),
168            verification_key_signature_for_concatenation: value
169                .verification_key_signature_for_concatenation
170                .map(|k| k.try_into().unwrap()),
171            operational_certificate: value
172                .operational_certificate
173                .map(|op_cert| op_cert.try_into().unwrap()),
174            kes_evolutions: value.kes_evolutions,
175            stake: value.stake,
176            #[cfg(feature = "future_snark")]
177            verification_key_for_snark: value
178                .verification_key_for_snark
179                .map(|k| k.try_into().unwrap()),
180            #[cfg(feature = "future_snark")]
181            verification_key_signature_for_snark: value
182                .verification_key_signature_for_snark
183                .map(|s| s.try_into().unwrap()),
184        }
185    }
186}
187
188impl Debug for SignerWithStakeMessagePart {
189    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
190        let should_be_exhaustive = f.alternate();
191        let mut debug = f.debug_struct("Signer");
192        debug.field("party_id", &self.party_id).field("stake", &self.stake);
193
194        match should_be_exhaustive {
195            true => {
196                debug
197                    .field(
198                        "verification_key_for_concatenation",
199                        &format_args!("{:?}", self.verification_key_for_concatenation),
200                    )
201                    .field(
202                        "verification_key_signature_for_concatenation",
203                        &format_args!("{:?}", self.verification_key_signature_for_concatenation),
204                    )
205                    .field(
206                        "operational_certificate",
207                        &format_args!("{:?}", self.operational_certificate),
208                    )
209                    .field("kes_evolutions", &format_args!("{:?}", self.kes_evolutions));
210
211                #[cfg(feature = "future_snark")]
212                {
213                    debug
214                        .field(
215                            "verification_key_for_snark",
216                            &format_args!("{:?}", self.verification_key_for_snark),
217                        )
218                        .field(
219                            "verification_key_signature_for_snark",
220                            &format_args!("{:?}", self.verification_key_signature_for_snark),
221                        );
222                }
223
224                debug.finish()
225            }
226            false => debug.finish_non_exhaustive(),
227        }
228    }
229}
230
231/// Signer Message
232#[derive(Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
233pub struct SignerMessagePart {
234    /// The unique identifier of the signer
235    ///
236    /// Used only for testing when SPO pool id is not certified
237    pub party_id: PartyId,
238
239    /// The verification key for the Concatenation proof system
240    #[serde(rename = "verification_key")]
241    pub verification_key_for_concatenation: HexEncodedVerificationKeyForConcatenation,
242
243    /// The encoded signer 'Mithril verification key' signature (signed by the
244    /// Cardano node KES secret key).
245    ///
246    /// None is used only for testing when SPO pool id is not certified
247    #[serde(
248        skip_serializing_if = "Option::is_none",
249        rename = "verification_key_signature"
250    )]
251    pub verification_key_signature_for_concatenation:
252        Option<HexEncodedVerificationKeySignatureForConcatenation>,
253
254    /// The encoded operational certificate of stake pool operator attached to
255    /// the signer node.
256    ///
257    /// None is used only for testing when SPO pool id is not certified
258    #[serde(skip_serializing_if = "Option::is_none")]
259    pub operational_certificate: Option<HexEncodedOpCert>,
260
261    /// The number of evolutions of the KES key since the start KES period of the operational certificate at the time of signature.
262    #[serde(rename = "kes_period", skip_serializing_if = "Option::is_none")]
263    pub kes_evolutions: Option<KesEvolutions>,
264
265    /// The verification key for the SNARK proof system (hex encoded)
266    #[cfg(feature = "future_snark")]
267    #[serde(skip_serializing_if = "Option::is_none", default)]
268    pub verification_key_for_snark: Option<HexEncodedVerificationKeyForSnark>,
269
270    /// The KES signature over the verification key for SNARK (hex encoded)
271    #[cfg(feature = "future_snark")]
272    #[serde(skip_serializing_if = "Option::is_none", default)]
273    pub verification_key_signature_for_snark: Option<HexEncodedVerificationKeySignatureForSnark>,
274}
275
276impl SignerMessagePart {
277    /// Convert a set of signer message parts into a set of signers
278    pub fn try_into_signers(messages: Vec<Self>) -> StdResult<Vec<Signer>> {
279        messages.into_iter().map(SignerMessagePart::try_into).collect()
280    }
281
282    /// Convert a set of signers into message parts
283    pub fn from_signers(signers: Vec<Signer>) -> Vec<Self> {
284        signers.into_iter().map(|signer| signer.into()).collect()
285    }
286
287    /// Remove SNARK-related fields for backward compatibility with older clients.
288    ///
289    /// This is needed during the Pythagoras era to avoid deserialization failures
290    /// in older signers/aggregators that don't know about the SNARK fields.
291    #[cfg(feature = "future_snark")]
292    pub fn without_snark_fields(mut self) -> Self {
293        self.verification_key_for_snark = None;
294        self.verification_key_signature_for_snark = None;
295        self
296    }
297
298    /// Remove SNARK-related fields from a list of signer message parts for backward compatibility.
299    #[cfg(feature = "future_snark")]
300    pub fn strip_snark_fields(signers: Vec<Self>) -> Vec<Self> {
301        signers.into_iter().map(Self::without_snark_fields).collect()
302    }
303}
304
305impl TryInto<Signer> for SignerMessagePart {
306    type Error = StdError;
307
308    fn try_into(self) -> Result<Signer, Self::Error> {
309        let verification_key_for_concatenation: ProtocolSignerVerificationKeyForConcatenation =
310            self.verification_key_for_concatenation.try_into().with_context(|| {
311                format!(
312                    "Error while parsing verification key message, party_id = '{}'",
313                    self.party_id
314                )
315            })?;
316        let verification_key_signature_for_concatenation: Option<
317            ProtocolSignerVerificationKeySignatureForConcatenation,
318        > = self
319            .verification_key_signature_for_concatenation
320            .map(|f| f.try_into())
321            .transpose()
322            .with_context(|| {
323                format!(
324                    "Error while parsing verification key signature message, party_id = '{}'",
325                    self.party_id
326                )
327            })?;
328        #[cfg(feature = "future_snark")]
329        let verification_key_for_snark: Option<ProtocolSignerVerificationKeyForSnark> = self
330            .verification_key_for_snark
331            .map(|f| f.try_into())
332            .transpose()
333            .with_context(|| {
334                format!(
335                    "Error while parsing SNARK verification key message, party_id = '{}'",
336                    self.party_id
337                )
338            })?;
339        #[cfg(feature = "future_snark")]
340        let verification_key_signature_for_snark: Option<
341            ProtocolSignerVerificationKeySignatureForSnark,
342        > = self
343            .verification_key_signature_for_snark
344            .map(|f| f.try_into())
345            .transpose()
346            .with_context(|| {
347                format!(
348                    "Error while parsing verification key signature message, party_id = '{}'",
349                    self.party_id
350                )
351            })?;
352        let operational_certificate: Option<ProtocolOpCert> = self
353            .operational_certificate
354            .map(|f| f.try_into())
355            .transpose()
356            .with_context(|| {
357                format!(
358                    "Error while parsing operational certificate message, party_id = '{}'.",
359                    self.party_id
360                )
361            })?;
362
363        let value = Signer {
364            party_id: self.party_id,
365            verification_key_for_concatenation,
366            verification_key_signature_for_concatenation,
367            kes_evolutions: self.kes_evolutions,
368            operational_certificate,
369            #[cfg(feature = "future_snark")]
370            verification_key_for_snark,
371            #[cfg(feature = "future_snark")]
372            verification_key_signature_for_snark,
373        };
374        Ok(value)
375    }
376}
377
378impl From<Signer> for SignerMessagePart {
379    fn from(value: Signer) -> Self {
380        Self {
381            party_id: value.party_id,
382            verification_key_for_concatenation: value
383                .verification_key_for_concatenation
384                .try_into()
385                .unwrap(),
386            verification_key_signature_for_concatenation: value
387                .verification_key_signature_for_concatenation
388                .map(|k| k.try_into().unwrap()),
389            operational_certificate: value
390                .operational_certificate
391                .map(|op_cert| op_cert.try_into().unwrap()),
392            kes_evolutions: value.kes_evolutions,
393            #[cfg(feature = "future_snark")]
394            verification_key_for_snark: value
395                .verification_key_for_snark
396                .map(|k| k.try_into().unwrap()),
397            #[cfg(feature = "future_snark")]
398            verification_key_signature_for_snark: value
399                .verification_key_signature_for_snark
400                .map(|s| s.try_into().unwrap()),
401        }
402    }
403}
404
405impl Debug for SignerMessagePart {
406    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
407        let should_be_exhaustive = f.alternate();
408        let mut debug = f.debug_struct("Signer");
409        debug.field("party_id", &self.party_id);
410
411        match should_be_exhaustive {
412            true => {
413                debug
414                    .field(
415                        "verification_key_for_concatenation",
416                        &format_args!("{:?}", self.verification_key_for_concatenation),
417                    )
418                    .field(
419                        "verification_key_signature_for_concatenation",
420                        &format_args!("{:?}", self.verification_key_signature_for_concatenation),
421                    )
422                    .field(
423                        "operational_certificate",
424                        &format_args!("{:?}", self.operational_certificate),
425                    )
426                    .field("kes_evolutions", &format_args!("{:?}", self.kes_evolutions));
427
428                #[cfg(feature = "future_snark")]
429                {
430                    debug
431                        .field(
432                            "verification_key_for_snark",
433                            &format_args!("{:?}", self.verification_key_for_snark),
434                        )
435                        .field(
436                            "verification_key_signature_for_snark",
437                            &format_args!("{:?}", self.verification_key_signature_for_snark),
438                        );
439                }
440
441                debug.finish()
442            }
443            false => debug.finish_non_exhaustive(),
444        }
445    }
446}
447#[cfg(test)]
448mod tests {
449    use super::*;
450
451    mod golden_protocol_key_encodings {
452        use super::*;
453
454        fn golden_signer_message_part_with_json_hex_encoding() -> SignerMessagePart {
455            SignerMessagePart {
456                    party_id: "pool1m8crhnqj5k2kyszf5j2scshupystyxc887zdfrpzh6ty6eun4fx"
457                        .to_string(),
458                    verification_key_for_concatenation: "7b22766b223a5b3138342c3134352c3230382c3138382c31342c342c3230342c3135392c33322c3234332c37352c3137392c38322c3133352c3235342c3135372c33312c35392c33382c3131302c3133362c3232352c3233342c3132332c34372c3130322c34322c3132352c3138392c31372c3136322c3234342c37352c3234382c3139352c3232372c3131362c3139322c3135322c39302c34312c32372c3235312c3137322c35332c3137382c3231342c35362c32302c3232372c3139372c31392c3234322c3138362c3130312c322c37332c3234332c31342c3230342c3136342c3133342c3136322c3233332c32392c3131342c33302c3136372c3230372c3137332c36382c362c37362c38302c3233342c36352c36342c3137332c3231372c3232392c34382c3133342c31322c39352c3138362c3233382c3135302c3139322c3138302c3139322c31302c312c3136322c3131372c3131322c3132325d2c22706f70223a5b3137382c3231342c3231342c3139332c3137382c3134372c34332c3132362c31362c3231362c3231352c3133322c3136342c3134362c382c3233382c3234352c38322c3232342c3233372c3234392c3134322c3135372c3232392c32372c3135392c3132312c3233312c3234382c3131312c38352c38352c35302c3139382c3233362c39312c3133302c3133352c36322c3133302c3132352c3134372c3136392c3134352c39352c33352c37382c3235332c3135302c3232352c3232352c3232372c38332c3132352c32382c3137332c3130382c3234312c3230302c37332c3134342c36322c31322c3232392c3134332c3134332c37352c37342c3133352c3135382c3139362c3139362c3232342c3232382c38382c3130352c3132342c34372c37362c3234382c3234342c38362c3136332c3232312c3134372c3134382c36352c3232382c31352c37392c3138352c39302c39362c3139322c3138392c3233365d7d".to_string(),
459                    verification_key_signature_for_concatenation: Some(
460                        "7b227369676d61223a7b227369676d61223a7b227369676d61223a7b227369676d61223a7b227369676d61223a7b227369676d61223a5b32312c3131392c302c3232382c3130322c3231362c3137372c3133302c34362c3131362c3132302c3235322c3232342c31322c34332c3139302c32322c3131372c3231362c3132342c3234302c3137332c31392c3234312c3138342c3230392c36342c3130352c39392c3233342c36312c31302c37312c36302c33392c3133392c39382c3133342c3133322c3135372c3133382c33372c3232322c3130362c3131312c37382c3132302c37382c39332c39382c32382c35332c3231322c3138332c3135322c37312c3135312c36332c3136382c3138372c33372c3232372c3133352c31345d2c226c68735f706b223a5b3135372c32312c33362c32392c32302c33342c31372c3233392c31332c38392c33372c3132302c32312c3135352c3233362c3234302c39352c3138392c3136362c3131332c3231332c39302c3138382c34312c3137312c33372c3131312c3138302c3234332c33312c35322c36385d2c227268735f706b223a5b3136302c3234332c39382c34352c342c3232312c36342c3131372c34382c3230332c3138362c34312c39352c31342c3135382c3232382c3137352c3232322c3131362c34342c38342c39372c3136372c3132372c38302c3139372c3234352c3136372c3139332c3139362c3135372c3137375d7d2c226c68735f706b223a5b3138332c3135352c36352c3234322c3233342c34332c3136372c3139322c31362c38322c3130322c39382c3133372c3139362c32382c38312c3232312c38312c36352c322c39332c37302c38342c3136382c3137392c3138372c3130322c32382c3234342c3138322c3132332c3134385d2c227268735f706b223a5b3135352c36392c3134302c32382c3137342c33382c3134392c36302c3137382c3232372c35342c3231312c3130362c3133312c36382c3135362c35392c3138302c35332c382c3138382c3233312c3137332c312c3137312c3131392c33322c3134372c3233342c34322c3136332c3131305d7d2c226c68735f706b223a5b3134312c3137322c3137372c38352c39302c3131312c3135332c3234362c3233332c3139362c3130352c3138342c35342c3234372c3133342c3135382c3232362c3230372c32392c3136332c31392c33342c312c3230372c3232322c37322c3136362c32332c31382c3137342c31362c39365d2c227268735f706b223a5b31322c3231322c3139372c3130382c3233302c38332c33372c3139342c3135382c3135362c38362c3138362c33322c3234352c3234342c34302c3234342c3138302c3136382c3233302c362c3137382c3138362c32322c36382c33392c3230322c35352c3130302c3232392c3138392c3232315d7d2c226c68735f706b223a5b33362c3235302c322c3232392c3233342c3136352c32372c36332c3132302c3137382c342c3138302c3235312c3134372c35302c3134382c3233372c3135342c33382c3134352c3131342c37342c3231312c34382c3133362c3231332c31382c3138322c3230332c3233302c31352c3133345d2c227268735f706b223a5b3139332c3134342c3231332c362c3235352c3137372c3131302c3131302c3231332c3137352c332c32382c3135382c3231362c3137332c32362c3232352c38392c34362c3133382c34392c37342c36332c34332c3134342c34382c34392c39352c35372c31392c3132392c34335d7d2c226c68735f706b223a5b3135362c38312c3130352c3134312c3230392c322c3137352c38332c38332c37342c3138332c3130312c3131362c3137362c31382c3233362c31382c3232332c3233372c3233332c3139332c35342c32382c3231332c302c3133352c3135372c33342c37352c3230352c34382c3133385d2c227268735f706b223a5b33382c3234382c35322c32302c35352c3131382c3138372c33392c3137362c3231332c33342c332c3231342c322c33312c3131382c3232342c3132392c34322c3136332c39342c3130382c3131362c35352c3231332c36372c3133322c39362c3232392c3132322c39362c3136385d7d2c226c68735f706b223a5b3132312c3134342c33312c3131302c3234392c3234342c3139382c3235342c3139312c36322c39312c31372c3135322c3135312c3233322c3130302c3130392c39362c3230322c3234392c3139382c3230342c39332c372c3131372c3233362c3132382c36362c38392c3231342c3133392c3134375d2c227268735f706b223a5b3234362c3136362c3230342c3135302c3139362c39312c3135382c3133312c3133382c3130332c3234352c34392c3134352c3133302c32322c3132362c3134372c39352c32332c39332c31332c3230392c3133312c34392c3138322c34362c3135332c35372c33372c3130332c3235332c3234325d7d".to_string(),
461                    ),
462                    operational_certificate: Some(
463                        "5b5b5b35312c36322c392c3230302c3230392c34312c3234352c3230372c3135392c3139392c31342c372c38322c3230332c3234302c312c3132392c3138372c3131392c3232312c3133362c3234372c38392c3132382c3232382c3133332c302c39382c31322c3232382c3137382c3233345d2c31362c313139302c5b3231302c3134382c37332c3136332c3232322c3233332c3138302c33372c3133312c3235342c392c3230352c3135382c3134392c31342c37302c39322c372c3233352c3231342c3131312c35322c3131362c34312c3131382c362c3132392c312c3130362c312c39342c3233332c3131352c3137332c3130302c3133392c3131342c3130392c31352c31342c3233332c34332c3137392c3137342c35302c31302c3135302c39372c3132372c3138322c31362c372c3131322c3234352c34382c3134312c38342c3130322c342c32352c3231312c3134342c3230322c345d5d2c5b3133312c3135352c37322c35372c3134372c3231382c3137332c36382c3139312c3234322c3138392c3234372c32372c3235342c3134382c3232352c35332c31312c36392c3135372c3138322c38302c3233342c3133312c3233342c33392c3130322c32312c322c332c36352c3139305d5d".to_string(),
464                    ),
465                    kes_evolutions: Some(KesEvolutions(6)),
466                    #[cfg(feature = "future_snark")]
467                    verification_key_for_snark: None,
468                    #[cfg(feature = "future_snark")]
469                    verification_key_signature_for_snark: None,
470                }
471        }
472
473        fn golden_signer_message_part_with_bytes_hex_encoding() -> SignerMessagePart {
474            SignerMessagePart {
475                    party_id: "pool1m8crhnqj5k2kyszf5j2scshupystyxc887zdfrpzh6ty6eun4fx"
476                        .to_string(),
477                    verification_key_for_concatenation: "b891d0bc0e04cc9f20f34bb35287fe9d1f3b266e88e1ea7b2f662a7dbd11a2f44bf8c3e374c0985a291bfbac35b2d63814e3c513f2ba650249f30ecca486a2e91d721ea7cfad44064c50ea4140add9e530860c5fbaee96c0b4c00a01a275707ab2d6d6c1b2932b7e10d8d784a49208eef552e0edf98e9de51b9f79e7f86f555532c6ec5b82873e827d93a9915f234efd96e1e1e3537d1cad6cf1c849903e0ce58f8f4b4a879ec4c4e0e458697c2f4cf8f456a3dd939441e40f4fb95a60c0bdec".to_string(),
478                    verification_key_signature_for_concatenation: Some(
479                        "157700e466d8b1822e7478fce00c2bbe1675d87cf0ad13f1b8d1406963ea3d0a473c278b6286849d8a25de6a6f4e784e5d621c35d4b79847973fa8bb25e3870e9d15241d142211ef0d592578159becf05fbda671d55abc29ab256fb4f31f3444a0f3622d04dd407530cbba295f0e9ee4afde742c5461a77f50c5f5a7c1c49db1b79b41f2ea2ba7c01052666289c41c51dd5141025d4654a8b3bb661cf4b67b949b458c1cae26953cb2e336d36a83449c3bb43508bce7ad01ab772093ea2aa36e8dacb1555a6f99f6e9c469b836f7869ee2cf1da3132201cfde48a61712ae10600cd4c56ce65325c29e9c56ba20f5f428f4b4a8e606b2ba164427ca3764e5bddd24fa02e5eaa51b3f78b204b4fb933294ed9a2691724ad33088d512b6cbe60f86c190d506ffb16e6ed5af031c9ed8ad1ae1592e8a314a3f2b9030315f3913812b9c51698dd102af53534ab76574b012ec12dfede9c1361cd500879d224bcd308a26f834143776bb27b0d52203d6021f76e0812aa35e6c7437d5438460e57a60a879901f6ef9f4c6febf3e5b119897e8646d60caf9c6cc5d0775ec804259d68b93f6a6cc96c45b9e838a67f5319182167e935f175d0dd18331b62e99392567fdf2".to_string(),
480                    ),
481                    operational_certificate: Some(
482                        "82845820333e09c8d129f5cf9fc70e0752cbf00181bb77dd88f75980e48500620ce4b2ea101904a65840d29449a3dee9b42583fe09cd9e950e465c07ebd66f347429760681016a015ee973ad648b726d0f0ee92bb3ae320a96617fb6100770f5308d54660419d390ca045820839b483993daad44bff2bdf71bfe94e1350b459db650ea83ea276615020341be".to_string(),
483                    ),
484                    kes_evolutions: Some(KesEvolutions(6)),
485                    #[cfg(feature = "future_snark")]
486                    verification_key_for_snark: None,
487                    #[cfg(feature = "future_snark")]
488                    verification_key_signature_for_snark: None,
489                }
490        }
491
492        mod signer {
493            use super::*;
494
495            fn golden_message_with_json_hex_encoding() -> SignerMessagePart {
496                golden_signer_message_part_with_json_hex_encoding()
497            }
498
499            fn golden_message_with_bytes_hex_encoding() -> SignerMessagePart {
500                golden_signer_message_part_with_bytes_hex_encoding()
501            }
502
503            #[test]
504            fn restorations_from_json_hex_and_bytes_hex_give_same_signer() {
505                let signer_from_json_hex: Signer =
506                    golden_message_with_json_hex_encoding().try_into().unwrap();
507
508                let signer_from_bytes_hex: Signer =
509                    golden_message_with_bytes_hex_encoding().try_into().unwrap();
510
511                assert_eq!(signer_from_json_hex, signer_from_bytes_hex);
512            }
513        }
514
515        mod signer_with_stake {
516            use super::*;
517
518            fn golden_message_with_json_hex_encoding() -> SignerWithStakeMessagePart {
519                let signer_message_part = golden_signer_message_part_with_json_hex_encoding();
520
521                SignerWithStakeMessagePart {
522                    party_id: signer_message_part.party_id,
523                    verification_key_for_concatenation: signer_message_part
524                        .verification_key_for_concatenation,
525                    verification_key_signature_for_concatenation: signer_message_part
526                        .verification_key_signature_for_concatenation,
527                    operational_certificate: signer_message_part.operational_certificate,
528                    kes_evolutions: signer_message_part.kes_evolutions,
529                    stake: 123,
530                    #[cfg(feature = "future_snark")]
531                    verification_key_for_snark: signer_message_part.verification_key_for_snark,
532                    #[cfg(feature = "future_snark")]
533                    verification_key_signature_for_snark: signer_message_part
534                        .verification_key_signature_for_snark,
535                }
536            }
537
538            fn golden_message_with_bytes_hex_encoding() -> SignerWithStakeMessagePart {
539                let signer_message_part = golden_signer_message_part_with_bytes_hex_encoding();
540
541                SignerWithStakeMessagePart {
542                    party_id: signer_message_part.party_id,
543                    verification_key_for_concatenation: signer_message_part
544                        .verification_key_for_concatenation,
545                    verification_key_signature_for_concatenation: signer_message_part
546                        .verification_key_signature_for_concatenation,
547                    operational_certificate: signer_message_part.operational_certificate,
548                    kes_evolutions: signer_message_part.kes_evolutions,
549                    stake: 123,
550                    #[cfg(feature = "future_snark")]
551                    verification_key_for_snark: signer_message_part.verification_key_for_snark,
552                    #[cfg(feature = "future_snark")]
553                    verification_key_signature_for_snark: signer_message_part
554                        .verification_key_signature_for_snark,
555                }
556            }
557
558            #[test]
559            fn restorations_from_json_hex_and_bytes_hex_give_same_signer() {
560                let signer_from_json_hex: SignerWithStake =
561                    golden_message_with_json_hex_encoding().try_into().unwrap();
562
563                let signer_from_bytes_hex: SignerWithStake =
564                    golden_message_with_bytes_hex_encoding().try_into().unwrap();
565
566                assert_eq!(signer_from_json_hex, signer_from_bytes_hex);
567            }
568        }
569    }
570
571    #[cfg(feature = "future_snark")]
572    mod strip_snark_fields {
573        use super::*;
574
575        fn signer_message_part_with_snark_fields() -> SignerMessagePart {
576            SignerMessagePart {
577                party_id: "party-1".to_string(),
578                verification_key_for_concatenation: "vk-1".to_string(),
579                verification_key_signature_for_concatenation: None,
580                operational_certificate: None,
581                kes_evolutions: None,
582                verification_key_for_snark: Some("snark-vk-1".to_string()),
583                verification_key_signature_for_snark: Some("snark-sig-1".to_string()),
584            }
585        }
586
587        fn signer_message_part_without_snark_fields() -> SignerMessagePart {
588            SignerMessagePart {
589                party_id: "party-1".to_string(),
590                verification_key_for_concatenation: "vk-1".to_string(),
591                verification_key_signature_for_concatenation: None,
592                operational_certificate: None,
593                kes_evolutions: None,
594                verification_key_for_snark: None,
595                verification_key_signature_for_snark: None,
596            }
597        }
598
599        #[test]
600        fn snark_fields_are_cleared_by_without_snark_fields() {
601            let signer = signer_message_part_with_snark_fields();
602            assert!(signer.verification_key_for_snark.is_some());
603            assert!(signer.verification_key_signature_for_snark.is_some());
604
605            let stripped_signer = signer.without_snark_fields();
606
607            assert!(stripped_signer.verification_key_for_snark.is_none());
608            assert!(stripped_signer.verification_key_signature_for_snark.is_none());
609            assert_eq!(signer_message_part_without_snark_fields(), stripped_signer);
610        }
611
612        #[test]
613        fn without_snark_fields_preserves_none_values() {
614            let signer = signer_message_part_without_snark_fields();
615            assert!(signer.verification_key_for_snark.is_none());
616            assert!(signer.verification_key_signature_for_snark.is_none());
617
618            let stripped_signer = signer.without_snark_fields();
619
620            assert!(stripped_signer.verification_key_for_snark.is_none());
621            assert!(stripped_signer.verification_key_signature_for_snark.is_none());
622        }
623
624        #[test]
625        fn strip_snark_fields_clears_all_entries() {
626            let signers = vec![
627                signer_message_part_with_snark_fields(),
628                signer_message_part_without_snark_fields(),
629            ];
630
631            let stripped_signers = SignerMessagePart::strip_snark_fields(signers);
632
633            let expected_stripped_signers = vec![
634                signer_message_part_without_snark_fields(),
635                signer_message_part_without_snark_fields(),
636            ];
637            assert_eq!(expected_stripped_signers, stripped_signers)
638        }
639
640        #[test]
641        fn strip_snark_fields_handles_empty_list() {
642            let stripped_signers = SignerMessagePart::strip_snark_fields(vec![]);
643            assert!(stripped_signers.is_empty());
644        }
645    }
646}