1use std::{collections::HashMap, fmt::Display, hash::Hash, str::FromStr};
2
3use anyhow::anyhow;
4use serde::{Deserialize, Serialize};
5
6use crate::{
7 MembershipDigest, Parameters, StmError, StmResult, membership_commitment::MerkleBatchPath,
8 proof_system::ConcatenationProof,
9};
10
11use super::{AggregateSignatureError, AggregateVerificationKey};
12
13#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
15pub enum AggregateSignatureType {
16 #[default]
18 Concatenation,
19 #[cfg(feature = "future_snark")]
21 Future,
22}
23
24impl AggregateSignatureType {
25 pub fn get_byte_encoding_prefix(&self) -> u8 {
29 match self {
30 AggregateSignatureType::Concatenation => 0,
31 #[cfg(feature = "future_snark")]
32 AggregateSignatureType::Future => 255,
33 }
34 }
35
36 pub fn from_byte_encoding_prefix(byte: u8) -> Option<Self> {
40 match byte {
41 0 => Some(AggregateSignatureType::Concatenation),
42 #[cfg(feature = "future_snark")]
43 255 => Some(AggregateSignatureType::Future),
44 _ => None,
45 }
46 }
47}
48
49impl<D: MembershipDigest> From<&AggregateSignature<D>> for AggregateSignatureType {
50 fn from(aggr_sig: &AggregateSignature<D>) -> Self {
51 match aggr_sig {
52 AggregateSignature::Concatenation(_) => AggregateSignatureType::Concatenation,
53 #[cfg(feature = "future_snark")]
54 AggregateSignature::Future => AggregateSignatureType::Future,
55 }
56 }
57}
58
59impl FromStr for AggregateSignatureType {
60 type Err = StmError;
61
62 fn from_str(s: &str) -> Result<Self, Self::Err> {
63 match s {
64 "Concatenation" => Ok(AggregateSignatureType::Concatenation),
65 #[cfg(feature = "future_snark")]
66 "Future" => Ok(AggregateSignatureType::Future),
67 _ => Err(anyhow!("Unknown aggregate signature type: {}", s)),
68 }
69 }
70}
71
72impl Display for AggregateSignatureType {
73 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74 match self {
75 AggregateSignatureType::Concatenation => write!(f, "Concatenation"),
76 #[cfg(feature = "future_snark")]
77 AggregateSignatureType::Future => write!(f, "Future"),
78 }
79 }
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
84#[serde(bound(
85 serialize = "MerkleBatchPath<D::ConcatenationHash>: Serialize",
86 deserialize = "MerkleBatchPath<D::ConcatenationHash>: Deserialize<'de>"
87))]
88pub enum AggregateSignature<D: MembershipDigest> {
89 #[cfg(feature = "future_snark")]
91 Future,
92
93 #[serde(untagged)]
98 Concatenation(ConcatenationProof<D>),
99}
100
101impl<D: MembershipDigest> AggregateSignature<D> {
102 pub fn verify(
104 &self,
105 msg: &[u8],
106 avk: &AggregateVerificationKey<D>,
107 parameters: &Parameters,
108 ) -> StmResult<()> {
109 match self {
110 AggregateSignature::Concatenation(concatenation_proof) => {
111 concatenation_proof.verify(msg, avk, parameters)
112 }
113 #[cfg(feature = "future_snark")]
114 AggregateSignature::Future => Err(anyhow!(
115 AggregateSignatureError::UnsupportedProofSystem(self.into())
116 )),
117 }
118 }
119
120 pub fn batch_verify(
122 stm_signatures: &[Self],
123 msgs: &[Vec<u8>],
124 avks: &[AggregateVerificationKey<D>],
125 parameters: &[Parameters],
126 ) -> StmResult<()> {
127 let stm_signatures: HashMap<AggregateSignatureType, Vec<Self>> =
128 stm_signatures.iter().fold(HashMap::new(), |mut acc, sig| {
129 acc.entry(sig.into()).or_default().push(sig.clone());
130 acc
131 });
132 stm_signatures.into_iter().try_for_each(
133 |(aggregate_signature_type, aggregate_signatures)| match aggregate_signature_type {
134 AggregateSignatureType::Concatenation => {
135 let aggregate_signatures_length = aggregate_signatures.len();
136 let concatenation_proofs = aggregate_signatures
137 .into_iter()
138 .filter_map(|s| s.to_concatenation_proof().cloned())
139 .collect::<Vec<_>>();
140 if concatenation_proofs.len() != aggregate_signatures_length {
141 return Err(anyhow!(AggregateSignatureError::BatchInvalid));
142 }
143
144 ConcatenationProof::batch_verify(&concatenation_proofs, msgs, avks, parameters)
145 }
146 #[cfg(feature = "future_snark")]
147 AggregateSignatureType::Future => Err(anyhow!(
148 AggregateSignatureError::UnsupportedProofSystem(aggregate_signature_type)
149 )),
150 },
151 )
152 }
153
154 pub fn to_bytes(&self) -> Vec<u8> {
156 let mut aggregate_signature_bytes = Vec::new();
157 let aggregate_signature_type: AggregateSignatureType = self.into();
158 aggregate_signature_bytes
159 .extend_from_slice(&[aggregate_signature_type.get_byte_encoding_prefix()]);
160
161 let mut proof_bytes = match self {
162 AggregateSignature::Concatenation(concatenation_proof) => {
163 concatenation_proof.to_bytes()
164 }
165 #[cfg(feature = "future_snark")]
166 AggregateSignature::Future => vec![],
167 };
168 aggregate_signature_bytes.append(&mut proof_bytes);
169
170 aggregate_signature_bytes
171 }
172
173 pub fn from_bytes(bytes: &[u8]) -> StmResult<Self> {
175 let proof_type_byte = bytes.first().ok_or(AggregateSignatureError::SerializationError)?;
176 let proof_bytes = &bytes[1..];
177 let proof_type = AggregateSignatureType::from_byte_encoding_prefix(*proof_type_byte)
178 .ok_or(AggregateSignatureError::SerializationError)?;
179 match proof_type {
180 AggregateSignatureType::Concatenation => Ok(AggregateSignature::Concatenation(
181 ConcatenationProof::from_bytes(proof_bytes)?,
182 )),
183 #[cfg(feature = "future_snark")]
184 AggregateSignatureType::Future => Ok(AggregateSignature::Future),
185 }
186 }
187
188 pub fn to_concatenation_proof(&self) -> Option<&ConcatenationProof<D>> {
190 match self {
191 AggregateSignature::Concatenation(proof) => Some(proof),
192 #[cfg(feature = "future_snark")]
193 AggregateSignature::Future => None,
194 }
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201
202 mod aggregate_signature_type_golden {
203 use super::*;
204
205 #[test]
206 fn golden_bytes_encoding_prefix() {
207 assert_eq!(
208 0u8,
209 AggregateSignatureType::Concatenation.get_byte_encoding_prefix()
210 );
211 assert_eq!(
212 AggregateSignatureType::from_byte_encoding_prefix(0u8),
213 Some(AggregateSignatureType::Concatenation)
214 );
215 }
216 }
217
218 mod aggregate_signature_golden_concatenation {
219 use rand_chacha::ChaCha20Rng;
220 use rand_core::SeedableRng;
221
222 use super::{AggregateSignature, AggregateSignatureType};
223 use crate::{
224 Clerk, ClosedKeyRegistration, KeyRegistration, MithrilMembershipDigest, Parameters,
225 Signer,
226 signature_scheme::{BlsSigningKey, BlsVerificationKeyProofOfPossession},
227 };
228
229 type D = MithrilMembershipDigest;
230
231 const GOLDEN_JSON: &str = r#"
232 {
233 "signatures": [
234 [
235 {
236 "sigma": [
237 149, 157, 201, 187, 140, 54, 0, 128, 209, 88, 16, 203, 61, 78, 77, 98,
238 161, 133, 58, 152, 29, 74, 217, 113, 64, 100, 10, 161, 186, 167, 133,
239 114, 211, 153, 218, 56, 223, 84, 105, 242, 41, 54, 224, 170, 208, 185,
240 126, 83
241 ],
242 "indexes": [1, 4, 5, 8],
243 "signer_index": 0
244 },
245 [
246 [
247 143, 161, 255, 48, 78, 57, 204, 220, 25, 221, 164, 252, 248, 14, 56,
248 126, 186, 135, 228, 188, 145, 181, 52, 200, 97, 99, 213, 46, 0, 199,
249 193, 89, 187, 88, 29, 135, 173, 244, 86, 36, 83, 54, 67, 164, 6, 137,
250 94, 72, 6, 105, 128, 128, 93, 48, 176, 11, 4, 246, 138, 48, 180, 133,
251 90, 142, 192, 24, 193, 111, 142, 31, 76, 111, 110, 234, 153, 90, 208,
252 192, 31, 124, 95, 102, 49, 158, 99, 52, 220, 165, 94, 251, 68, 69,
253 121, 16, 224, 194
254 ],
255 1
256 ]
257 ],
258 [
259 {
260 "sigma": [
261 149, 169, 22, 201, 216, 97, 163, 188, 115, 210, 217, 236, 233, 161,
262 201, 13, 42, 132, 12, 63, 5, 31, 120, 22, 78, 177, 125, 134, 208, 205,
263 73, 58, 247, 141, 59, 62, 187, 81, 213, 30, 153, 218, 41, 42, 110,
264 156, 161, 205
265 ],
266 "indexes": [0, 3, 6],
267 "signer_index": 1
268 },
269 [
270 [
271 145, 56, 175, 32, 122, 187, 214, 226, 251, 148, 88, 9, 1, 103, 159,
272 146, 80, 166, 107, 243, 251, 236, 41, 28, 111, 128, 207, 164, 132,
273 147, 228, 83, 246, 228, 170, 68, 89, 78, 60, 28, 123, 130, 88, 234,
274 38, 97, 42, 65, 1, 100, 53, 18, 78, 131, 8, 61, 122, 131, 238, 84,
275 233, 223, 154, 118, 118, 73, 28, 27, 101, 78, 80, 233, 123, 206, 220,
276 174, 134, 205, 71, 110, 112, 180, 97, 98, 0, 113, 69, 145, 231, 168,
277 43, 173, 172, 56, 104, 208
278 ],
279 1
280 ]
281 ]
282 ],
283 "batch_proof": { "values": [], "indices": [0, 1], "hasher": null }
284 }
285 "#;
286
287 fn golden_value() -> AggregateSignature<D> {
288 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
289 let msg = [0u8; 16];
290 let params = Parameters {
291 m: 10,
292 k: 5,
293 phi_f: 0.8,
294 };
295 let sk_1 = BlsSigningKey::generate(&mut rng);
296 let sk_2 = BlsSigningKey::generate(&mut rng);
297 let pk_1 = BlsVerificationKeyProofOfPossession::from(&sk_1);
298 let pk_2 = BlsVerificationKeyProofOfPossession::from(&sk_2);
299 let mut key_reg = KeyRegistration::init();
300 key_reg.register(1, pk_1).unwrap();
301 key_reg.register(1, pk_2).unwrap();
302 let closed_key_reg: ClosedKeyRegistration<D> = key_reg.close();
303 let clerk = Clerk::new_clerk_from_closed_key_registration(¶ms, &closed_key_reg);
304 let signer_1 = Signer::set_signer(0, 1, params, sk_1, pk_1.vk, closed_key_reg.clone());
305 let signer_2 = Signer::set_signer(1, 1, params, sk_2, pk_2.vk, closed_key_reg);
306 let signature_1 = signer_1.sign(&msg).unwrap();
307 let signature_2 = signer_2.sign(&msg).unwrap();
308
309 clerk
310 .aggregate_signatures_with_type(
311 &[signature_1, signature_2],
312 &msg,
313 AggregateSignatureType::Concatenation,
314 )
315 .unwrap()
316 }
317
318 #[test]
319 fn golden_conversions() {
320 let value: AggregateSignature<D> = serde_json::from_str(GOLDEN_JSON)
321 .expect("This JSON deserialization should not fail");
322
323 let serialized =
324 serde_json::to_string(&value).expect("This JSON serialization should not fail");
325 let golden_serialized = serde_json::to_string(&golden_value())
326 .expect("This JSON serialization should not fail");
327 assert_eq!(golden_serialized, serialized);
328 }
329 }
330}