mithril_common/entities/
signer.rs

1use crate::{
2    crypto_helper::{
3        KesEvolutions, ProtocolOpCert, ProtocolSignerVerificationKey,
4        ProtocolSignerVerificationKeySignature,
5    },
6    entities::{PartyId, Stake},
7};
8use std::fmt::{Debug, Formatter};
9
10use serde::{Deserialize, Serialize};
11use sha2::{Digest, Sha256};
12
13/// Signer represents a signing participant in the network
14#[derive(Clone, Eq, Serialize, Deserialize)]
15pub struct Signer {
16    /// The unique identifier of the signer
17    ///
18    /// Used only for testing when SPO pool id is not certified
19    pub party_id: PartyId,
20
21    /// The public key used to authenticate signer signature
22    pub verification_key: ProtocolSignerVerificationKey,
23
24    /// The encoded signer 'Mithril verification key' signature (signed by the Cardano node KES secret key)
25    ///
26    /// None is used only for testing when SPO pool id is not certified
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub verification_key_signature: Option<ProtocolSignerVerificationKeySignature>,
29
30    /// The encoded operational certificate of stake pool operator attached to the signer node
31    ///
32    /// None is used only for testing when SPO pool id is not certified
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub operational_certificate: Option<ProtocolOpCert>,
35
36    /// The number of evolutions of the KES key since the start KES period of the operational certificate at the time of signature.
37    #[serde(rename = "kes_period", skip_serializing_if = "Option::is_none")]
38    pub kes_evolutions: Option<KesEvolutions>,
39}
40
41impl PartialEq for Signer {
42    fn eq(&self, other: &Self) -> bool {
43        self.party_id.eq(&other.party_id)
44    }
45}
46
47impl PartialOrd for Signer {
48    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
49        Some(self.cmp(other))
50    }
51}
52
53impl Ord for Signer {
54    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
55        self.party_id.cmp(&other.party_id)
56    }
57}
58
59impl Signer {
60    /// Signer factory
61    pub fn new(
62        party_id: PartyId,
63        verification_key: ProtocolSignerVerificationKey,
64        verification_key_signature: Option<ProtocolSignerVerificationKeySignature>,
65        operational_certificate: Option<ProtocolOpCert>,
66        kes_evolutions: Option<KesEvolutions>,
67    ) -> Signer {
68        Signer {
69            party_id,
70            verification_key,
71            verification_key_signature,
72            operational_certificate,
73            kes_evolutions,
74        }
75    }
76
77    /// Convert the given values to a vec of signers.
78    pub fn vec_from<T: Into<Signer>>(from: Vec<T>) -> Vec<Self> {
79        from.into_iter().map(|f| f.into()).collect()
80    }
81
82    /// Computes the hash of Signer
83    pub fn compute_hash(&self) -> String {
84        let mut hasher = Sha256::new();
85        hasher.update(self.party_id.as_bytes());
86        hasher.update(self.verification_key.to_json_hex().unwrap().as_bytes());
87
88        if let Some(verification_key_signature) = &self.verification_key_signature {
89            hasher.update(verification_key_signature.to_json_hex().unwrap().as_bytes());
90        }
91        if let Some(operational_certificate) = &self.operational_certificate {
92            hasher.update(operational_certificate.to_json_hex().unwrap().as_bytes());
93        }
94        hex::encode(hasher.finalize())
95    }
96}
97
98impl Debug for Signer {
99    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
100        let should_be_exhaustive = f.alternate();
101        let mut debug = f.debug_struct("Signer");
102        debug.field("party_id", &self.party_id);
103
104        match should_be_exhaustive {
105            true => debug
106                .field(
107                    "verification_key",
108                    &format_args!("{:?}", self.verification_key),
109                )
110                .field(
111                    "verification_key_signature",
112                    &format_args!("{:?}", self.verification_key_signature),
113                )
114                .field(
115                    "operational_certificate",
116                    &format_args!("{:?}", self.operational_certificate),
117                )
118                .field("kes_evolutions", &format_args!("{:?}", self.kes_evolutions))
119                .finish(),
120            false => debug.finish_non_exhaustive(),
121        }
122    }
123}
124
125impl From<SignerWithStake> for Signer {
126    fn from(other: SignerWithStake) -> Self {
127        Signer::new(
128            other.party_id,
129            other.verification_key,
130            other.verification_key_signature,
131            other.operational_certificate,
132            other.kes_evolutions,
133        )
134    }
135}
136
137/// Signer represents a signing party in the network (including its stakes)
138#[derive(Clone, Eq, Serialize, Deserialize)]
139pub struct SignerWithStake {
140    /// The unique identifier of the signer
141    ///
142    /// Used only for testing when SPO pool id is not certified
143    pub party_id: PartyId,
144
145    /// The public key used to authenticate signer signature
146    pub verification_key: ProtocolSignerVerificationKey,
147
148    /// The encoded signer 'Mithril verification key' signature (signed by the Cardano node KES secret key)
149    ///
150    /// None is used only for testing when SPO pool id is not certified
151    #[serde(skip_serializing_if = "Option::is_none")]
152    pub verification_key_signature: Option<ProtocolSignerVerificationKeySignature>,
153
154    /// The encoded operational certificate of stake pool operator attached to the signer node
155    ///
156    /// None is used only for testing when SPO pool id is not certified
157    #[serde(skip_serializing_if = "Option::is_none")]
158    pub operational_certificate: Option<ProtocolOpCert>,
159
160    /// The number of evolutions of the KES key since the start KES period of the operational certificate at the time of signature.
161    #[serde(rename = "kes_period", skip_serializing_if = "Option::is_none")]
162    pub kes_evolutions: Option<KesEvolutions>,
163
164    /// The signer stake
165    pub stake: Stake,
166}
167
168impl PartialEq for SignerWithStake {
169    fn eq(&self, other: &Self) -> bool {
170        self.party_id.eq(&other.party_id)
171    }
172}
173
174impl PartialOrd for SignerWithStake {
175    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
176        Some(self.cmp(other))
177    }
178}
179
180impl Ord for SignerWithStake {
181    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
182        self.party_id.cmp(&other.party_id)
183    }
184}
185
186impl SignerWithStake {
187    /// SignerWithStake factory
188    pub fn new(
189        party_id: PartyId,
190        verification_key: ProtocolSignerVerificationKey,
191        verification_key_signature: Option<ProtocolSignerVerificationKeySignature>,
192        operational_certificate: Option<ProtocolOpCert>,
193        kes_evolutions: Option<KesEvolutions>,
194        stake: Stake,
195    ) -> SignerWithStake {
196        SignerWithStake {
197            party_id,
198            verification_key,
199            verification_key_signature,
200            operational_certificate,
201            kes_evolutions,
202            stake,
203        }
204    }
205
206    /// Turn a [Signer] into a [SignerWithStake].
207    pub fn from_signer(signer: Signer, stake: Stake) -> Self {
208        Self {
209            party_id: signer.party_id,
210            verification_key: signer.verification_key,
211            verification_key_signature: signer.verification_key_signature,
212            operational_certificate: signer.operational_certificate,
213            kes_evolutions: signer.kes_evolutions,
214            stake,
215        }
216    }
217
218    /// Computes the hash of SignerWithStake
219    pub fn compute_hash(&self) -> String {
220        let mut hasher = Sha256::new();
221        hasher.update(self.party_id.as_bytes());
222        hasher.update(self.verification_key.to_json_hex().unwrap().as_bytes());
223
224        if let Some(verification_key_signature) = &self.verification_key_signature {
225            hasher.update(verification_key_signature.to_json_hex().unwrap().as_bytes());
226        }
227
228        if let Some(operational_certificate) = &self.operational_certificate {
229            hasher.update(operational_certificate.to_json_hex().unwrap().as_bytes());
230        }
231        hasher.update(self.stake.to_be_bytes());
232        hex::encode(hasher.finalize())
233    }
234}
235
236impl Debug for SignerWithStake {
237    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
238        let should_be_exhaustive = f.alternate();
239        let mut debug = f.debug_struct("SignerWithStake");
240        debug.field("party_id", &self.party_id).field("stake", &self.stake);
241
242        match should_be_exhaustive {
243            true => debug
244                .field(
245                    "verification_key",
246                    &format_args!("{:?}", self.verification_key),
247                )
248                .field(
249                    "verification_key_signature",
250                    &format_args!("{:?}", self.verification_key_signature),
251                )
252                .field(
253                    "operational_certificate",
254                    &format_args!("{:?}", self.operational_certificate),
255                )
256                .field("kes_evolutions", &format_args!("{:?}", self.kes_evolutions))
257                .finish(),
258            false => debug.finish_non_exhaustive(),
259        }
260    }
261}
262
263#[cfg(test)]
264mod tests {
265    use crate::test::{builder::MithrilFixtureBuilder, double::fake_keys};
266
267    use super::*;
268
269    #[test]
270    fn test_stake_signers_from_into() {
271        let verification_key = MithrilFixtureBuilder::default()
272            .with_signers(1)
273            .build()
274            .signers_with_stake()[0]
275            .verification_key;
276        let signer_expected = Signer::new("1".to_string(), verification_key, None, None, None);
277        let signer_with_stake =
278            SignerWithStake::new("1".to_string(), verification_key, None, None, None, 100);
279
280        let signer_into: Signer = signer_with_stake.into();
281        assert_eq!(signer_expected, signer_into);
282    }
283
284    #[test]
285    fn test_signer_compute_hash() {
286        const HASH_EXPECTED: &str =
287            "02778791113dcd8647b019366e223bfe3aa8a054fa6d9d1918b6b669de485f1c";
288
289        assert_eq!(
290            HASH_EXPECTED,
291            Signer::new(
292                "1".to_string(),
293                fake_keys::signer_verification_key()[3].try_into().unwrap(),
294                None,
295                None,
296                None,
297            )
298            .compute_hash()
299        );
300        assert_ne!(
301            HASH_EXPECTED,
302            Signer::new(
303                "0".to_string(),
304                fake_keys::signer_verification_key()[3].try_into().unwrap(),
305                None,
306                None,
307                None
308            )
309            .compute_hash()
310        );
311        assert_ne!(
312            HASH_EXPECTED,
313            Signer::new(
314                "1".to_string(),
315                fake_keys::signer_verification_key()[0].try_into().unwrap(),
316                None,
317                None,
318                None
319            )
320            .compute_hash()
321        );
322    }
323
324    #[test]
325    fn test_signer_with_stake_compute_hash() {
326        const EXPECTED_HASH: &str =
327            "9a832baccd04aabfc419f57319e3831a1655a95bf3bf5ed96a1167d1e81b5085";
328        let signers = MithrilFixtureBuilder::default()
329            .with_signers(2)
330            .build()
331            .signers_with_stake();
332        let signer = signers[0].clone();
333
334        assert_eq!(EXPECTED_HASH, signer.compute_hash());
335
336        {
337            let mut signer_different_party_id = signer.clone();
338            signer_different_party_id.party_id = "whatever".to_string();
339
340            assert_ne!(EXPECTED_HASH, signer_different_party_id.compute_hash());
341        }
342        {
343            let mut signer_different_verification_key = signer.clone();
344            signer_different_verification_key.verification_key = signers[1].verification_key;
345
346            assert_ne!(
347                EXPECTED_HASH,
348                signer_different_verification_key.compute_hash()
349            );
350        }
351        {
352            let mut signer_different_stake = signer.clone();
353            signer_different_stake.stake += 20;
354
355            assert_ne!(EXPECTED_HASH, signer_different_stake.compute_hash());
356        }
357    }
358}