mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-16 20:39:10 +00:00
@@ -25,9 +25,8 @@ pub fn attestation_parent_hashes(
|
||||
block_slot: u64,
|
||||
attestation_slot: u64,
|
||||
current_hashes: &[Hash256],
|
||||
oblique_hashes: &[Hash256])
|
||||
-> Result<Vec<Hash256>, ParentHashesError>
|
||||
{
|
||||
oblique_hashes: &[Hash256],
|
||||
) -> Result<Vec<Hash256>, ParentHashesError> {
|
||||
// This cast places a limit on cycle_length. If you change it, check math
|
||||
// for overflow.
|
||||
let cycle_length: u64 = u64::from(cycle_length);
|
||||
@@ -65,20 +64,18 @@ pub fn attestation_parent_hashes(
|
||||
* Arithmetic is:
|
||||
* start + cycle_length - oblique_hashes.len()
|
||||
*/
|
||||
let end = start.checked_add(cycle_length)
|
||||
let end = start
|
||||
.checked_add(cycle_length)
|
||||
.and_then(|x| x.checked_sub(oblique_hashes.len() as u64))
|
||||
.ok_or(ParentHashesError::IntWrapping)?;
|
||||
|
||||
|
||||
let mut hashes = Vec::new();
|
||||
hashes.extend_from_slice(
|
||||
¤t_hashes[(start as usize)..(end as usize)]);
|
||||
hashes.extend_from_slice(¤t_hashes[(start as usize)..(end as usize)]);
|
||||
hashes.extend_from_slice(oblique_hashes);
|
||||
|
||||
Ok(hashes)
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -106,7 +103,8 @@ mod tests {
|
||||
block_slot,
|
||||
attestation_slot,
|
||||
¤t_hashes,
|
||||
&oblique_hashes);
|
||||
&oblique_hashes,
|
||||
);
|
||||
assert!(result.is_ok());
|
||||
let result = result.unwrap();
|
||||
assert_eq!(result.len(), cycle_length as usize);
|
||||
@@ -131,7 +129,8 @@ mod tests {
|
||||
block_slot,
|
||||
attestation_slot,
|
||||
¤t_hashes,
|
||||
&oblique_hashes);
|
||||
&oblique_hashes,
|
||||
);
|
||||
assert!(result.is_ok());
|
||||
let result = result.unwrap();
|
||||
assert_eq!(result.len(), cycle_length as usize);
|
||||
@@ -156,7 +155,8 @@ mod tests {
|
||||
block_slot,
|
||||
attestation_slot,
|
||||
¤t_hashes,
|
||||
&oblique_hashes);
|
||||
&oblique_hashes,
|
||||
);
|
||||
assert!(result.is_ok());
|
||||
let result = result.unwrap();
|
||||
assert_eq!(result.len(), cycle_length as usize);
|
||||
@@ -179,7 +179,8 @@ mod tests {
|
||||
block_slot,
|
||||
attestation_slot,
|
||||
¤t_hashes,
|
||||
&oblique_hashes);
|
||||
&oblique_hashes,
|
||||
);
|
||||
let result = result.unwrap();
|
||||
assert_eq!(result.len(), cycle_length as usize);
|
||||
let expected_result = get_range_of_hashes(7, 15);
|
||||
@@ -201,7 +202,8 @@ mod tests {
|
||||
block_slot,
|
||||
attestation_slot,
|
||||
¤t_hashes,
|
||||
&oblique_hashes);
|
||||
&oblique_hashes,
|
||||
);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
@@ -220,7 +222,8 @@ mod tests {
|
||||
block_slot,
|
||||
attestation_slot,
|
||||
¤t_hashes,
|
||||
&oblique_hashes);
|
||||
&oblique_hashes,
|
||||
);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +1,16 @@
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
use super::types::{
|
||||
AttestationRecord,
|
||||
AttesterMap,
|
||||
};
|
||||
use super::attestation_parent_hashes::{
|
||||
attestation_parent_hashes,
|
||||
ParentHashesError,
|
||||
};
|
||||
use super::db::{
|
||||
ClientDB,
|
||||
DBError
|
||||
};
|
||||
use super::db::stores::{
|
||||
BeaconBlockStore,
|
||||
BeaconBlockAtSlotError,
|
||||
ValidatorStore,
|
||||
};
|
||||
use super::types::{
|
||||
Hash256,
|
||||
};
|
||||
use super::attestation_parent_hashes::{attestation_parent_hashes, ParentHashesError};
|
||||
use super::db::stores::{BeaconBlockAtSlotError, BeaconBlockStore, ValidatorStore};
|
||||
use super::db::{ClientDB, DBError};
|
||||
use super::message_generation::generate_signed_message;
|
||||
use super::signature_verification::{
|
||||
verify_aggregate_signature_for_indices,
|
||||
SignatureVerificationError,
|
||||
verify_aggregate_signature_for_indices, SignatureVerificationError,
|
||||
};
|
||||
use super::types::Hash256;
|
||||
use super::types::{AttestationRecord, AttesterMap};
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum AttestationValidationError {
|
||||
ParentSlotTooHigh,
|
||||
ParentSlotTooLow,
|
||||
@@ -52,7 +36,8 @@ pub enum AttestationValidationError {
|
||||
|
||||
/// The context against which some attestation should be validated.
|
||||
pub struct AttestationValidationContext<T>
|
||||
where T: ClientDB + Sized
|
||||
where
|
||||
T: ClientDB + Sized,
|
||||
{
|
||||
/// The slot as determined by the system time.
|
||||
pub block_slot: u64,
|
||||
@@ -73,7 +58,8 @@ pub struct AttestationValidationContext<T>
|
||||
}
|
||||
|
||||
impl<T> AttestationValidationContext<T>
|
||||
where T: ClientDB
|
||||
where
|
||||
T: ClientDB,
|
||||
{
|
||||
/// Validate a (fully deserialized) AttestationRecord against this context.
|
||||
///
|
||||
@@ -82,9 +68,10 @@ impl<T> AttestationValidationContext<T>
|
||||
///
|
||||
/// The attestation's aggregate signature will be verified, therefore the function must able to
|
||||
/// access all required validation public keys via the `validator_store`.
|
||||
pub fn validate_attestation(&self, a: &AttestationRecord)
|
||||
-> Result<HashSet<usize>, AttestationValidationError>
|
||||
{
|
||||
pub fn validate_attestation(
|
||||
&self,
|
||||
a: &AttestationRecord,
|
||||
) -> Result<HashSet<usize>, AttestationValidationError> {
|
||||
/*
|
||||
* The attesation slot must be less than or equal to the parent of the slot of the block
|
||||
* that contained the attestation.
|
||||
@@ -97,8 +84,10 @@ impl<T> AttestationValidationContext<T>
|
||||
* The slot of this attestation must not be more than cycle_length + 1 distance
|
||||
* from the parent_slot of block that contained it.
|
||||
*/
|
||||
if a.slot < self.parent_block_slot
|
||||
.saturating_sub(u64::from(self.cycle_length).saturating_add(1)) {
|
||||
if a.slot < self
|
||||
.parent_block_slot
|
||||
.saturating_sub(u64::from(self.cycle_length).saturating_add(1))
|
||||
{
|
||||
return Err(AttestationValidationError::ParentSlotTooLow);
|
||||
}
|
||||
|
||||
@@ -124,18 +113,18 @@ impl<T> AttestationValidationContext<T>
|
||||
* This is an array mapping the order that validators will appear in the bitfield to the
|
||||
* canonincal index of a validator.
|
||||
*/
|
||||
let attestation_indices = self.attester_map.get(&(a.slot, a.shard_id))
|
||||
let attestation_indices = self
|
||||
.attester_map
|
||||
.get(&(a.slot, a.shard_id))
|
||||
.ok_or(AttestationValidationError::BadAttesterMap)?;
|
||||
|
||||
/*
|
||||
* The bitfield must be no longer than the minimum required to represent each validator in the
|
||||
* attestation indices for this slot and shard id.
|
||||
*/
|
||||
if a.attester_bitfield.num_bytes() !=
|
||||
bytes_for_bits(attestation_indices.len())
|
||||
{
|
||||
if a.attester_bitfield.num_bytes() != bytes_for_bits(attestation_indices.len()) {
|
||||
return Err(AttestationValidationError::BadBitfieldLength);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If there are excess bits in the bitfield because the number of a validators in not a
|
||||
@@ -145,7 +134,7 @@ impl<T> AttestationValidationContext<T>
|
||||
* refer to the same AttesationRecord.
|
||||
*/
|
||||
if a.attester_bitfield.len() > attestation_indices.len() {
|
||||
return Err(AttestationValidationError::InvalidBitfieldEndBits)
|
||||
return Err(AttestationValidationError::InvalidBitfieldEndBits);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -156,7 +145,8 @@ impl<T> AttestationValidationContext<T>
|
||||
self.block_slot,
|
||||
a.slot,
|
||||
&self.recent_block_hashes,
|
||||
&a.oblique_parent_hashes)?;
|
||||
&a.oblique_parent_hashes,
|
||||
)?;
|
||||
|
||||
/*
|
||||
* The specified justified block hash supplied in the attestation must be in the chain at
|
||||
@@ -166,11 +156,15 @@ impl<T> AttestationValidationContext<T>
|
||||
* block store (database) we iterate back through the blocks until we find (or fail to
|
||||
* find) the justified block hash referenced in the attestation record.
|
||||
*/
|
||||
let latest_parent_hash = parent_hashes.last()
|
||||
let latest_parent_hash = parent_hashes
|
||||
.last()
|
||||
.ok_or(AttestationValidationError::BadCurrentHashes)?;
|
||||
match self.block_store.block_at_slot(&latest_parent_hash, a.justified_slot)? {
|
||||
match self
|
||||
.block_store
|
||||
.block_at_slot(&latest_parent_hash, a.justified_slot)?
|
||||
{
|
||||
Some((ref hash, _)) if *hash == a.justified_block_hash.to_vec() => (),
|
||||
_ => return Err(AttestationValidationError::InvalidJustifiedBlockHash)
|
||||
_ => return Err(AttestationValidationError::InvalidJustifiedBlockHash),
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -182,16 +176,17 @@ impl<T> AttestationValidationContext<T>
|
||||
&parent_hashes,
|
||||
a.shard_id,
|
||||
&a.shard_block_hash,
|
||||
a.justified_slot)
|
||||
a.justified_slot,
|
||||
)
|
||||
};
|
||||
|
||||
let voted_hashset =
|
||||
verify_aggregate_signature_for_indices(
|
||||
&signed_message,
|
||||
&a.aggregate_sig,
|
||||
&attestation_indices,
|
||||
&a.attester_bitfield,
|
||||
&self.validator_store)?;
|
||||
let voted_hashset = verify_aggregate_signature_for_indices(
|
||||
&signed_message,
|
||||
&a.aggregate_sig,
|
||||
&attestation_indices,
|
||||
&a.attester_bitfield,
|
||||
&self.validator_store,
|
||||
)?;
|
||||
|
||||
/*
|
||||
* If the hashset of voters is None, the signature verification failed.
|
||||
@@ -210,16 +205,11 @@ fn bytes_for_bits(bits: usize) -> usize {
|
||||
impl From<ParentHashesError> for AttestationValidationError {
|
||||
fn from(e: ParentHashesError) -> Self {
|
||||
match e {
|
||||
ParentHashesError::BadCurrentHashes
|
||||
=> AttestationValidationError::BadCurrentHashes,
|
||||
ParentHashesError::BadObliqueHashes
|
||||
=> AttestationValidationError::BadObliqueHashes,
|
||||
ParentHashesError::SlotTooLow
|
||||
=> AttestationValidationError::BlockSlotTooLow,
|
||||
ParentHashesError::SlotTooHigh
|
||||
=> AttestationValidationError::BlockSlotTooHigh,
|
||||
ParentHashesError::IntWrapping
|
||||
=> AttestationValidationError::IntWrapping
|
||||
ParentHashesError::BadCurrentHashes => AttestationValidationError::BadCurrentHashes,
|
||||
ParentHashesError::BadObliqueHashes => AttestationValidationError::BadObliqueHashes,
|
||||
ParentHashesError::SlotTooLow => AttestationValidationError::BlockSlotTooLow,
|
||||
ParentHashesError::SlotTooHigh => AttestationValidationError::BlockSlotTooHigh,
|
||||
ParentHashesError::IntWrapping => AttestationValidationError::IntWrapping,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -228,8 +218,7 @@ impl From<BeaconBlockAtSlotError> for AttestationValidationError {
|
||||
fn from(e: BeaconBlockAtSlotError) -> Self {
|
||||
match e {
|
||||
BeaconBlockAtSlotError::DBError(s) => AttestationValidationError::DBError(s),
|
||||
_ => AttestationValidationError::InvalidJustifiedBlockHash
|
||||
|
||||
_ => AttestationValidationError::InvalidJustifiedBlockHash,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -243,14 +232,16 @@ impl From<DBError> for AttestationValidationError {
|
||||
impl From<SignatureVerificationError> for AttestationValidationError {
|
||||
fn from(e: SignatureVerificationError) -> Self {
|
||||
match e {
|
||||
SignatureVerificationError::BadValidatorIndex
|
||||
=> AttestationValidationError::BadAttesterMap,
|
||||
SignatureVerificationError::PublicKeyCorrupt
|
||||
=> AttestationValidationError::PublicKeyCorrupt,
|
||||
SignatureVerificationError::NoPublicKeyForValidator
|
||||
=> AttestationValidationError::NoPublicKeyForValidator,
|
||||
SignatureVerificationError::DBError(s)
|
||||
=> AttestationValidationError::DBError(s),
|
||||
SignatureVerificationError::BadValidatorIndex => {
|
||||
AttestationValidationError::BadAttesterMap
|
||||
}
|
||||
SignatureVerificationError::PublicKeyCorrupt => {
|
||||
AttestationValidationError::PublicKeyCorrupt
|
||||
}
|
||||
SignatureVerificationError::NoPublicKeyForValidator => {
|
||||
AttestationValidationError::NoPublicKeyForValidator
|
||||
}
|
||||
SignatureVerificationError::DBError(s) => AttestationValidationError::DBError(s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
extern crate db;
|
||||
extern crate bls;
|
||||
extern crate db;
|
||||
extern crate hashing;
|
||||
extern crate ssz;
|
||||
extern crate ssz_helpers;
|
||||
extern crate types;
|
||||
|
||||
pub mod attestation_validation;
|
||||
mod attestation_parent_hashes;
|
||||
pub mod attestation_validation;
|
||||
pub mod block_validation;
|
||||
mod message_generation;
|
||||
mod signature_verification;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::ssz::SszStream;
|
||||
use super::hashing::canonical_hash;
|
||||
use super::ssz::SszStream;
|
||||
use super::types::Hash256;
|
||||
|
||||
/// Generates the message used to validate the signature provided with an AttestationRecord.
|
||||
@@ -10,9 +10,8 @@ pub fn generate_signed_message(
|
||||
parent_hashes: &[Hash256],
|
||||
shard_id: u16,
|
||||
shard_block_hash: &Hash256,
|
||||
justified_slot: u64)
|
||||
-> Vec<u8>
|
||||
{
|
||||
justified_slot: u64,
|
||||
) -> Vec<u8> {
|
||||
/*
|
||||
* Note: it's a little risky here to use SSZ, because the encoding is not necessarily SSZ
|
||||
* (for example, SSZ might change whilst this doesn't).
|
||||
@@ -39,9 +38,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_generate_signed_message() {
|
||||
let slot = 93;
|
||||
let parent_hashes: Vec<Hash256> = (0..12)
|
||||
.map(|i| Hash256::from(i as u64))
|
||||
.collect();
|
||||
let parent_hashes: Vec<Hash256> = (0..12).map(|i| Hash256::from(i as u64)).collect();
|
||||
let shard_id = 15;
|
||||
let shard_block_hash = Hash256::from("shard_block_hash".as_bytes());
|
||||
let justified_slot = 18;
|
||||
@@ -51,7 +48,8 @@ mod tests {
|
||||
&parent_hashes,
|
||||
shard_id,
|
||||
&shard_block_hash,
|
||||
justified_slot);
|
||||
justified_slot,
|
||||
);
|
||||
|
||||
/*
|
||||
* Note: this is not some well-known test vector, it's simply the result of running
|
||||
@@ -60,9 +58,8 @@ mod tests {
|
||||
* Once well-known test vectors are established, they should be placed here.
|
||||
*/
|
||||
let expected = vec![
|
||||
149, 99, 94, 229, 72, 144, 233, 14, 164, 16, 143, 53, 94, 48,
|
||||
118, 179, 33, 181, 172, 215, 2, 191, 176, 18, 188, 172, 137,
|
||||
178, 236, 66, 74, 120
|
||||
149, 99, 94, 229, 72, 144, 233, 14, 164, 16, 143, 53, 94, 48, 118, 179, 33, 181, 172,
|
||||
215, 2, 191, 176, 18, 188, 172, 137, 178, 236, 66, 74, 120,
|
||||
];
|
||||
|
||||
assert_eq!(output, expected);
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
use std::collections::HashSet;
|
||||
use super::bls::{
|
||||
AggregateSignature,
|
||||
AggregatePublicKey,
|
||||
};
|
||||
use super::bls::{AggregatePublicKey, AggregateSignature};
|
||||
use super::db::stores::{ValidatorStore, ValidatorStoreError};
|
||||
use super::db::ClientDB;
|
||||
use super::db::stores::{
|
||||
ValidatorStore,
|
||||
ValidatorStoreError,
|
||||
};
|
||||
use super::types::Bitfield;
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum SignatureVerificationError {
|
||||
@@ -30,9 +24,10 @@ pub fn verify_aggregate_signature_for_indices<T>(
|
||||
agg_sig: &AggregateSignature,
|
||||
attestation_indices: &[usize],
|
||||
bitfield: &Bitfield,
|
||||
validator_store: &ValidatorStore<T>)
|
||||
-> Result<Option<HashSet<usize>>, SignatureVerificationError>
|
||||
where T: ClientDB + Sized
|
||||
validator_store: &ValidatorStore<T>,
|
||||
) -> Result<Option<HashSet<usize>>, SignatureVerificationError>
|
||||
where
|
||||
T: ClientDB + Sized,
|
||||
{
|
||||
let mut voters = HashSet::new();
|
||||
let mut agg_pub_key = AggregatePublicKey::new();
|
||||
@@ -43,7 +38,8 @@ pub fn verify_aggregate_signature_for_indices<T>(
|
||||
/*
|
||||
* De-reference the attestation index into a canonical ValidatorRecord index.
|
||||
*/
|
||||
let validator = *attestation_indices.get(i)
|
||||
let validator = *attestation_indices
|
||||
.get(i)
|
||||
.ok_or(SignatureVerificationError::BadValidatorIndex)?;
|
||||
/*
|
||||
* Load the validators public key from our store.
|
||||
@@ -77,23 +73,17 @@ pub fn verify_aggregate_signature_for_indices<T>(
|
||||
impl From<ValidatorStoreError> for SignatureVerificationError {
|
||||
fn from(error: ValidatorStoreError) -> Self {
|
||||
match error {
|
||||
ValidatorStoreError::DBError(s) =>
|
||||
SignatureVerificationError::DBError(s),
|
||||
ValidatorStoreError::DecodeError =>
|
||||
SignatureVerificationError::PublicKeyCorrupt,
|
||||
ValidatorStoreError::DBError(s) => SignatureVerificationError::DBError(s),
|
||||
ValidatorStoreError::DecodeError => SignatureVerificationError::PublicKeyCorrupt,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::super::bls::{
|
||||
Keypair,
|
||||
Signature,
|
||||
};
|
||||
use super::super::bls::{Keypair, Signature};
|
||||
use super::super::db::MemoryDB;
|
||||
use super::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
/*
|
||||
@@ -130,8 +120,7 @@ mod tests {
|
||||
let mut all_keypairs = signing_keypairs.clone();
|
||||
all_keypairs.append(&mut non_signing_keypairs.clone());
|
||||
|
||||
let attestation_indices: Vec<usize> = (0..all_keypairs.len())
|
||||
.collect();
|
||||
let attestation_indices: Vec<usize> = (0..all_keypairs.len()).collect();
|
||||
let mut bitfield = Bitfield::new();
|
||||
for i in 0..signing_keypairs.len() {
|
||||
bitfield.set_bit(i, true);
|
||||
@@ -158,11 +147,11 @@ mod tests {
|
||||
&agg_sig,
|
||||
&attestation_indices,
|
||||
&bitfield,
|
||||
&store).unwrap();
|
||||
&store,
|
||||
).unwrap();
|
||||
|
||||
let voters = voters.unwrap();
|
||||
(0..signing_keypairs.len())
|
||||
.for_each(|i| assert!(voters.contains(&i)));
|
||||
(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)));
|
||||
|
||||
@@ -176,7 +165,8 @@ mod tests {
|
||||
&agg_sig,
|
||||
&attestation_indices,
|
||||
&bitfield,
|
||||
&store).unwrap();
|
||||
&store,
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(voters, None);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user