Skip to main content

mithril_stm/protocol/key_registration/
registration_entry.rs

1use std::cmp::Ordering;
2use std::hash::Hash;
3
4use serde::{Deserialize, Serialize};
5
6#[cfg(feature = "future_snark")]
7use crate::VerificationKeyForSnark;
8use crate::{
9    Initializer, RegisterError, Stake, StmResult, VerificationKeyForConcatenation,
10    VerificationKeyProofOfPossessionForConcatenation,
11};
12
13use super::ClosedRegistrationEntry;
14
15/// Represents a signer registration entry
16#[derive(PartialEq, Eq, Clone, Debug, Copy, Serialize, Deserialize)]
17pub struct RegistrationEntry(
18    VerificationKeyForConcatenation,
19    Stake,
20    #[cfg(feature = "future_snark")]
21    #[serde(skip_serializing_if = "Option::is_none")]
22    Option<VerificationKeyForSnark>,
23);
24
25impl RegistrationEntry {
26    /// Creates a new registration entry. Verifies the proof of possession of verification key for
27    /// concatenation and validates the schnorr verification key before creating the entry.
28    pub fn new(
29        bls_verification_key_proof_of_possession: VerificationKeyProofOfPossessionForConcatenation,
30        stake: Stake,
31        #[cfg(feature = "future_snark")] schnorr_verification_key: Option<VerificationKeyForSnark>,
32    ) -> StmResult<Self> {
33        bls_verification_key_proof_of_possession
34            .verify_proof_of_possession()
35            .map_err(|_| {
36                RegisterError::ConcatenationKeyInvalid(Box::new(
37                    bls_verification_key_proof_of_possession.vk,
38                ))
39            })?;
40
41        #[cfg(feature = "future_snark")]
42        schnorr_verification_key
43            .map(|schnorr_vk| {
44                schnorr_vk
45                    .is_valid()
46                    .map_err(|_| RegisterError::SnarkKeyInvalid(Box::new(schnorr_vk)))
47            })
48            .transpose()?;
49
50        Ok(RegistrationEntry(
51            bls_verification_key_proof_of_possession.vk,
52            stake,
53            #[cfg(feature = "future_snark")]
54            schnorr_verification_key,
55        ))
56    }
57
58    /// Gets the verification key for concatenation.
59    pub fn get_verification_key_for_concatenation(&self) -> VerificationKeyForConcatenation {
60        self.0
61    }
62
63    #[cfg(feature = "future_snark")]
64    /// Gets the verification key for snark.
65    pub fn get_verification_key_for_snark(&self) -> Option<VerificationKeyForSnark> {
66        self.2
67    }
68
69    /// Gets the stake associated with the registration entry.
70    pub fn get_stake(&self) -> Stake {
71        self.1
72    }
73}
74
75impl From<ClosedRegistrationEntry> for RegistrationEntry {
76    fn from(entry: ClosedRegistrationEntry) -> Self {
77        RegistrationEntry(
78            entry.get_verification_key_for_concatenation(),
79            entry.get_stake(),
80            #[cfg(feature = "future_snark")]
81            entry.get_verification_key_for_snark(),
82        )
83    }
84}
85
86impl TryFrom<Initializer> for RegistrationEntry {
87    type Error = anyhow::Error;
88
89    fn try_from(initializer: Initializer) -> StmResult<Self> {
90        Self::new(
91            initializer.bls_verification_key_proof_of_possession,
92            initializer.stake,
93            #[cfg(feature = "future_snark")]
94            initializer.schnorr_verification_key,
95        )
96    }
97}
98
99impl Hash for RegistrationEntry {
100    /// Hashes the registration entry by hashing the stake first, then the verification key.
101    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
102        self.1.hash(state);
103        self.0.hash(state);
104        #[cfg(feature = "future_snark")]
105        self.2.hash(state);
106    }
107
108    fn hash_slice<H: std::hash::Hasher>(data: &[Self], state: &mut H)
109    where
110        Self: Sized,
111    {
112        for piece in data {
113            piece.hash(state)
114        }
115    }
116}
117
118impl PartialOrd for RegistrationEntry {
119    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
120        Some(std::cmp::Ord::cmp(self, other))
121    }
122}
123
124impl Ord for RegistrationEntry {
125    /// Compares the registration entries by comparing the stake first, then the verification key.
126    fn cmp(&self, other: &Self) -> Ordering {
127        self.1.cmp(&other.1).then(self.0.cmp(&other.0))
128    }
129}
130
131#[cfg(test)]
132mod tests {
133    use rand_chacha::ChaCha20Rng;
134    use rand_core::SeedableRng;
135    use std::cmp::Ordering;
136
137    use crate::{
138        VerificationKeyProofOfPossessionForConcatenation, signature_scheme::BlsSigningKey,
139    };
140
141    #[cfg(feature = "future_snark")]
142    use crate::{VerificationKeyForSnark, signature_scheme::SchnorrSigningKey};
143
144    use super::*;
145
146    fn create_registration_entry(rng: &mut ChaCha20Rng, stake: Stake) -> RegistrationEntry {
147        let bls_sk = BlsSigningKey::generate(rng);
148        let bls_pk = VerificationKeyProofOfPossessionForConcatenation::from(&bls_sk);
149
150        #[cfg(feature = "future_snark")]
151        let schnorr_verification_key = {
152            let sk = SchnorrSigningKey::generate(rng);
153            VerificationKeyForSnark::new_from_signing_key(sk)
154        };
155        RegistrationEntry::new(
156            bls_pk,
157            stake,
158            #[cfg(feature = "future_snark")]
159            Some(schnorr_verification_key),
160        )
161        .unwrap()
162    }
163
164    #[test]
165    fn test_ord_different_stakes() {
166        let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
167
168        let entry_low_stake = create_registration_entry(&mut rng, 100);
169        let entry_high_stake = create_registration_entry(&mut rng, 200);
170
171        assert_eq!(entry_low_stake.cmp(&entry_high_stake), Ordering::Less);
172        assert_eq!(entry_high_stake.cmp(&entry_low_stake), Ordering::Greater);
173    }
174
175    #[test]
176    fn test_ord_same_stake_different_keys() {
177        let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
178
179        let entry1 = create_registration_entry(&mut rng, 100);
180        let entry2 = create_registration_entry(&mut rng, 100);
181
182        let cmp_result = entry1.cmp(&entry2);
183        assert!(cmp_result == Ordering::Less || cmp_result == Ordering::Greater);
184
185        assert_eq!(entry2.cmp(&entry1), cmp_result.reverse());
186    }
187}