Progress on block validation

This commit is contained in:
Paul Hauner 2018-09-24 23:35:51 +10:00
parent 2141b8c623
commit 1677b6dcc8
No known key found for this signature in database
GPG Key ID: 303E4494BB28068C
9 changed files with 157 additions and 56 deletions

View File

@ -61,7 +61,7 @@ impl Decodable for AttestationRecord {
// Do aggregate sig decoding properly. // Do aggregate sig decoding properly.
let (agg_sig_bytes, i) = decode_ssz_list(bytes, i)?; let (agg_sig_bytes, i) = decode_ssz_list(bytes, i)?;
let aggregate_sig = AggregateSignature::from_bytes(&agg_sig_bytes) let aggregate_sig = AggregateSignature::from_bytes(&agg_sig_bytes)
.map_err(|_| DecodeError::OutOfBounds)?; .map_err(|_| DecodeError::TooShort)?; // also could be TooLong
let attestation_record = Self { let attestation_record = Self {
slot, slot,

View File

@ -11,7 +11,7 @@ pub use self::attestation_record::{
MIN_SSZ_ATTESTION_RECORD_LENGTH, MIN_SSZ_ATTESTION_RECORD_LENGTH,
}; };
pub use self::ssz_splitter::{ pub use self::ssz_splitter::{
split_all, split_all_attestations,
split_one, split_one_attestation,
AttestationSplitError, AttestationSplitError,
}; };

View File

@ -9,13 +9,13 @@ pub enum AttestationSplitError {
/// Given some ssz slice, find the bounds of each serialized AttestationRecord and return a vec of /// Given some ssz slice, find the bounds of each serialized AttestationRecord and return a vec of
/// slices point to each. /// slices point to each.
pub fn split_all<'a>(full_ssz: &'a [u8], index: usize) pub fn split_all_attestations<'a>(full_ssz: &'a [u8], index: usize)
-> Result<Vec<&'a [u8]>, AttestationSplitError> -> Result<Vec<&'a [u8]>, AttestationSplitError>
{ {
let mut v = vec![]; let mut v = vec![];
let mut index = index; let mut index = index;
while index < full_ssz.len() - 1 { while index < full_ssz.len() - 1 {
let (slice, i) = split_one(full_ssz, index)?; let (slice, i) = split_one_attestation(full_ssz, index)?;
v.push(slice); v.push(slice);
index = i; index = i;
} }
@ -24,27 +24,27 @@ pub fn split_all<'a>(full_ssz: &'a [u8], index: usize)
/// Given some ssz slice, find the bounds of one serialized AttestationRecord /// Given some ssz slice, find the bounds of one serialized AttestationRecord
/// and return a slice pointing to that. /// and return a slice pointing to that.
pub fn split_one<'a>(full_ssz: &'a [u8], index: usize) pub fn split_one_attestation<'a>(full_ssz: &'a [u8], index: usize)
-> Result<(&'a [u8], usize), AttestationSplitError> -> Result<(&'a [u8], usize), AttestationSplitError>
{ {
if full_ssz.len() < MIN_LENGTH { if full_ssz.len() < MIN_LENGTH {
return Err(AttestationSplitError::TooShort); return Err(AttestationSplitError::TooShort);
} }
let hashes_len = decode_length(full_ssz, 10, LENGTH_BYTES) let hashes_len = decode_length(full_ssz, index + 10, LENGTH_BYTES)
.map_err(|_| AttestationSplitError::TooShort)?; .map_err(|_| AttestationSplitError::TooShort)?;
let bitfield_len = decode_length( let bitfield_len = decode_length(
full_ssz, hashes_len + 46, full_ssz, index + hashes_len + 46,
LENGTH_BYTES) LENGTH_BYTES)
.map_err(|_| AttestationSplitError::TooShort)?; .map_err(|_| AttestationSplitError::TooShort)?;
// Subtract one because the min length assume 1 byte of bitfield // Subtract one because the min length assumes 1 byte of bitfield
let len = MIN_LENGTH let len = MIN_LENGTH - 1
+ hashes_len + hashes_len
+ bitfield_len.saturating_sub(1); + bitfield_len;
if full_ssz.len() < len { if index + full_ssz.len() < len {
return Err(AttestationSplitError::TooShort); return Err(AttestationSplitError::TooShort);
} }
@ -102,7 +102,7 @@ mod tests {
let mut ssz_stream = SszStream::new(); let mut ssz_stream = SszStream::new();
ssz_stream.append(&a); ssz_stream.append(&a);
let ssz = ssz_stream.drain(); let ssz = ssz_stream.drain();
let (a_ssz, i) = split_one(&ssz, 0).unwrap(); let (a_ssz, i) = split_one_attestation(&ssz, 0).unwrap();
assert_eq!(i, ssz.len()); assert_eq!(i, ssz.len());
let (decoded_a, _) = AttestationRecord::ssz_decode(a_ssz, 0) let (decoded_a, _) = AttestationRecord::ssz_decode(a_ssz, 0)
.unwrap(); .unwrap();
@ -115,7 +115,7 @@ mod tests {
ssz_stream.append(&a); ssz_stream.append(&a);
ssz_stream.append(&b); ssz_stream.append(&b);
let ssz = ssz_stream.drain(); let ssz = ssz_stream.drain();
let ssz_vec = split_all(&ssz, 0).unwrap(); let ssz_vec = split_all_attestations(&ssz, 0).unwrap();
let (decoded_a, _) = let (decoded_a, _) =
AttestationRecord::ssz_decode(ssz_vec[0], 0) AttestationRecord::ssz_decode(ssz_vec[0], 0)
.unwrap(); .unwrap();
@ -133,7 +133,7 @@ mod tests {
ssz_stream.append(&b); ssz_stream.append(&b);
let ssz = ssz_stream.drain(); let ssz = ssz_stream.drain();
let ssz = &ssz[0..ssz.len() - 1]; let ssz = &ssz[0..ssz.len() - 1];
assert!(split_all(&ssz, 0).is_err()); assert!(split_all_attestations(&ssz, 0).is_err());
} }
} }

View File

@ -1,9 +1,10 @@
use std::collections::HashSet; use std::collections::HashSet;
use super::AttestationRecord; use super::attestation_record::AttestationRecord;
use super::attestation_parent_hashes::{ use super::attestation_parent_hashes::{
attestation_parent_hashes, attestation_parent_hashes,
ParentHashesError, ParentHashesError,
}; };
use super::AttesterMap;
use super::db::{ use super::db::{
ClientDB, ClientDB,
DBError DBError
@ -17,7 +18,6 @@ use super::utils::hash::canonical_hash;
use super::utils::types::{ use super::utils::types::{
Hash256, Hash256,
}; };
use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use super::signatures::{ use super::signatures::{
verify_aggregate_signature_for_indices, verify_aggregate_signature_for_indices,
@ -44,10 +44,6 @@ pub enum AttestationValidationError {
DBError(String), DBError(String),
} }
type Slot = u64;
type ShardId = u64;
type AttesterMap = HashMap<(Slot, ShardId), Vec<usize>>;
fn bytes_for_bits(bits: usize) -> usize { fn bytes_for_bits(bits: usize) -> usize {
(bits.saturating_sub(1) / 8) + 1 (bits.saturating_sub(1) / 8) + 1
} }
@ -62,12 +58,14 @@ pub fn validate_attestation<T>(a: &AttestationRecord,
cycle_length: u8, cycle_length: u8,
known_last_justified_slot: u64, known_last_justified_slot: u64,
known_parent_hashes: Arc<Vec<Hash256>>, known_parent_hashes: Arc<Vec<Hash256>>,
block_store: BlockStore<T>, block_store: Arc<BlockStore<T>>,
validator_store: ValidatorStore<T>, validator_store: Arc<ValidatorStore<T>>,
attester_map: Arc<AttesterMap>) attester_map: Arc<AttesterMap>)
-> Result<(bool, Option<HashSet<usize>>), AttestationValidationError> -> Result<Option<HashSet<usize>>, AttestationValidationError>
where T: ClientDB + Sized where T: ClientDB + Sized
{ {
// TODO: assert attestion isn't already known
/* /*
* The attesation slot must not be higher than the block that contained it. * The attesation slot must not be higher than the block that contained it.
*/ */
@ -156,7 +154,7 @@ pub fn validate_attestation<T>(a: &AttestationRecord,
a.justified_slot) a.justified_slot)
}; };
let (signature_valid, voted_hashmap) = let voted_hashmap =
verify_aggregate_signature_for_indices( verify_aggregate_signature_for_indices(
&signed_message, &signed_message,
&a.aggregate_sig, &a.aggregate_sig,
@ -164,7 +162,7 @@ pub fn validate_attestation<T>(a: &AttestationRecord,
&a.attester_bitfield, &a.attester_bitfield,
&validator_store)?; &validator_store)?;
Ok((signature_valid, voted_hashmap)) Ok(voted_hashmap)
} }
/// Generates the message used to validate the signature provided with an AttestationRecord. /// Generates the message used to validate the signature provided with an AttestationRecord.

View File

@ -1,6 +1,7 @@
use super::AttesterMap;
use super::db; use super::db;
use super::bls; use super::bls;
use super::AttestationRecord; use super::attestation_record;
use super::ssz; use super::ssz;
use super::attestation_parent_hashes; use super::attestation_parent_hashes;
use super::utils; use super::utils;
@ -8,4 +9,7 @@ use super::utils;
mod attestation_validation; mod attestation_validation;
mod signatures; mod signatures;
pub use self::attestation_validation::validate_attestation; pub use self::attestation_validation::{
validate_attestation,
AttestationValidationError,
};

View File

@ -24,7 +24,7 @@ pub fn verify_aggregate_signature_for_indices<T>(
attestation_indices: &[usize], attestation_indices: &[usize],
bitfield: &Bitfield, bitfield: &Bitfield,
validator_store: &ValidatorStore<T>) validator_store: &ValidatorStore<T>)
-> Result<(bool, Option<HashSet<usize>>), SignatureVerificationError> -> Result<Option<HashSet<usize>>, SignatureVerificationError>
where T: ClientDB + Sized where T: ClientDB + Sized
{ {
let mut voters = HashSet::new(); let mut voters = HashSet::new();
@ -41,9 +41,9 @@ pub fn verify_aggregate_signature_for_indices<T>(
} }
} }
if agg_sig.verify(&message, &agg_pub_key) { if agg_sig.verify(&message, &agg_pub_key) {
Ok((true, Some(voters))) Ok(Some(voters))
} else { } else {
Ok((false, None)) Ok(None)
} }
} }
@ -126,7 +126,7 @@ mod tests {
/* /*
* Test using all valid parameters. * Test using all valid parameters.
*/ */
let (is_valid, voters) = verify_aggregate_signature_for_indices( let voters = verify_aggregate_signature_for_indices(
&message, &message,
&agg_sig, &agg_sig,
&attestation_indices, &attestation_indices,
@ -134,7 +134,6 @@ mod tests {
&store).unwrap(); &store).unwrap();
let voters = voters.unwrap(); let voters = voters.unwrap();
assert_eq!(is_valid, true);
(0..signing_keypairs.len()) (0..signing_keypairs.len())
.for_each(|i| assert!(voters.contains(&i))); .for_each(|i| assert!(voters.contains(&i)));
(signing_keypairs.len()..non_signing_keypairs.len()) (signing_keypairs.len()..non_signing_keypairs.len())
@ -145,14 +144,13 @@ mod tests {
* parameters the same and assert that it fails. * parameters the same and assert that it fails.
*/ */
bitfield.set_bit(signing_keypairs.len() + 1, true); bitfield.set_bit(signing_keypairs.len() + 1, true);
let (is_valid, voters) = verify_aggregate_signature_for_indices( let voters = verify_aggregate_signature_for_indices(
&message, &message,
&agg_sig, &agg_sig,
&attestation_indices, &attestation_indices,
&bitfield, &bitfield,
&store).unwrap(); &store).unwrap();
assert_eq!(is_valid, false);
assert_eq!(voters, None); assert_eq!(voters, None);
} }
} }

