diff --git a/lighthouse/state/validation/attestation_validation.rs b/lighthouse/state/validation/attestation_validation.rs index d47d15c53..0061f7a9f 100644 --- a/lighthouse/state/validation/attestation_validation.rs +++ b/lighthouse/state/validation/attestation_validation.rs @@ -4,10 +4,15 @@ use super::attestation_parent_hashes::{ ParentHashesError, }; use super::db::ClientDB; -use super::db::stores::BlockStore; +use super::db::stores::{ + BlockStore, + ValidatorStore, + StoreError, +}; use super::ssz::SszStream; use super::bls::{ AggregateSignature, + AggregatePublicKey, PublicKey, }; use super::utils::hash::canonical_hash; diff --git a/lighthouse/state/validation/mod.rs b/lighthouse/state/validation/mod.rs index 70c375754..c11d2c3c2 100644 --- a/lighthouse/state/validation/mod.rs +++ b/lighthouse/state/validation/mod.rs @@ -15,4 +15,5 @@ use super::transition::attestation_parent_hashes; use super::utils; mod attestation_validation; +mod signatures; mod ssz_block; diff --git a/lighthouse/state/validation/signatures.rs b/lighthouse/state/validation/signatures.rs new file mode 100644 index 000000000..a2a289328 --- /dev/null +++ b/lighthouse/state/validation/signatures.rs @@ -0,0 +1,126 @@ +use std::collections::HashSet; +use super::bls::{ + AggregateSignature, + AggregatePublicKey, +}; +use super::db::ClientDB; +use super::db::stores::{ + ValidatorStore, + StoreError, +}; +use super::utils::types::Bitfield; + +#[derive(Debug, PartialEq)] +pub enum SignatureVerificationError { + BadValidatorIndex, + PublicKeyCorrupt, + NoPublicKeyForValidator, + DBError(String), +} + +fn verify_aggregate_signature_for_indices(message: &[u8], + agg_sig: &AggregateSignature, + attestation_indices: &[usize], + bitfield: &Bitfield, + validator_store: &ValidatorStore) + -> Result<(bool, HashSet), SignatureVerificationError> + where T: ClientDB + Sized +{ + let mut voters = HashSet::new(); + let mut agg_pub_key = AggregatePublicKey::new(); + + for i in 0..attestation_indices.len() { + let voted = bitfield.get_bit(i); + if voted { + let validator = attestation_indices[i]; + let pub_key = validator_store.get_public_key_by_index(i)? + .ok_or(SignatureVerificationError::NoPublicKeyForValidator)?; + agg_pub_key.add(&pub_key); + voters.insert(validator); + } + } + Ok((agg_sig.verify(&message, &agg_pub_key), + voters)) +} + +impl From for SignatureVerificationError { + fn from(error: StoreError) -> Self { + match error { + StoreError::DBError(s) => + SignatureVerificationError::DBError(s), + StoreError::DecodeError => + SignatureVerificationError::PublicKeyCorrupt, + } + } +} + + +#[cfg(test)] +mod tests { + use super::*; + use super::super::bls::{ + Keypair, + Signature, + }; + use super::super::db::MemoryDB; + use std::sync::Arc; + + #[test] + fn test_signature_verification() { + let message = "cats".as_bytes(); + let signing_keypairs = vec![ + Keypair::random(), + Keypair::random(), + Keypair::random(), + Keypair::random(), + Keypair::random(), + Keypair::random(), + ]; + let non_signing_keypairs = vec![ + Keypair::random(), + Keypair::random(), + Keypair::random(), + Keypair::random(), + Keypair::random(), + Keypair::random(), + ]; + /* + * Signing keypairs first, then non-signing + */ + let mut all_keypairs = signing_keypairs.clone(); + all_keypairs.append(&mut non_signing_keypairs.clone()); + + let attestation_indices: Vec = (0..all_keypairs.len()) + .collect(); + let mut bitfield = Bitfield::new(); + for i in 0..signing_keypairs.len() { + bitfield.set_bit(i, true); + } + + let db = Arc::new(MemoryDB::open()); + let store = ValidatorStore::new(db); + + for (i, keypair) in all_keypairs.iter().enumerate() { + store.put_public_key_by_index(i, &keypair.pk).unwrap(); + } + + let mut agg_sig = AggregateSignature::new(); + for keypair in &signing_keypairs { + let sig = Signature::new(&message, &keypair.sk); + agg_sig.add(&sig); + } + + let (is_valid, voters) = verify_aggregate_signature_for_indices( + &message, + &agg_sig, + &attestation_indices, + &bitfield, + &store).unwrap(); + + assert_eq!(is_valid, true); + (0..signing_keypairs.len()) + .for_each(|i| assert!(voters.contains(&i))); + (signing_keypairs.len()..non_signing_keypairs.len()) + .for_each(|i| assert!(!voters.contains(&i))); + } +}