Skip to main content

mithril_stm/proof_system/concatenation/
aggregate_key.rs

1use serde::{Deserialize, Serialize};
2
3use crate::{
4    ClosedKeyRegistration, MembershipDigest, RegistrationEntryForConcatenation, Stake, StmResult,
5    codec,
6    membership_commitment::{
7        MerkleBatchPath, MerkleTreeBatchCommitment, MerkleTreeConcatenationLeaf, MerkleTreeError,
8    },
9};
10
11/// Aggregate verification key of the concatenation proof system.
12#[derive(Debug, Clone, Serialize, Deserialize)]
13#[serde(bound(
14    serialize = "MerkleBatchPath<D::ConcatenationHash>: Serialize",
15    deserialize = "MerkleBatchPath<D::ConcatenationHash>: Deserialize<'de>"
16))]
17pub struct AggregateVerificationKeyForConcatenation<D: MembershipDigest> {
18    mt_commitment: MerkleTreeBatchCommitment<D::ConcatenationHash, MerkleTreeConcatenationLeaf>,
19    total_stake: Stake,
20}
21
22impl<D: MembershipDigest> AggregateVerificationKeyForConcatenation<D> {
23    /// Get the Merkle tree batch commitment.
24    pub(crate) fn get_merkle_tree_batch_commitment(
25        &self,
26    ) -> MerkleTreeBatchCommitment<D::ConcatenationHash, MerkleTreeConcatenationLeaf> {
27        self.mt_commitment.clone()
28    }
29
30    /// Get the total stake.
31    pub fn get_total_stake(&self) -> Stake {
32        self.total_stake
33    }
34
35    /// Convert the aggregate verification key for concatenation to bytes.
36    pub fn to_bytes(&self) -> StmResult<Vec<u8>> {
37        codec::to_cbor_bytes(self)
38    }
39
40    /// Deserialize an aggregate verification key for concatenation from bytes.
41    ///
42    /// Supports both CBOR-encoded (version-prefixed) and legacy formats.
43    pub fn from_bytes(bytes: &[u8]) -> StmResult<Self> {
44        codec::from_versioned_bytes(bytes, Self::from_bytes_legacy)
45    }
46
47    fn from_bytes_legacy(bytes: &[u8]) -> StmResult<Self> {
48        let mut u64_bytes = [0u8; 8];
49        let size = bytes.len();
50
51        u64_bytes.copy_from_slice(&bytes[size - 8..]);
52        let stake = u64::from_be_bytes(u64_bytes);
53        let mt_commitment = MerkleTreeBatchCommitment::from_bytes(
54            bytes.get(..size - 8).ok_or(MerkleTreeError::SerializationError)?,
55        )?;
56        Ok(Self {
57            mt_commitment,
58            total_stake: stake,
59        })
60    }
61}
62
63impl<D: MembershipDigest> PartialEq for AggregateVerificationKeyForConcatenation<D> {
64    fn eq(&self, other: &Self) -> bool {
65        self.mt_commitment == other.mt_commitment && self.total_stake == other.total_stake
66    }
67}
68
69impl<D: MembershipDigest> Eq for AggregateVerificationKeyForConcatenation<D> {}
70
71impl<D: MembershipDigest> From<&ClosedKeyRegistration>
72    for AggregateVerificationKeyForConcatenation<D>
73{
74    fn from(reg: &ClosedKeyRegistration) -> Self {
75        Self {
76            mt_commitment: reg
77                .to_merkle_tree::<D::ConcatenationHash, RegistrationEntryForConcatenation>()
78                .to_merkle_tree_batch_commitment(),
79            total_stake: reg.total_stake,
80        }
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use rand_chacha::ChaCha20Rng;
87    use rand_core::SeedableRng;
88
89    use crate::{
90        KeyRegistration, MithrilMembershipDigest, Parameters, RegistrationEntry,
91        VerificationKeyProofOfPossessionForConcatenation,
92        proof_system::AggregateVerificationKeyForConcatenation,
93        proof_system::concatenation::clerk::ConcatenationClerk, signature_scheme::BlsSigningKey,
94    };
95
96    type D = MithrilMembershipDigest;
97    mod golden {
98        use super::*;
99
100        const GOLDEN_BYTES: &[u8; 48] = &[
101            0, 0, 0, 0, 0, 0, 0, 2, 56, 37, 95, 107, 157, 98, 252, 194, 190, 204, 170, 26, 224, 10,
102            212, 7, 214, 89, 116, 196, 217, 122, 111, 56, 113, 253, 96, 45, 170, 121, 235, 159, 0,
103            0, 0, 0, 0, 0, 0, 2,
104        ];
105
106        fn golden_value() -> AggregateVerificationKeyForConcatenation<D> {
107            let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
108            let params = Parameters {
109                m: 10,
110                k: 5,
111                phi_f: 0.8,
112            };
113            let sk_1 = BlsSigningKey::generate(&mut rng);
114            let sk_2 = BlsSigningKey::generate(&mut rng);
115            let pk_1 = VerificationKeyProofOfPossessionForConcatenation::from(&sk_1);
116            let pk_2 = VerificationKeyProofOfPossessionForConcatenation::from(&sk_2);
117
118            let mut key_reg = KeyRegistration::initialize();
119            let entry1 = RegistrationEntry::new(
120                pk_1,
121                1,
122                #[cfg(feature = "future_snark")]
123                None,
124            )
125            .unwrap();
126            let entry2 = RegistrationEntry::new(
127                pk_2,
128                1,
129                #[cfg(feature = "future_snark")]
130                None,
131            )
132            .unwrap();
133
134            key_reg.register_by_entry(&entry1).unwrap();
135            key_reg.register_by_entry(&entry2).unwrap();
136            let closed_key_reg = key_reg.close_registration(&params).unwrap();
137
138            let clerk = ConcatenationClerk::new_clerk_from_closed_key_registration(
139                &params,
140                &closed_key_reg,
141            );
142
143            clerk.compute_aggregate_verification_key_for_concatenation()
144        }
145
146        #[test]
147        fn golden_conversions() {
148            let value = AggregateVerificationKeyForConcatenation::from_bytes(GOLDEN_BYTES)
149                .expect("This from bytes should not fail");
150            assert_eq!(golden_value(), value);
151
152            let serialized = AggregateVerificationKeyForConcatenation::to_bytes(&value)
153                .expect("AggregateVerificationKeyForConcatenation serialization should not fail");
154            let golden_serialized = AggregateVerificationKeyForConcatenation::to_bytes(
155                &golden_value(),
156            )
157            .expect("AggregateVerificationKeyForConcatenation serialization should not fail");
158            assert_eq!(golden_serialized, serialized);
159        }
160
161        const GOLDEN_CBOR_BYTES: &[u8; 118] = &[
162            1, 162, 109, 109, 116, 95, 99, 111, 109, 109, 105, 116, 109, 101, 110, 116, 163, 100,
163            114, 111, 111, 116, 152, 32, 24, 56, 24, 37, 24, 95, 24, 107, 24, 157, 24, 98, 24, 252,
164            24, 194, 24, 190, 24, 204, 24, 170, 24, 26, 24, 224, 10, 24, 212, 7, 24, 214, 24, 89,
165            24, 116, 24, 196, 24, 217, 24, 122, 24, 111, 24, 56, 24, 113, 24, 253, 24, 96, 24, 45,
166            24, 170, 24, 121, 24, 235, 24, 159, 105, 110, 114, 95, 108, 101, 97, 118, 101, 115, 2,
167            102, 104, 97, 115, 104, 101, 114, 246, 107, 116, 111, 116, 97, 108, 95, 115, 116, 97,
168            107, 101, 2,
169        ];
170
171        #[test]
172        fn cbor_golden_bytes_can_be_decoded() {
173            let decoded = AggregateVerificationKeyForConcatenation::from_bytes(GOLDEN_CBOR_BYTES)
174                .expect("CBOR golden bytes deserialization should not fail");
175            assert_eq!(golden_value(), decoded);
176        }
177
178        #[test]
179        fn cbor_encoding_is_stable() {
180            let bytes = AggregateVerificationKeyForConcatenation::to_bytes(&golden_value())
181                .expect("AggregateVerificationKeyForConcatenation serialization should not fail");
182            assert_eq!(GOLDEN_CBOR_BYTES.as_slice(), bytes.as_slice());
183        }
184    }
185
186    mod golden_json {
187        use super::*;
188
189        const GOLDEN_JSON: &str = r#"
190        {
191            "mt_commitment":{
192                "root":[56,37,95,107,157,98,252,194,190,204,170,26,224,10,212,7,214,89,116,196,217,122,111,56,113,253,96,45,170,121,235,159],
193                "nr_leaves":2,
194                "hasher":null
195            },
196            "total_stake":2
197        }
198        "#;
199
200        fn golden_value() -> AggregateVerificationKeyForConcatenation<D> {
201            let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
202            let params = Parameters {
203                m: 10,
204                k: 5,
205                phi_f: 0.8,
206            };
207            let sk_1 = BlsSigningKey::generate(&mut rng);
208            let sk_2 = BlsSigningKey::generate(&mut rng);
209            let pk_1 = VerificationKeyProofOfPossessionForConcatenation::from(&sk_1);
210            let pk_2 = VerificationKeyProofOfPossessionForConcatenation::from(&sk_2);
211
212            let mut key_reg = KeyRegistration::initialize();
213            let entry1 = RegistrationEntry::new(
214                pk_1,
215                1,
216                #[cfg(feature = "future_snark")]
217                None,
218            )
219            .unwrap();
220            let entry2 = RegistrationEntry::new(
221                pk_2,
222                1,
223                #[cfg(feature = "future_snark")]
224                None,
225            )
226            .unwrap();
227
228            key_reg.register_by_entry(&entry1).unwrap();
229            key_reg.register_by_entry(&entry2).unwrap();
230            let closed_key_reg = key_reg.close_registration(&params).unwrap();
231
232            let clerk = ConcatenationClerk::new_clerk_from_closed_key_registration(
233                &params,
234                &closed_key_reg,
235            );
236
237            clerk.compute_aggregate_verification_key_for_concatenation()
238        }
239
240        #[test]
241        fn golden_conversions() {
242            let value: AggregateVerificationKeyForConcatenation<D> =
243                serde_json::from_str(GOLDEN_JSON)
244                    .expect("This JSON deserialization should not fail");
245
246            let serialized =
247                serde_json::to_string(&value).expect("This JSON serialization should not fail");
248            let golden_serialized = serde_json::to_string(&golden_value())
249                .expect("This JSON serialization should not fail");
250            assert_eq!(golden_serialized, serialized);
251        }
252    }
253}