mithril_common/entities/
signer.rs

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