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
12use super::Signer;
13
14/// Structure responsible of creating a signer
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct Initializer {
17    /// Stake of the participant
18    pub stake: Stake,
19    /// Protocol parameters.
20    #[serde(rename = "params")]
21    pub parameters: Parameters,
22    /// Signing key for concatenation proof system.
23    #[serde(rename = "sk")]
24    pub bls_signing_key: BlsSigningKey,
25    /// Verification key for concatenation proof system.
26    #[serde(rename = "pk")]
27    pub bls_verification_key_proof_of_possession: VerificationKeyProofOfPossessionForConcatenation,
28}
29
30impl Initializer {
31    /// Creates a new initializer
32    pub fn new<R: RngCore + CryptoRng>(parameters: Parameters, stake: Stake, rng: &mut R) -> Self {
33        let bls_signing_key = BlsSigningKey::generate(rng);
34        let bls_verification_key_proof_of_possession =
35            VerificationKeyProofOfPossessionForConcatenation::from(&bls_signing_key);
36        Self {
37            stake,
38            parameters,
39            bls_signing_key,
40            bls_verification_key_proof_of_possession,
41        }
42    }
43
44    /// Attempts to generate a new signer from the current registration state.
45    ///
46    /// # Process
47    /// 1. Verifies that registration is closed (determined by total stake threshold)
48    /// 2. Confirms the initializer is registered and retrieves its signer index
49    /// 3. Constructs the Merkle tree commitment
50    /// 4. Creates the underlying proof system signer (currently only the concatenation proof system)
51    ///
52    /// # Errors
53    /// Returns an error if:
54    /// - Registration is not yet closed
55    /// - The initializer is not registered
56    pub fn try_create_signer<D: MembershipDigest>(
57        self,
58        closed_key_registration: &ClosedKeyRegistration,
59    ) -> StmResult<Signer<D>> {
60        let registration_entry =
61            RegistrationEntry::new(self.bls_verification_key_proof_of_possession, self.stake)?;
62
63        let signer_index = match closed_key_registration
64            .key_registration
65            .get_signer_index_for_registration(&registration_entry)
66        {
67            Some(index) => index,
68            None => return Err(anyhow!(RegisterError::UnregisteredInitializer)),
69        };
70
71        let key_registration_commitment = closed_key_registration
72            .key_registration
73            .into_merkle_tree::<D::ConcatenationHash, RegistrationEntryForConcatenation>(
74        );
75
76        // Create concatenation proof signer
77        let concatenation_proof_signer = ConcatenationProofSigner::new(
78            registration_entry.get_stake(),
79            closed_key_registration.total_stake,
80            self.parameters,
81            self.bls_signing_key,
82            self.bls_verification_key_proof_of_possession.vk,
83            key_registration_commitment,
84        );
85
86        // Create and return signer
87        Ok(Signer::new(
88            signer_index,
89            concatenation_proof_signer,
90            closed_key_registration.clone(),
91            self.parameters,
92            registration_entry.get_stake(),
93        ))
94    }
95
96    /// Extract the verification key with proof of possession.
97    pub fn get_verification_key_proof_of_possession_for_concatenation(
98        &self,
99    ) -> VerificationKeyProofOfPossessionForConcatenation {
100        self.bls_verification_key_proof_of_possession
101    }
102
103    /// Convert to bytes
104    /// # Layout
105    /// * Stake (u64)
106    /// * Params
107    /// * Secret Key
108    /// * Public key (including PoP)
109    pub fn to_bytes(&self) -> [u8; 256] {
110        let mut out = [0u8; 256];
111        out[..8].copy_from_slice(&self.stake.to_be_bytes());
112        out[8..32].copy_from_slice(&self.parameters.to_bytes());
113        out[32..64].copy_from_slice(&self.bls_signing_key.to_bytes());
114        out[64..].copy_from_slice(&self.bls_verification_key_proof_of_possession.to_bytes());
115        out
116    }
117
118    /// Convert a slice of bytes to an `Initializer`
119    /// # Error
120    /// The function fails if the given string of bytes is not of required size.
121    pub fn from_bytes(bytes: &[u8]) -> StmResult<Initializer> {
122        let mut u64_bytes = [0u8; 8];
123        u64_bytes.copy_from_slice(bytes.get(..8).ok_or(RegisterError::SerializationError)?);
124        let stake = u64::from_be_bytes(u64_bytes);
125        let params =
126            Parameters::from_bytes(bytes.get(8..32).ok_or(RegisterError::SerializationError)?)?;
127        let sk =
128            BlsSigningKey::from_bytes(bytes.get(32..).ok_or(RegisterError::SerializationError)?)?;
129        let pk = VerificationKeyProofOfPossessionForConcatenation::from_bytes(
130            bytes.get(64..).ok_or(RegisterError::SerializationError)?,
131        )?;
132
133        Ok(Self {
134            stake,
135            parameters: params,
136            bls_signing_key: sk,
137            bls_verification_key_proof_of_possession: pk,
138        })
139    }
140}
141
142impl PartialEq for Initializer {
143    fn eq(&self, other: &Self) -> bool {
144        self.stake == other.stake
145            && self.parameters == other.parameters
146            && self.bls_signing_key.to_bytes() == other.bls_signing_key.to_bytes()
147            && self.get_verification_key_proof_of_possession_for_concatenation()
148                == other.get_verification_key_proof_of_possession_for_concatenation()
149    }
150}
151
152#[cfg(test)]
153mod tests {
154    use super::*;
155
156    mod golden {
157        use rand_chacha::ChaCha20Rng;
158        use rand_core::SeedableRng;
159
160        use super::*;
161
162        const GOLDEN_JSON: &str = r#"
163            {
164                "stake":1,
165                "params":
166                {
167                    "m":20973,
168                    "k":2422,
169                    "phi_f":0.2
170                },
171                "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],
172                "pk":
173                {
174                    "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],
175                    "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]
176                }
177            }
178        "#;
179
180        fn golden_value() -> Initializer {
181            let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
182            let sk = BlsSigningKey::generate(&mut rng);
183            let pk = VerificationKeyProofOfPossessionForConcatenation::from(&sk);
184            Initializer {
185                stake: 1,
186                parameters: Parameters {
187                    m: 20973,
188                    k: 2422,
189                    phi_f: 0.2,
190                },
191                bls_signing_key: sk,
192                bls_verification_key_proof_of_possession: pk,
193            }
194        }
195
196        #[test]
197        fn golden_conversions() {
198            let value = serde_json::from_str(GOLDEN_JSON)
199                .expect("This JSON deserialization should not fail");
200            assert_eq!(golden_value(), value);
201
202            let serialized =
203                serde_json::to_string(&value).expect("This JSON serialization should not fail");
204            let golden_serialized = serde_json::to_string(&golden_value())
205                .expect("This JSON serialization should not fail");
206            assert_eq!(golden_serialized, serialized);
207        }
208    }
209}