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 From<Initializer> for RegistrationEntry {
87    fn from(initializer: Initializer) -> Self {
88        Self(
89            initializer.bls_verification_key_proof_of_possession.vk,
90            initializer.stake,
91            #[cfg(feature = "future_snark")]
92            initializer.schnorr_verification_key,
93        )
94    }
95}
96
97impl Hash for RegistrationEntry {
98    /// Hashes the registration entry by hashing the stake first, then the verification key.
99    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
100        self.1.hash(state);
101        self.0.hash(state);
102        #[cfg(feature = "future_snark")]
103        self.2.hash(state);
104    }
105
106    fn hash_slice<H: std::hash::Hasher>(data: &[Self], state: &mut H)
107    where
108        Self: Sized,
109    {
110        for piece in data {
111            piece.hash(state)
112        }
113    }
114}
115
116impl PartialOrd for RegistrationEntry {
117    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
118        Some(std::cmp::Ord::cmp(self, other))
119    }
120}
121
122impl Ord for RegistrationEntry {
123    /// Compares the registration entries by comparing the stake first, then the verification key.
124    fn cmp(&self, other: &Self) -> Ordering {
125        self.1.cmp(&other.1).then(self.0.cmp(&other.0))
126    }
127}
128
129#[cfg(test)]
130mod tests {
131    use rand_chacha::ChaCha20Rng;
132    use rand_core::SeedableRng;
133    use std::cmp::Ordering;
134
135    use crate::{
136        VerificationKeyProofOfPossessionForConcatenation, signature_scheme::BlsSigningKey,
137    };
138
139    #[cfg(feature = "future_snark")]
140    use crate::{VerificationKeyForSnark, signature_scheme::SchnorrSigningKey};
141
142    use super::*;
143
144    fn create_registration_entry(rng: &mut ChaCha20Rng, stake: Stake) -> RegistrationEntry {
145        let bls_sk = BlsSigningKey::generate(rng);
146        let bls_pk = VerificationKeyProofOfPossessionForConcatenation::from(&bls_sk);
147
148        #[cfg(feature = "future_snark")]
149        let schnorr_verification_key = {
150            let sk = SchnorrSigningKey::generate(rng);
151            VerificationKeyForSnark::new_from_signing_key(sk)
152        };
153        RegistrationEntry::new(
154            bls_pk,
155            stake,
156            #[cfg(feature = "future_snark")]
157            Some(schnorr_verification_key),
158        )
159        .unwrap()
160    }
161
162    #[test]
163    fn test_ord_different_stakes() {
164        let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
165
166        let entry_low_stake = create_registration_entry(&mut rng, 100);
167        let entry_high_stake = create_registration_entry(&mut rng, 200);
168
169        assert_eq!(entry_low_stake.cmp(&entry_high_stake), Ordering::Less);
170        assert_eq!(entry_high_stake.cmp(&entry_low_stake), Ordering::Greater);
171    }
172
173    #[test]
174    fn test_ord_same_stake_different_keys() {
175        let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
176
177        let entry1 = create_registration_entry(&mut rng, 100);
178        let entry2 = create_registration_entry(&mut rng, 100);
179
180        let cmp_result = entry1.cmp(&entry2);
181        assert!(cmp_result == Ordering::Less || cmp_result == Ordering::Greater);
182
183        assert_eq!(entry2.cmp(&entry1), cmp_result.reverse());
184    }
185}