Verify attestation justified_block_hash.

Previously there was not a check that the hash was in the chain, just
that it was known (in the database in any chain)
This commit is contained in:
Paul Hauner 2018-10-12 00:41:47 +11:00
parent d729355c09
commit 6ef4268d6d
No known key found for this signature in database
GPG Key ID: 303E4494BB28068C
6 changed files with 167 additions and 36 deletions

View File

@ -12,7 +12,11 @@ use super::db::{
ClientDB, ClientDB,
DBError DBError
}; };
use super::db::stores::ValidatorStore; use super::db::stores::{
BlockStore,
BlockAtSlotError,
ValidatorStore,
};
use super::types::{ use super::types::{
Hash256, Hash256,
}; };
@ -27,7 +31,7 @@ pub enum AttestationValidationError {
SlotTooHigh, SlotTooHigh,
SlotTooLow, SlotTooLow,
JustifiedSlotIncorrect, JustifiedSlotIncorrect,
UnknownJustifiedBlock, InvalidJustifiedBlockHash,
TooManyObliqueHashes, TooManyObliqueHashes,
BadCurrentHashes, BadCurrentHashes,
BadObliqueHashes, BadObliqueHashes,
@ -54,10 +58,10 @@ pub struct AttestationValidationContext<T>
pub cycle_length: u8, pub cycle_length: u8,
/// The last justified slot as per the client's view of the canonical chain. /// The last justified slot as per the client's view of the canonical chain.
pub last_justified_slot: u64, pub last_justified_slot: u64,
/// The last justified block hash as per the client's view of the canonical chain.
pub last_justified_block_hash: Hash256,
/// A vec of the hashes of the blocks preceeding the present slot. /// A vec of the hashes of the blocks preceeding the present slot.
pub parent_hashes: Arc<Vec<Hash256>>, pub parent_hashes: Arc<Vec<Hash256>>,
/// The store containing block information.
pub block_store: Arc<BlockStore<T>>,
/// The store containing validator information. /// The store containing validator information.
pub validator_store: Arc<ValidatorStore<T>>, pub validator_store: Arc<ValidatorStore<T>>,
/// A map of (slot, shard_id) to the attestation set of validation indices. /// A map of (slot, shard_id) to the attestation set of validation indices.
@ -97,18 +101,10 @@ impl<T> AttestationValidationContext<T>
* The attestation must indicate that its last justified slot is the same as the last justified * The attestation must indicate that its last justified slot is the same as the last justified
* slot known to us. * slot known to us.
*/ */
if a.justified_slot != self.last_justified_slot { if a.justified_slot > self.last_justified_slot {
return Err(AttestationValidationError::JustifiedSlotIncorrect); return Err(AttestationValidationError::JustifiedSlotIncorrect);
} }
/*
* The specified justified block hash supplied in the attestation must match our knowledge
* of the last justified block this chain.
*/
if a.justified_block_hash != self.last_justified_block_hash {
return Err(AttestationValidationError::UnknownJustifiedBlock)
}
/* /*
* There is no need to include more oblique parents hashes than there are blocks * There is no need to include more oblique parents hashes than there are blocks
* in a cycle. * in a cycle.
@ -147,13 +143,35 @@ impl<T> AttestationValidationContext<T>
return Err(AttestationValidationError::InvalidBitfieldEndBits) return Err(AttestationValidationError::InvalidBitfieldEndBits)
} }
/*
* Generate the parent hashes for this attestation
*/
let parent_hashes = attestation_parent_hashes(
self.cycle_length,
self.block_slot,
a.slot,
&self.parent_hashes,
&a.oblique_parent_hashes)?;
/*
* The specified justified block hash supplied in the attestation must be in the chain at
* the given slot number.
*
* First, we find the latest parent hash from the parent_hashes array. Then, using the
* 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()
.ok_or(AttestationValidationError::BadCurrentHashes)?;
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)
};
/*
* Generate the message that this attestation aggregate signature must sign across.
*/
let signed_message = { let signed_message = {
let parent_hashes = attestation_parent_hashes(
self.cycle_length,
self.block_slot,
a.slot,
&self.parent_hashes,
&a.oblique_parent_hashes)?;
generate_signed_message( generate_signed_message(
a.slot, a.slot,
&parent_hashes, &parent_hashes,
@ -201,6 +219,16 @@ impl From<ParentHashesError> for AttestationValidationError {
} }
} }
impl From<BlockAtSlotError> for AttestationValidationError {
fn from(e: BlockAtSlotError) -> Self {
match e {
BlockAtSlotError::DBError(s) => AttestationValidationError::DBError(s),
_ => AttestationValidationError::InvalidJustifiedBlockHash
}
}
}
impl From<DBError> for AttestationValidationError { impl From<DBError> for AttestationValidationError {
fn from(e: DBError) -> Self { fn from(e: DBError) -> Self {
AttestationValidationError::DBError(e.message) AttestationValidationError::DBError(e.message)

View File

@ -207,8 +207,8 @@ impl<T> BlockValidationContext<T>
block_slot, block_slot,
cycle_length: self.cycle_length, cycle_length: self.cycle_length,
last_justified_slot: self.last_justified_slot, last_justified_slot: self.last_justified_slot,
last_justified_block_hash: self.last_justified_block_hash,
parent_hashes: self.parent_hashes.clone(), parent_hashes: self.parent_hashes.clone(),
block_store: self.block_store.clone(),
validator_store: self.validator_store.clone(), validator_store: self.validator_store.clone(),
attester_map: self.attester_map.clone(), attester_map: self.attester_map.clone(),
}); });

View File

@ -3,11 +3,15 @@ use std::sync::Arc;
use super::db::{ use super::db::{
MemoryDB, MemoryDB,
}; };
use super::db::stores::ValidatorStore; use super::db::stores::{
ValidatorStore,
BlockStore,
};
use super::types::{ use super::types::{
AttestationRecord, AttestationRecord,
AttesterMap, AttesterMap,
Bitfield, Bitfield,
Block,
Hash256, Hash256,
}; };
use super::validation::attestation_validation::{ use super::validation::attestation_validation::{
@ -28,15 +32,18 @@ use super::hashing::{
pub struct TestStore { pub struct TestStore {
pub db: Arc<MemoryDB>, pub db: Arc<MemoryDB>,
pub validator: Arc<ValidatorStore<MemoryDB>>, pub validator: Arc<ValidatorStore<MemoryDB>>,
pub block: Arc<BlockStore<MemoryDB>>,
} }
impl TestStore { impl TestStore {
pub fn new() -> Self { pub fn new() -> Self {
let db = Arc::new(MemoryDB::open()); let db = Arc::new(MemoryDB::open());
let validator = Arc::new(ValidatorStore::new(db.clone())); let validator = Arc::new(ValidatorStore::new(db.clone()));
let block = Arc::new(BlockStore::new(db.clone()));
Self { Self {
db, db,
validator, validator,
block,
} }
} }
} }
@ -73,7 +80,8 @@ pub fn generate_attestation(shard_id: u16,
justified_block_hash: &Hash256, justified_block_hash: &Hash256,
cycle_length: u8, cycle_length: u8,
parent_hashes: &[Hash256], parent_hashes: &[Hash256],
signing_keys: &[Option<SecretKey>]) signing_keys: &[Option<SecretKey>],
block_store: &BlockStore<MemoryDB>)
-> AttestationRecord -> AttestationRecord
{ {
let mut attester_bitfield = Bitfield::new(); let mut attester_bitfield = Bitfield::new();
@ -86,6 +94,11 @@ pub fn generate_attestation(shard_id: u16,
&parent_hashes[first..last] &parent_hashes[first..last]
}; };
/*
* Create a justified block at the correct slot and store it in the db.
*/
create_block_at_slot(&block_store, &justified_block_hash, justified_slot);
/* /*
* Generate the message that will be signed across for this attr record. * Generate the message that will be signed across for this attr record.
*/ */
@ -120,6 +133,31 @@ pub fn generate_attestation(shard_id: u16,
} }
} }
/// Create a minimum viable block at some slot.
///
/// Allows the validation function to read the block and verify its slot.
pub fn create_block_at_slot(block_store: &BlockStore<MemoryDB>, hash: &Hash256, slot: u64) {
let mut justified_block = Block::zero();
justified_block.attestations.push(AttestationRecord::zero());
justified_block.slot_number = slot;
let mut s = SszStream::new();
s.append(&justified_block);
let justified_block_ssz = s.drain();
block_store.put_serialized_block(&hash.to_vec(), &justified_block_ssz).unwrap();
}
/// Inserts a justified_block_hash in a position that will be referenced by an attestation record.
pub fn insert_justified_block_hash(
parent_hashes: &mut Vec<Hash256>,
justified_block_hash: &Hash256,
block_slot: u64,
attestation_slot: u64)
{
let attestation_parent_hash_index = parent_hashes.len() - 1 -
(block_slot as usize - attestation_slot as usize);
parent_hashes[attestation_parent_hash_index] = justified_block_hash.clone();
}
pub fn setup_attestation_validation_test(shard_id: u16, attester_count: usize) pub fn setup_attestation_validation_test(shard_id: u16, attester_count: usize)
-> TestRig -> TestRig
{ {
@ -127,15 +165,24 @@ pub fn setup_attestation_validation_test(shard_id: u16, attester_count: usize)
let block_slot = 10000; let block_slot = 10000;
let cycle_length: u8 = 64; let cycle_length: u8 = 64;
let last_justified_slot = block_slot - u64::from(cycle_length); let mut parent_hashes: Vec<Hash256> = (0..(cycle_length * 2))
let parent_hashes: Vec<Hash256> = (0..(cycle_length * 2))
.map(|i| Hash256::from(i as u64)) .map(|i| Hash256::from(i as u64))
.collect(); .collect();
let parent_hashes = Arc::new(parent_hashes); let attestation_slot = block_slot - 1;
let last_justified_slot = attestation_slot - 1;
let justified_block_hash = Hash256::from("justified_block".as_bytes()); let justified_block_hash = Hash256::from("justified_block".as_bytes());
let shard_block_hash = Hash256::from("shard_block".as_bytes()); let shard_block_hash = Hash256::from("shard_block".as_bytes());
let attestation_slot = block_slot - 1; /*
* Insert the required justified_block_hash into parent_hashes
*/
insert_justified_block_hash(
&mut parent_hashes,
&justified_block_hash,
block_slot,
attestation_slot);
let parent_hashes = Arc::new(parent_hashes);
let mut keypairs = vec![]; let mut keypairs = vec![];
let mut signing_keys = vec![]; let mut signing_keys = vec![];
@ -159,8 +206,8 @@ pub fn setup_attestation_validation_test(shard_id: u16, attester_count: usize)
block_slot, block_slot,
cycle_length, cycle_length,
last_justified_slot, last_justified_slot,
last_justified_block_hash: justified_block_hash,
parent_hashes: parent_hashes.clone(), parent_hashes: parent_hashes.clone(),
block_store: stores.block.clone(),
validator_store: stores.validator.clone(), validator_store: stores.validator.clone(),
attester_map: Arc::new(attester_map), attester_map: Arc::new(attester_map),
}; };
@ -173,7 +220,8 @@ pub fn setup_attestation_validation_test(shard_id: u16, attester_count: usize)
&justified_block_hash, &justified_block_hash,
cycle_length, cycle_length,
&parent_hashes.clone(), &parent_hashes.clone(),
&signing_keys); &signing_keys,
&stores.block);
TestRig { TestRig {
attestation, attestation,

View File

@ -3,6 +3,7 @@ use std::sync::Arc;
use super::helpers::{ use super::helpers::{
TestRig, TestRig,
setup_attestation_validation_test, setup_attestation_validation_test,
create_block_at_slot,
}; };
use super::validation::attestation_validation::{ use super::validation::attestation_validation::{
AttestationValidationError, AttestationValidationError,
@ -56,12 +57,25 @@ fn test_attestation_validation_invalid_justified_slot_incorrect() {
let original = rig.attestation.justified_slot; let original = rig.attestation.justified_slot;
rig.attestation.justified_slot = original - 1; rig.attestation.justified_slot = original - 1;
// Ensures we don't get a bad justified block error instead.
create_block_at_slot(
&rig.stores.block,
&rig.attestation.justified_block_hash,
rig.attestation.justified_slot);
let result = rig.context.validate_attestation(&rig.attestation); let result = rig.context.validate_attestation(&rig.attestation);
assert_eq!(result, Err(AttestationValidationError::JustifiedSlotIncorrect)); assert_eq!(result, Err(AttestationValidationError::BadAggregateSignature));
rig.attestation.justified_slot = original + 1; rig.attestation.justified_slot = original + 1;
// Ensures we don't get a bad justified block error instead.
create_block_at_slot(
&rig.stores.block,
&rig.attestation.justified_block_hash,
rig.attestation.justified_slot);
// Ensures we don't get an error that the last justified slot is ahead of the context justified
// slot.
rig.context.last_justified_slot = rig.attestation.justified_slot;
let result = rig.context.validate_attestation(&rig.attestation); let result = rig.context.validate_attestation(&rig.attestation);
assert_eq!(result, Err(AttestationValidationError::JustifiedSlotIncorrect)); assert_eq!(result, Err(AttestationValidationError::BadAggregateSignature));
} }
#[test] #[test]
@ -139,13 +153,38 @@ fn test_attestation_validation_invalid_invalid_bitfield_end_bit_with_irreguar_bi
} }
#[test] #[test]
fn test_attestation_validation_invalid_unknown_justfied_block_hash() { fn test_attestation_validation_invalid_unknown_justified_block_hash() {
let mut rig = generic_rig(); let mut rig = generic_rig();
rig.attestation.justified_block_hash = Hash256::from("unknown block hash".as_bytes()); rig.attestation.justified_block_hash = Hash256::from("unknown block hash".as_bytes());
let result = rig.context.validate_attestation(&rig.attestation); let result = rig.context.validate_attestation(&rig.attestation);
assert_eq!(result, Err(AttestationValidationError::UnknownJustifiedBlock)); assert_eq!(result, Err(AttestationValidationError::InvalidJustifiedBlockHash));
}
#[test]
fn test_attestation_validation_invalid_unknown_justified_block_hash_wrong_slot() {
let rig = generic_rig();
/*
* justified_block_hash points to a block with a slot that is too high.
*/
create_block_at_slot(
&rig.stores.block,
&rig.attestation.justified_block_hash,
rig.attestation.justified_slot + 1);
let result = rig.context.validate_attestation(&rig.attestation);
assert_eq!(result, Err(AttestationValidationError::InvalidJustifiedBlockHash));
/*
* justified_block_hash points to a block with a slot that is too low.
*/
create_block_at_slot(
&rig.stores.block,
&rig.attestation.justified_block_hash,
rig.attestation.justified_slot - 1);
let result = rig.context.validate_attestation(&rig.attestation);
assert_eq!(result, Err(AttestationValidationError::InvalidJustifiedBlockHash));
} }
#[test] #[test]

View File

@ -1,6 +1,9 @@
use std::sync::Arc; use std::sync::Arc;
use super::generate_attestation; use super::attestation_validation::helpers::{
generate_attestation,
insert_justified_block_hash,
};
use super::bls::{ use super::bls::{
Keypair, Keypair,
}; };
@ -82,7 +85,7 @@ pub fn setup_block_validation_scenario(params: &BlockTestParams)
let block_slot = params.block_slot; let block_slot = params.block_slot;
let attestations_justified_slot = params.attestations_justified_slot; let attestations_justified_slot = params.attestations_justified_slot;
let parent_hashes: Vec<Hash256> = (0..(cycle_length * 2)) let mut parent_hashes: Vec<Hash256> = (0..(cycle_length * 2))
.map(|i| Hash256::from(i as u64)) .map(|i| Hash256::from(i as u64))
.collect(); .collect();
let parent_hash = Hash256::from("parent_hash".as_bytes()); let parent_hash = Hash256::from("parent_hash".as_bytes());
@ -93,6 +96,9 @@ pub fn setup_block_validation_scenario(params: &BlockTestParams)
let crystallized_state_root = Hash256::from("cry_state".as_bytes()); let crystallized_state_root = Hash256::from("cry_state".as_bytes());
let shard_block_hash = Hash256::from("shard_block_hash".as_bytes()); let shard_block_hash = Hash256::from("shard_block_hash".as_bytes());
/*
* Store a valid PoW chain ref
*/
stores.pow_chain.put_block_hash(pow_chain_ref.as_ref()).unwrap(); stores.pow_chain.put_block_hash(pow_chain_ref.as_ref()).unwrap();
/* /*
@ -117,6 +123,15 @@ pub fn setup_block_validation_scenario(params: &BlockTestParams)
let mut attester_map = AttesterMap::new(); let mut attester_map = AttesterMap::new();
let mut attestations = vec![]; let mut attestations = vec![];
let mut keypairs = vec![]; let mut keypairs = vec![];
/*
* Insert the required justified_block_hash into parent_hashes
*/
insert_justified_block_hash(
&mut parent_hashes,
&justified_block_hash,
block_slot,
attestation_slot);
/* /*
* For each shard in this slot, generate an attestation. * For each shard in this slot, generate an attestation.
*/ */
@ -146,7 +161,8 @@ pub fn setup_block_validation_scenario(params: &BlockTestParams)
&justified_block_hash, &justified_block_hash,
cycle_length, cycle_length,
&parent_hashes, &parent_hashes,
&signing_keys[..]); &signing_keys[..],
&stores.block);
attestations.push(attestation); attestations.push(attestation);
} }
(attester_map, attestations, keypairs) (attester_map, attestations, keypairs)

View File

@ -9,4 +9,4 @@ use super::ssz_helpers;
use super::types; use super::types;
use super::validation; use super::validation;
use super::attestation_validation::helpers::generate_attestation; use super::attestation_validation;