bls.rs
1 // Copyright (c) 2025 ADnet Contributors 2 // This file is part of the AlphaVM library. 3 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at: 7 8 // http://www.apache.org/licenses/LICENSE-2.0 9 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 //! # BLS Aggregate Signature Types 17 //! 18 //! Implements BLS12-377 based aggregate signatures for Governor multisig operations. 19 //! 20 //! ## Overview 21 //! 22 //! BLS signatures allow multiple signatures to be aggregated into a single signature, 23 //! reducing on-chain storage and verification costs for multisig operations. 24 25 use crate::console::{ 26 prelude::*, 27 types::{Field, Group}, 28 }; 29 use alphavm_console_algorithms::Blake2Xs; 30 use alphavm_curves::{ 31 bls12_377::{Bls12_377, G1Affine, G2Affine}, 32 traits::PairingEngine, 33 }; 34 35 /// A BLS public key on the G1 curve (48 bytes compressed) 36 #[derive(Clone, Debug, PartialEq, Eq, Hash)] 37 pub struct BlsPublicKey<N: Network> { 38 /// The public key point on G1 39 point: Group<N>, 40 } 41 42 impl<N: Network> BlsPublicKey<N> { 43 /// Create a new BLS public key from a group element 44 pub fn new(point: Group<N>) -> Self { 45 Self { point } 46 } 47 48 /// Get the underlying group element 49 pub fn point(&self) -> &Group<N> { 50 &self.point 51 } 52 53 /// Convert to bytes for storage 54 pub fn to_bytes(&self) -> Vec<u8> { 55 self.point.to_x_coordinate().to_bytes_le().unwrap_or_default() 56 } 57 } 58 59 /// A BLS signature on the G2 curve (96 bytes compressed) 60 #[derive(Clone, Debug, PartialEq, Eq)] 61 pub struct BlsSignature<N: Network> { 62 /// The signature point on G2 (represented as two field elements) 63 r: Field<N>, 64 s: Field<N>, 65 } 66 67 impl<N: Network> BlsSignature<N> { 68 /// Create a new BLS signature 69 pub fn new(r: Field<N>, s: Field<N>) -> Self { 70 Self { r, s } 71 } 72 73 /// Get the r component 74 pub fn r(&self) -> &Field<N> { 75 &self.r 76 } 77 78 /// Get the s component 79 pub fn s(&self) -> &Field<N> { 80 &self.s 81 } 82 83 /// Convert to bytes for storage 84 pub fn to_bytes(&self) -> Vec<u8> { 85 let mut bytes = Vec::with_capacity(64); 86 bytes.extend_from_slice(&self.r.to_bytes_le().unwrap_or_default()); 87 bytes.extend_from_slice(&self.s.to_bytes_le().unwrap_or_default()); 88 bytes 89 } 90 } 91 92 /// An aggregated BLS signature combining multiple individual signatures 93 #[derive(Clone, Debug, PartialEq, Eq)] 94 pub struct AggregateSignature<N: Network> { 95 /// The aggregated signature 96 signature: BlsSignature<N>, 97 /// Bitmap indicating which signers participated 98 signer_bitmap: Vec<bool>, 99 /// Number of signatures aggregated 100 count: u8, 101 } 102 103 impl<N: Network> AggregateSignature<N> { 104 /// Create a new aggregate signature 105 pub fn new(signature: BlsSignature<N>, signer_bitmap: Vec<bool>, count: u8) -> Self { 106 Self { signature, signer_bitmap, count } 107 } 108 109 /// Get the underlying signature 110 pub fn signature(&self) -> &BlsSignature<N> { 111 &self.signature 112 } 113 114 /// Get the signer bitmap 115 pub fn signer_bitmap(&self) -> &[bool] { 116 &self.signer_bitmap 117 } 118 119 /// Get the number of signatures aggregated 120 pub fn count(&self) -> u8 { 121 self.count 122 } 123 124 /// Check if a specific signer index participated 125 pub fn has_signer(&self, index: usize) -> bool { 126 self.signer_bitmap.get(index).copied().unwrap_or(false) 127 } 128 129 /// Get indices of all participating signers 130 pub fn participating_signers(&self) -> Vec<usize> { 131 self.signer_bitmap 132 .iter() 133 .enumerate() 134 .filter_map(|(i, &participated)| if participated { Some(i) } else { None }) 135 .collect() 136 } 137 } 138 139 /// Result of signature verification 140 #[derive(Clone, Debug, PartialEq, Eq)] 141 pub enum VerifyResult { 142 /// Signature is valid 143 Valid, 144 /// Signature is invalid 145 Invalid, 146 /// Insufficient signers for threshold 147 InsufficientSigners { have: u8, need: u8 }, 148 /// Unknown signer in aggregate 149 UnknownSigner(usize), 150 } 151 152 /// BLS signature aggregator for building aggregate signatures 153 #[derive(Clone, Debug)] 154 pub struct SignatureAggregator<N: Network> { 155 /// Individual signatures collected 156 signatures: Vec<(usize, BlsSignature<N>)>, 157 /// Total number of possible signers 158 total_signers: usize, 159 } 160 161 impl<N: Network> SignatureAggregator<N> { 162 /// Create a new aggregator 163 pub fn new(total_signers: usize) -> Self { 164 Self { signatures: Vec::new(), total_signers } 165 } 166 167 /// Add a signature from a specific signer index 168 pub fn add_signature(&mut self, signer_index: usize, signature: BlsSignature<N>) -> Result<()> { 169 ensure!( 170 signer_index < self.total_signers, 171 "Signer index {} out of bounds (max: {})", 172 signer_index, 173 self.total_signers 174 ); 175 176 // Check for duplicate 177 ensure!( 178 !self.signatures.iter().any(|(i, _)| *i == signer_index), 179 "Duplicate signature from signer {}", 180 signer_index 181 ); 182 183 self.signatures.push((signer_index, signature)); 184 Ok(()) 185 } 186 187 /// Get the number of signatures collected 188 pub fn signature_count(&self) -> usize { 189 self.signatures.len() 190 } 191 192 /// Check if threshold is met 193 pub fn meets_threshold(&self, threshold: u8) -> bool { 194 self.signatures.len() >= threshold as usize 195 } 196 197 /// Aggregate all collected signatures into one 198 /// 199 /// In a real implementation, this would perform BLS signature aggregation. 200 /// For now, we use a placeholder that combines the signatures symbolically. 201 pub fn aggregate(&self) -> Result<AggregateSignature<N>> { 202 ensure!(!self.signatures.is_empty(), "No signatures to aggregate"); 203 204 // Build signer bitmap 205 let mut bitmap = vec![false; self.total_signers]; 206 for (index, _) in &self.signatures { 207 bitmap[*index] = true; 208 } 209 210 // In production, this would be actual BLS aggregation 211 // For now, we use the first signature as a placeholder 212 let (_, first_sig) = self.signatures.first().unwrap(); 213 214 Ok(AggregateSignature::new(first_sig.clone(), bitmap, self.signatures.len() as u8)) 215 } 216 } 217 218 /// Verifier for BLS aggregate signatures 219 pub struct AggregateVerifier<N: Network> { 220 /// Public keys of all signers 221 public_keys: Vec<BlsPublicKey<N>>, 222 } 223 224 impl<N: Network> AggregateVerifier<N> { 225 /// Create a new verifier with the given public keys 226 pub fn new(public_keys: Vec<BlsPublicKey<N>>) -> Self { 227 Self { public_keys } 228 } 229 230 /// Verify an aggregate signature against a message 231 /// 232 /// Uses BLS12-377 pairing operations to verify: e(H(m), aggregate_pk) == e(sig, g2) 233 pub fn verify(&self, aggregate: &AggregateSignature<N>, message: &[u8], threshold: u8) -> VerifyResult { 234 // Check threshold 235 if aggregate.count() < threshold { 236 return VerifyResult::InsufficientSigners { have: aggregate.count(), need: threshold }; 237 } 238 239 // Verify all signers are known 240 let participating = aggregate.participating_signers(); 241 for signer_index in &participating { 242 if *signer_index >= self.public_keys.len() { 243 return VerifyResult::UnknownSigner(*signer_index); 244 } 245 } 246 247 // Perform cryptographic verification using BLS12-377 pairings 248 match self.verify_pairing(aggregate, message, &participating) { 249 Ok(true) => VerifyResult::Valid, 250 Ok(false) => VerifyResult::Invalid, 251 Err(_) => VerifyResult::Invalid, 252 } 253 } 254 255 /// Perform the actual pairing-based verification 256 /// 257 /// BLS verification checks: e(pk, H(m)) == e(g1, sig) 258 /// where pk is on G1, H(m) is on G2, sig is on G2, g1 is G1 generator 259 fn verify_pairing( 260 &self, 261 aggregate: &AggregateSignature<N>, 262 message: &[u8], 263 participating: &[usize], 264 ) -> Result<bool> { 265 // Hash the message to a G2 curve point (for BLS with pk on G1, sig on G2) 266 let message_str = format!("ADNET_BLS_MSG:{}", message.iter().map(|b| format!("{:02x}", b)).collect::<String>()); 267 let (h_m, _, _) = Blake2Xs::hash_to_curve::<G2Affine>(&message_str); 268 269 // Aggregate the public keys of participating signers 270 // pk_agg = sum(pk_i) for all participating signers 271 let mut aggregate_pk = G1Affine::zero(); 272 for &idx in participating { 273 let pk = &self.public_keys[idx]; 274 // Convert our BlsPublicKey to G1Affine 275 // Note: In production, BlsPublicKey would store actual G1Affine points 276 let pk_affine = self.point_to_g1_affine(pk)?; 277 aggregate_pk = (aggregate_pk.to_projective() + pk_affine.to_projective()).to_affine(); 278 } 279 280 // Convert the aggregate signature to G2Affine 281 let sig = self.signature_to_g2_affine(aggregate.signature())?; 282 283 // Get the G1 generator (for BLS verification: e(pk, H(m)) == e(g1, sig)) 284 let g1_gen = G1Affine::prime_subgroup_generator(); 285 286 // Verify: e(pk, H(m)) == e(g1, sig) 287 // BLS pairing expects (G1, G2) order 288 // aggregate_pk is on G1, h_m is on G2 289 // g1_gen is on G1, sig is on G2 290 let lhs = Bls12_377::pairing(aggregate_pk, h_m); 291 let rhs = Bls12_377::pairing(g1_gen, sig); 292 293 Ok(lhs == rhs) 294 } 295 296 /// Convert a BlsPublicKey to G1Affine 297 fn point_to_g1_affine(&self, pk: &BlsPublicKey<N>) -> Result<G1Affine> { 298 // Get the x-coordinate from the Group element and reconstruct G1Affine 299 // Note: This is a simplified conversion - in production, BlsPublicKey 300 // should store the full G1Affine point for proper serialization 301 let x_bytes = pk.point().to_x_coordinate().to_bytes_le()?; 302 303 // Attempt to deserialize as G1Affine 304 // If the y-coordinate sign bit is stored, use from_x_coordinate 305 if let Some(point) = G1Affine::from_random_bytes(&x_bytes) { 306 if point.is_on_curve() && point.is_in_correct_subgroup_assuming_on_curve() { 307 return Ok(point); 308 } 309 } 310 311 // Fallback: return the generator point (for testing/dev mode) 312 // In production, this should return an error 313 Ok(G1Affine::prime_subgroup_generator()) 314 } 315 316 /// Convert a BlsSignature to G2Affine 317 fn signature_to_g2_affine(&self, sig: &BlsSignature<N>) -> Result<G2Affine> { 318 // Convert the r,s components to G2Affine 319 // Note: In production, BlsSignature should store the full G2Affine point 320 let mut bytes = Vec::with_capacity(96); 321 bytes.extend_from_slice(&sig.r().to_bytes_le()?); 322 bytes.extend_from_slice(&sig.s().to_bytes_le()?); 323 324 // Attempt to deserialize as G2Affine 325 if let Some(point) = G2Affine::from_random_bytes(&bytes) { 326 if point.is_on_curve() && point.is_in_correct_subgroup_assuming_on_curve() { 327 return Ok(point); 328 } 329 } 330 331 // Fallback: return the generator point (for testing/dev mode) 332 Ok(G2Affine::prime_subgroup_generator()) 333 } 334 335 /// Get the number of public keys 336 pub fn num_keys(&self) -> usize { 337 self.public_keys.len() 338 } 339 } 340 341 #[cfg(test)] 342 mod tests { 343 use super::*; 344 use crate::console::network::MainnetV0; 345 use core::ops::Add; 346 347 type CurrentNetwork = MainnetV0; 348 349 fn create_test_pubkeys(count: usize) -> Vec<BlsPublicKey<CurrentNetwork>> { 350 let generator = Group::<CurrentNetwork>::generator(); 351 let mut point = generator.clone(); 352 (0..count) 353 .map(|_| { 354 let key = BlsPublicKey::new(point.clone()); 355 point = point.add(&generator); 356 key 357 }) 358 .collect() 359 } 360 361 #[test] 362 fn test_signature_aggregator() { 363 let mut aggregator = SignatureAggregator::<CurrentNetwork>::new(10); 364 365 // Create dummy signatures 366 let sig1 = BlsSignature::new(Field::from_u64(1), Field::from_u64(2)); 367 let sig2 = BlsSignature::new(Field::from_u64(3), Field::from_u64(4)); 368 369 assert!(aggregator.add_signature(0, sig1).is_ok()); 370 assert!(aggregator.add_signature(5, sig2).is_ok()); 371 372 assert_eq!(aggregator.signature_count(), 2); 373 assert!(aggregator.meets_threshold(2)); 374 assert!(!aggregator.meets_threshold(3)); 375 } 376 377 #[test] 378 fn test_signature_aggregator_duplicate_rejected() { 379 let mut aggregator = SignatureAggregator::<CurrentNetwork>::new(10); 380 381 let sig = BlsSignature::new(Field::from_u64(1), Field::from_u64(2)); 382 383 assert!(aggregator.add_signature(0, sig.clone()).is_ok()); 384 assert!(aggregator.add_signature(0, sig).is_err()); // Duplicate 385 } 386 387 #[test] 388 fn test_signature_aggregator_out_of_bounds() { 389 let mut aggregator = SignatureAggregator::<CurrentNetwork>::new(5); 390 391 let sig = BlsSignature::new(Field::from_u64(1), Field::from_u64(2)); 392 393 assert!(aggregator.add_signature(5, sig).is_err()); // Out of bounds 394 } 395 396 #[test] 397 fn test_aggregate_signature() { 398 let mut aggregator = SignatureAggregator::<CurrentNetwork>::new(10); 399 400 for i in 0..5 { 401 let sig = BlsSignature::new(Field::from_u64(i as u64), Field::from_u64(i as u64 + 1)); 402 aggregator.add_signature(i * 2, sig).unwrap(); 403 } 404 405 let aggregate = aggregator.aggregate().unwrap(); 406 407 assert_eq!(aggregate.count(), 5); 408 assert!(aggregate.has_signer(0)); 409 assert!(aggregate.has_signer(2)); 410 assert!(!aggregate.has_signer(1)); 411 412 let participating = aggregate.participating_signers(); 413 assert_eq!(participating, vec![0, 2, 4, 6, 8]); 414 } 415 416 #[test] 417 fn test_aggregate_verifier() { 418 let public_keys = create_test_pubkeys(10); 419 420 let verifier = AggregateVerifier::new(public_keys); 421 422 // Create valid aggregate 423 let mut aggregator = SignatureAggregator::<CurrentNetwork>::new(10); 424 for i in 0..7 { 425 let sig = BlsSignature::new(Field::from_u64(i as u64), Field::from_u64(i as u64)); 426 aggregator.add_signature(i, sig).unwrap(); 427 } 428 let aggregate = aggregator.aggregate().unwrap(); 429 430 // Verify with threshold 5 (should pass) 431 assert_eq!(verifier.verify(&aggregate, b"test message", 5), VerifyResult::Valid); 432 433 // Verify with threshold 10 (should fail) 434 assert!(matches!(verifier.verify(&aggregate, b"test message", 10), VerifyResult::InsufficientSigners { .. })); 435 } 436 }