View File

@ -1,18 +1,17 @@
/* use std::collections::HashMap;
use super::crystallized_state::CrystallizedState;
use super::active_state::ActiveState;
use super::attestation_record::AttestationRecord;
use super::block::Block;
use super::chain_config::ChainConfig;
*/
use super::block; use super::block;
use super::bls; use super::bls;
use super::Logger; use super::Logger;
use super::db; use super::db;
use super::attestation_record::AttestationRecord; use super::attestation_record;
use super::ssz; use super::ssz;
use super::transition::attestation_parent_hashes; use super::transition::attestation_parent_hashes;
use super::utils; use super::utils;
mod attestation; mod attestation;
mod ssz_block; mod ssz_block;
type Slot = u64;
type ShardId = u64;
type AttesterMap = HashMap<(Slot, ShardId), Vec<usize>>;
type ProposerMap = HashMap<Slot, usize>;

View File

@ -1,14 +1,34 @@
use std::sync::Arc;
use super::attestation::{
validate_attestation,
AttestationValidationError,
};
use super::attestation_record::{
AttestationRecord,
split_one_attestation,
split_all_attestations,
AttestationSplitError,
};
use super::{
AttesterMap,
ProposerMap,
};
use super::block::SszBlock; use super::block::SszBlock;
use super::Logger;
use super::db::{ use super::db::{
ClientDB, ClientDB,
DBError, DBError,
}; };
use super::Logger;
use super::db::stores::{ use super::db::stores::{
BlockStore, BlockStore,
PoWChainStore, PoWChainStore,
ValidatorStore, ValidatorStore,
}; };
use super::ssz::{
Decodable,
DecodeError,
};
use super::utils::types::Hash256;
pub enum BlockStatus { pub enum BlockStatus {
NewBlock, NewBlock,
@ -18,21 +38,25 @@ pub enum BlockStatus {
pub enum SszBlockValidationError { pub enum SszBlockValidationError {
FutureSlot, FutureSlot,
UnknownPoWChainRef, UnknownPoWChainRef,
BadAttestationSsz,
AttestationValidationError(AttestationValidationError),
InvalidAttestation,
NoProposerSignature,
BadProposerMap,
DatabaseError(String), DatabaseError(String),
} }
impl From<DBError> for SszBlockValidationError {
fn from(e: DBError) -> SszBlockValidationError {
SszBlockValidationError::DatabaseError(e.message)
}
}
#[allow(dead_code)] #[allow(dead_code)]
pub fn validate_ssz_block<T>(b: &SszBlock, pub fn validate_ssz_block<T>(b: &SszBlock,
expected_slot: u64, expected_slot: u64,
block_store: &BlockStore<T>,
pow_store: &PoWChainStore<T>, pow_store: &PoWChainStore<T>,
_validator_store: &ValidatorStore<T>, cycle_length: u8,
last_justified_slot: u64,
parent_hashes: Arc<Vec<Hash256>>,
proposer_map: Arc<ProposerMap>,
attester_map: Arc<AttesterMap>,
block_store: Arc<BlockStore<T>>,
validator_store: Arc<ValidatorStore<T>>,
_log: &Logger) _log: &Logger)
-> Result<BlockStatus, SszBlockValidationError> -> Result<BlockStatus, SszBlockValidationError>
where T: ClientDB + Sized where T: ClientDB + Sized
@ -41,7 +65,8 @@ pub fn validate_ssz_block<T>(b: &SszBlock,
return Ok(BlockStatus::KnownBlock); return Ok(BlockStatus::KnownBlock);
} }
if b.slot_number() > expected_slot { let block_slot = b.slot_number();
if block_slot > expected_slot {
return Err(SszBlockValidationError::FutureSlot); return Err(SszBlockValidationError::FutureSlot);
} }
@ -49,6 +74,84 @@ pub fn validate_ssz_block<T>(b: &SszBlock,
return Err(SszBlockValidationError::UnknownPoWChainRef); return Err(SszBlockValidationError::UnknownPoWChainRef);
} }
// Do validation here let attestations_ssz = &b.attestations();
let (first_attestation_ssz, next_index) = split_one_attestation(
&attestations_ssz,
0)?;
let (first_attestation, _) = AttestationRecord::ssz_decode(
&first_attestation_ssz, 0)?;
let attestation_voters = validate_attestation(
&first_attestation,
block_slot,
cycle_length,
last_justified_slot,
parent_hashes.clone(),
block_store.clone(),
validator_store.clone(),
attester_map.clone())?;
let attestation_voters = attestation_voters
.ok_or(SszBlockValidationError::InvalidAttestation)?;
let proposer = proposer_map.get(&block_slot)
.ok_or(SszBlockValidationError::BadProposerMap)?;
if !attestation_voters.contains(&proposer) {
return Err(SszBlockValidationError::NoProposerSignature);
}
let other_attestations = split_all_attestations(attestations_ssz,
next_index)?;
for attestation in other_attestations {
let (a, _) = AttestationRecord::ssz_decode(&attestation, 0)?;
let attestation_voters = validate_attestation(
&a,
block_slot,
cycle_length,
last_justified_slot,
parent_hashes.clone(),
block_store.clone(),
validator_store.clone(),
attester_map.clone())?;
if attestation_voters.is_none() {
return Err(SszBlockValidationError::InvalidAttestation);
}
}
Ok(BlockStatus::NewBlock) Ok(BlockStatus::NewBlock)
} }
impl From<DBError> for SszBlockValidationError {
fn from(e: DBError) -> Self {
SszBlockValidationError::DatabaseError(e.message)
}
}
impl From<AttestationSplitError> for SszBlockValidationError {
fn from(e: AttestationSplitError) -> Self {
match e {
AttestationSplitError::TooShort =>
SszBlockValidationError::BadAttestationSsz
}
}
}
impl From<DecodeError> for SszBlockValidationError {
fn from(e: DecodeError) -> Self {
match e {
DecodeError::TooShort =>
SszBlockValidationError::BadAttestationSsz,
DecodeError::TooLong =>
SszBlockValidationError::BadAttestationSsz,
}
}
}
impl From<AttestationValidationError> for SszBlockValidationError {
fn from(e: AttestationValidationError) -> Self {
SszBlockValidationError::AttestationValidationError(e)
}
}

View File

@ -4,7 +4,6 @@ use super::{
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum DecodeError { pub enum DecodeError {
OutOfBounds,
TooShort, TooShort,
TooLong, TooLong,
} }
@ -22,7 +21,7 @@ pub fn decode_ssz<T>(ssz_bytes: &[u8], index: usize)
where T: Decodable where T: Decodable
{ {
if index >= ssz_bytes.len() { if index >= ssz_bytes.len() {
return Err(DecodeError::OutOfBounds) return Err(DecodeError::TooShort)
} }
T::ssz_decode(ssz_bytes, index) T::ssz_decode(ssz_bytes, index)
} }