mithril_stm/protocol/participant/
initializer.rs

1use anyhow::anyhow;
2use rand_core::{CryptoRng, RngCore};
3use serde::{Deserialize, Serialize};
4
5use crate::{
6    ClosedKeyRegistration, MembershipDigest, Parameters, RegisterError, RegistrationEntry,
7    RegistrationEntryForConcatenation, Stake, StmResult,
8    VerificationKeyProofOfPossessionForConcatenation, proof_system::ConcatenationProofSigner,
9    signature_scheme::BlsSigningKey,
10};
11#[cfg(feature = "future_snark")]
12use crate::{VerificationKeyForSnark, signature_scheme::SchnorrSigningKey};
13
14use super::Signer;
15
16/// Structure responsible of creating a signer
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct Initializer {
19    /// Stake of the participant
20    pub stake: Stake,
21    /// Protocol parameters.
22    #[serde(rename = "params")]
23    pub parameters: Parameters,
24    /// Signing key for concatenation proof system.
25    #[serde(rename = "sk")]
26    pub bls_signing_key: BlsSigningKey,
27    /// Verification key for concatenation proof system.
28    #[serde(rename = "pk")]
29    pub bls_verification_key_proof_of_possession: VerificationKeyProofOfPossessionForConcatenation,
30    /// Signing key for snark proof system.
31    #[cfg(feature = "future_snark")]
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub schnorr_signing_key: Option<SchnorrSigningKey>,
34    /// Verification key for snark proof system.
35    #[cfg(feature = "future_snark")]
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub schnorr_verification_key: Option<VerificationKeyForSnark>,
38}
39
40impl Initializer {
41    /// Creates a new initializer
42    pub fn new<R: RngCore + CryptoRng>(parameters: Parameters, stake: Stake, rng: &mut R) -> Self {
43        let bls_signing_key = BlsSigningKey::generate(rng);
44        let bls_verification_key_proof_of_possession =
45            VerificationKeyProofOfPossessionForConcatenation::from(&bls_signing_key);
46        #[cfg(feature = "future_snark")]
47        let (schnorr_signing_key, schnorr_verification_key) = {
48            let sk = SchnorrSigningKey::generate(rng);
49            let vk = VerificationKeyForSnark::new_from_signing_key(sk.clone());
50            (Some(sk), Some(vk))
51        };
52        Self {
53            stake,
54            parameters,
55            bls_signing_key,
56            bls_verification_key_proof_of_possession,
57            #[cfg(feature = "future_snark")]
58            schnorr_signing_key,
59            #[cfg(feature = "future_snark")]
60            schnorr_verification_key,
61        }
62    }
63
64    /// Attempts to generate a new signer from the current registration state.
65    ///
66    /// # Process
67    /// 1. Verifies that registration is closed (determined by total stake threshold)
68    /// 2. Confirms the initializer is registered and retrieves its signer index
69    /// 3. Constructs the Merkle tree commitment
70    /// 4. Creates the underlying proof system signer (currently only the concatenation proof system)
71    ///
72    /// # Errors
73    /// Returns an error if:
74    /// - Registration is not yet closed
75    /// - The initializer is not registered
76    pub fn try_create_signer<D: MembershipDigest>(
77        self,
78        closed_key_registration: &ClosedKeyRegistration,
79    ) -> StmResult<Signer<D>> {
80        let registration_entry = RegistrationEntry::new(
81            self.bls_verification_key_proof_of_possession,
82            self.stake,
83            #[cfg(feature = "future_snark")]
84            self.schnorr_verification_key,
85        )?;
86
87        let signer_index = closed_key_registration
88            .get_signer_index_for_registration(
89                &(registration_entry, closed_key_registration.total_stake).into(),
90            )
91            .ok_or_else(|| anyhow!(RegisterError::UnregisteredInitializer))?;
92
93        let key_registration_commitment = closed_key_registration
94            .to_merkle_tree::<D::ConcatenationHash, RegistrationEntryForConcatenation>();
95
96        // Create concatenation proof signer
97        let concatenation_proof_signer = ConcatenationProofSigner::new(
98            registration_entry.get_stake(),
99            closed_key_registration.total_stake,
100            self.parameters,
101            self.bls_signing_key,
102            self.bls_verification_key_proof_of_possession.vk,
103            key_registration_commitment,
104        );
105
106        // Create and return signer
107        Ok(Signer::new(
108            signer_index,
109            concatenation_proof_signer,
110            closed_key_registration.clone(),
111            self.parameters,
112            registration_entry.get_stake(),
113        ))
114    }
115
116    /// Extract the verification key with proof of possession.
117    pub fn get_verification_key_proof_of_possession_for_concatenation(
118        &self,
119    ) -> VerificationKeyProofOfPossessionForConcatenation {
120        self.bls_verification_key_proof_of_possession
121    }
122
123    /// Extract the verification key for snark.
124    #[cfg(feature = "future_snark")]
125    pub fn get_verification_key_for_snark(&self) -> Option<VerificationKeyForSnark> {
126        self.schnorr_verification_key
127    }
128
129    /// Convert to bytes
130    /// # Layout
131    /// * Stake (u64)
132    /// * Params
133    /// * BLS signing key
134    /// * BLS verification key (including PoP)
135    /// * [Future Snark - Schnorr Signing Key]
136    /// * [Future Snark - Schnorr Verification Key]
137    pub fn to_bytes(&self) -> Vec<u8> {
138        let capacity = if cfg!(feature = "future_snark") {
139            352
140        } else {
141            256
142        };
143        let mut out = Vec::with_capacity(capacity);
144        out.extend_from_slice(&self.stake.to_be_bytes());
145        out.extend_from_slice(&self.parameters.to_bytes());
146        out.extend_from_slice(&self.bls_signing_key.to_bytes());
147        out.extend_from_slice(&self.bls_verification_key_proof_of_possession.to_bytes());
148
149        #[cfg(feature = "future_snark")]
150        if let (Some(schnorr_sk), Some(schnorr_vk)) =
151            (&self.schnorr_signing_key, &self.schnorr_verification_key)
152        {
153            out.extend_from_slice(&schnorr_sk.to_bytes());
154            out.extend_from_slice(&schnorr_vk.to_bytes());
155        }
156
157        out
158    }
159
160    /// Convert a slice of bytes to an `Initializer`
161    /// # Error
162    /// The function fails if the given string of bytes is not of required size.
163    pub fn from_bytes(bytes: &[u8]) -> StmResult<Initializer> {
164        let mut u64_bytes = [0u8; 8];
165        u64_bytes.copy_from_slice(bytes.get(..8).ok_or(RegisterError::SerializationError)?);
166        let stake = u64::from_be_bytes(u64_bytes);
167        let params =
168            Parameters::from_bytes(bytes.get(8..32).ok_or(RegisterError::SerializationError)?)?;
169        let bls_signing_key =
170            BlsSigningKey::from_bytes(bytes.get(32..64).ok_or(RegisterError::SerializationError)?)?;
171        let bls_verification_key_proof_of_possession =
172            VerificationKeyProofOfPossessionForConcatenation::from_bytes(
173                bytes.get(64..256).ok_or(RegisterError::SerializationError)?,
174            )?;
175
176        #[cfg(feature = "future_snark")]
177        let (schnorr_signing_key, schnorr_verification_key) = {
178            let schnorr_signing_key =
179                bytes.get(256..288).map(SchnorrSigningKey::from_bytes).transpose()?;
180            let schnorr_verification_key = bytes
181                .get(288..352)
182                .map(VerificationKeyForSnark::from_bytes)
183                .transpose()?;
184
185            match (&schnorr_signing_key, &schnorr_verification_key) {
186                (Some(_), None) | (None, Some(_)) => {
187                    return Err(RegisterError::SerializationError.into());
188                }
189                _ => {}
190            }
191            (schnorr_signing_key, schnorr_verification_key)
192        };
193
194        Ok(Self {
195            stake,
196            parameters: params,
197            bls_signing_key,
198            bls_verification_key_proof_of_possession,
199            #[cfg(feature = "future_snark")]
200            schnorr_signing_key,
201            #[cfg(feature = "future_snark")]
202            schnorr_verification_key,
203        })
204    }
205}
206
207impl PartialEq for Initializer {
208    fn eq(&self, other: &Self) -> bool {
209        let base_eq = self.stake == other.stake
210            && self.parameters == other.parameters
211            && self.bls_signing_key.to_bytes() == other.bls_signing_key.to_bytes()
212            && self.get_verification_key_proof_of_possession_for_concatenation()
213                == other.get_verification_key_proof_of_possession_for_concatenation();
214
215        #[cfg(feature = "future_snark")]
216        let base_eq = base_eq
217            && self.schnorr_signing_key == other.schnorr_signing_key
218            && self.schnorr_verification_key == other.schnorr_verification_key;
219
220        base_eq
221    }
222}
223
224#[cfg(test)]
225mod tests {
226    use super::*;
227
228    mod golden {
229        use rand_chacha::ChaCha20Rng;
230        use rand_core::SeedableRng;
231
232        use super::*;
233
234        const GOLDEN_JSON: &str = r#"
235            {
236                "stake":1,
237                "params":
238                {
239                    "m":20973,
240                    "k":2422,
241                    "phi_f":0.2
242                },
243                "sk":[64,129,87,121,27,239,221,215,2,103,45,207,207,201,157,163,81,47,156,14,168,24,137,15,203,106,183,73,88,14,242,207],
244                "pk":
245                {
246                    "vk":[143,161,255,48,78,57,204,220,25,221,164,252,248,14,56,126,186,135,228,188,145,181,52,200,97,99,213,46,0,199,193,89,187,88,29,135,173,244,86,36,83,54,67,164,6,137,94,72,6,105,128,128,93,48,176,11,4,246,138,48,180,133,90,142,192,24,193,111,142,31,76,111,110,234,153,90,208,192,31,124,95,102,49,158,99,52,220,165,94,251,68,69,121,16,224,194],
247                    "pop":[168,50,233,193,15,136,65,72,123,148,129,176,38,198,209,47,28,204,176,144,57,251,42,28,66,76,89,97,158,63,54,198,194,176,135,221,14,185,197,225,202,98,243,74,233,225,143,151,147,177,170,117,66,165,66,62,33,216,232,75,68,114,195,22,100,65,44,198,4,166,102,233,253,240,59,175,60,117,142,114,140,122,17,87,110,187,1,17,10,195,154,13,249,86,54,226]
248                }
249            }
250        "#;
251
252        fn golden_value() -> Initializer {
253            let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
254            let sk = BlsSigningKey::generate(&mut rng);
255            let pk = VerificationKeyProofOfPossessionForConcatenation::from(&sk);
256            Initializer {
257                stake: 1,
258                parameters: Parameters {
259                    m: 20973,
260                    k: 2422,
261                    phi_f: 0.2,
262                },
263                bls_signing_key: sk,
264                bls_verification_key_proof_of_possession: pk,
265                #[cfg(feature = "future_snark")]
266                schnorr_signing_key: None,
267                #[cfg(feature = "future_snark")]
268                schnorr_verification_key: None,
269            }
270        }
271
272        #[test]
273        fn golden_conversions() {
274            let value = serde_json::from_str(GOLDEN_JSON)
275                .expect("This JSON deserialization should not fail");
276            assert_eq!(golden_value(), value);
277
278            let serialized =
279                serde_json::to_string(&value).expect("This JSON serialization should not fail");
280            let golden_serialized = serde_json::to_string(&golden_value())
281                .expect("This JSON serialization should not fail");
282            assert_eq!(golden_serialized, serialized);
283        }
284    }
285}