Remove outdated attestation_validation crate

This commit is contained in:
Paul Hauner 2019-01-24 11:42:40 +11:00
parent db6d40e614
commit 2bcce37b3c
No known key found for this signature in database
GPG Key ID: D362883A9218FCC6
10 changed files with 0 additions and 653 deletions

View File

@ -1,6 +1,5 @@
[workspace]
members = [
"eth2/attestation_validation",
"eth2/block_producer",
"eth2/genesis",
"eth2/naive_fork_choice",

View File

@ -1,12 +0,0 @@
[package]
name = "attestation_validation"
version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2018"
[dependencies]
bls = { path = "../utils/bls" }
db = { path = "../../beacon_node/db" }
hashing = { path = "../utils/hashing" }
ssz = { path = "../utils/ssz" }
types = { path = "../types" }

View File

@ -1,246 +0,0 @@
use super::{Error, Invalid, Outcome};
/// Check that an attestation is valid to be included in some block.
pub fn validate_attestation_for_block(
attestation_slot: u64,
block_slot: u64,
parent_block_slot: u64,
min_attestation_inclusion_delay: u64,
epoch_length: u64,
) -> Result<Outcome, Error> {
/*
* There is a delay before an attestation may be included in a block, quantified by
* `slots` and defined as `min_attestation_inclusion_delay`.
*
* So, an attestation must be at least `min_attestation_inclusion_delay` slots "older" than the
* block it is contained in.
*/
verify_or!(
// TODO: this differs from the spec as it does not handle underflows correctly.
// https://github.com/sigp/lighthouse/issues/95
attestation_slot < block_slot.saturating_sub(min_attestation_inclusion_delay - 1),
reject!(Invalid::AttestationTooRecent)
);
/*
* A block may not include attestations reference slots more than an epoch length + 1 prior to
* the block slot.
*/
verify_or!(
attestation_slot >= parent_block_slot.saturating_sub(epoch_length + 1),
reject!(Invalid::AttestationTooOld)
);
accept!()
}
#[cfg(test)]
mod tests {
use super::*;
/*
* Invalid::AttestationTooOld tests.
*/
#[test]
fn test_inclusion_too_old_minimal() {
let min_attestation_inclusion_delay = 10;
let epoch_length = 20;
let block_slot = 100;
let parent_block_slot = block_slot - 1;
let attestation_slot = block_slot - min_attestation_inclusion_delay;
let outcome = validate_attestation_for_block(
attestation_slot,
block_slot,
parent_block_slot,
min_attestation_inclusion_delay,
epoch_length,
);
assert_eq!(outcome, Ok(Outcome::Valid));
}
#[test]
fn test_inclusion_too_old_maximal() {
let min_attestation_inclusion_delay = 10;
let epoch_length = 20;
let block_slot = 100;
let parent_block_slot = block_slot - 1;
let attestation_slot = block_slot - epoch_length + 1;
let outcome = validate_attestation_for_block(
attestation_slot,
block_slot,
parent_block_slot,
min_attestation_inclusion_delay,
epoch_length,
);
assert_eq!(outcome, Ok(Outcome::Valid));
}
#[test]
fn test_inclusion_too_old_saturating_non_zero_attestation_slot() {
let min_attestation_inclusion_delay = 10;
let epoch_length = 20;
let block_slot = epoch_length + 1;
let parent_block_slot = block_slot - 1;
let attestation_slot = block_slot - min_attestation_inclusion_delay;
let outcome = validate_attestation_for_block(
attestation_slot,
block_slot,
parent_block_slot,
min_attestation_inclusion_delay,
epoch_length,
);
assert_eq!(outcome, Ok(Outcome::Valid));
}
#[test]
fn test_inclusion_too_old_saturating_zero_attestation_slot() {
let min_attestation_inclusion_delay = 10;
let epoch_length = 20;
let block_slot = epoch_length + 1;
let parent_block_slot = block_slot - 1;
let attestation_slot = 0;
let outcome = validate_attestation_for_block(
attestation_slot,
block_slot,
parent_block_slot,
min_attestation_inclusion_delay,
epoch_length,
);
assert_eq!(outcome, Ok(Outcome::Valid));
}
#[test]
fn test_inclusion_too_old() {
let min_attestation_inclusion_delay = 10;
let epoch_length = 20;
let block_slot = epoch_length * 2;
let parent_block_slot = block_slot - 1;
let attestation_slot = parent_block_slot - (epoch_length + 2);
let outcome = validate_attestation_for_block(
attestation_slot,
block_slot,
parent_block_slot,
min_attestation_inclusion_delay,
epoch_length,
);
assert_eq!(outcome, Ok(Outcome::Invalid(Invalid::AttestationTooOld)));
}
/*
* Invalid::AttestationTooRecent tests.
*/
#[test]
fn test_inclusion_too_recent_minimal() {
let parent_block_slot = 99;
let min_attestation_inclusion_delay = 10;
let epoch_length = 20;
let block_slot = 100;
let attestation_slot = block_slot - min_attestation_inclusion_delay;
let outcome = validate_attestation_for_block(
attestation_slot,
block_slot,
parent_block_slot,
min_attestation_inclusion_delay,
epoch_length,
);
assert_eq!(outcome, Ok(Outcome::Valid));
}
#[test]
fn test_inclusion_too_recent_maximal() {
let parent_block_slot = 99;
let min_attestation_inclusion_delay = 10;
let epoch_length = 20;
let block_slot = 100;
let attestation_slot = block_slot - epoch_length;
let outcome = validate_attestation_for_block(
attestation_slot,
block_slot,
parent_block_slot,
min_attestation_inclusion_delay,
epoch_length,
);
assert_eq!(outcome, Ok(Outcome::Valid));
}
#[test]
fn test_inclusion_too_recent_insufficient() {
let parent_block_slot = 99;
let min_attestation_inclusion_delay = 10;
let epoch_length = 20;
let block_slot = 100;
let attestation_slot = block_slot - (min_attestation_inclusion_delay - 1);
let outcome = validate_attestation_for_block(
attestation_slot,
block_slot,
parent_block_slot,
min_attestation_inclusion_delay,
epoch_length,
);
assert_eq!(outcome, Ok(Outcome::Invalid(Invalid::AttestationTooRecent)));
}
#[test]
fn test_inclusion_too_recent_first_possible_slot() {
let min_attestation_inclusion_delay = 10;
let epoch_length = 20;
let block_slot = min_attestation_inclusion_delay;
let attestation_slot = 0;
let parent_block_slot = block_slot - 1;
let outcome = validate_attestation_for_block(
attestation_slot,
block_slot,
parent_block_slot,
min_attestation_inclusion_delay,
epoch_length,
);
assert_eq!(outcome, Ok(Outcome::Valid));
}
#[test]
fn test_inclusion_too_recent_saturation_non_zero_slot() {
let min_attestation_inclusion_delay = 10;
let epoch_length = 20;
let block_slot = min_attestation_inclusion_delay - 1;
let parent_block_slot = block_slot - 1;
let attestation_slot = 0;
let outcome = validate_attestation_for_block(
attestation_slot,
block_slot,
parent_block_slot,
min_attestation_inclusion_delay,
epoch_length,
);
assert_eq!(outcome, Ok(Outcome::Invalid(Invalid::AttestationTooRecent)));
}
#[test]
fn test_inclusion_too_recent_saturation_zero_slot() {
let min_attestation_inclusion_delay = 10;
let epoch_length = 20;
let block_slot = min_attestation_inclusion_delay - 1;
let parent_block_slot = block_slot - 1;
let attestation_slot = 0;
let outcome = validate_attestation_for_block(
attestation_slot,
block_slot,
parent_block_slot,
min_attestation_inclusion_delay,
epoch_length,
);
assert_eq!(outcome, Ok(Outcome::Invalid(Invalid::AttestationTooRecent)));
}
}

View File

@ -1,37 +0,0 @@
/// Reasons why an `AttestationRecord` can be invalid.
#[derive(PartialEq, Debug)]
pub enum Invalid {
AttestationTooRecent,
AttestationTooOld,
JustifiedSlotImpermissable,
JustifiedBlockNotInChain,
JustifiedBlockHashMismatch,
UnknownShard,
ShardBlockHashMismatch,
SignatureInvalid,
}
/// The outcome of validating the `AttestationRecord`.
///
/// Distinct from the `Error` enum as an `Outcome` indicates that validation executed sucessfully
/// and determined the validity `AttestationRecord`.
#[derive(PartialEq, Debug)]
pub enum Outcome {
Valid,
Invalid(Invalid),
}
/// Errors that prevent this function from correctly validating the `AttestationRecord`.
///
/// Distinct from the `Outcome` enum as `Errors` indicate that validation encountered an unexpected
/// condition and was unable to perform its duty.
#[derive(PartialEq, Debug)]
pub enum Error {
BlockHasNoParent,
BadValidatorIndex,
UnableToLookupBlockAtSlot,
OutOfBoundsBitfieldIndex,
PublicKeyCorrupt,
NoPublicKeyForValidator,
DBError(String),
}

View File

@ -1,80 +0,0 @@
use super::db::stores::{BeaconBlockAtSlotError, BeaconBlockStore};
use super::db::ClientDB;
use super::types::AttestationData;
use super::types::Hash256;
use super::{Error, Invalid, Outcome};
use std::sync::Arc;
/// Verify that a attestation's `data.justified_block_hash` matches the local hash of the block at the
/// attestation's `data.justified_slot`.
///
/// `chain_tip_block_hash` is the tip of the chain in which the justified block hash should exist
/// locally. As Lightouse stores multiple chains locally, it is possible to have multiple blocks at
/// the same slot. `chain_tip_block_hash` serves to restrict the lookup to a single chain, where
/// each slot may have exactly zero or one blocks.
pub fn validate_attestation_justified_block_hash<T>(
data: &AttestationData,
chain_tip_block_hash: &Hash256,
block_store: &Arc<BeaconBlockStore<T>>,
) -> Result<Outcome, Error>
where
T: ClientDB + Sized,
{
/*
* The `justified_block_hash` in the attestation must match exactly the hash of the block at
* that slot in the local chain.
*
* This condition also infers that the `justified_slot` specified in attestation must exist
* locally.
*/
match block_hash_at_slot(chain_tip_block_hash, data.justified_slot, block_store)? {
None => reject!(Invalid::JustifiedBlockNotInChain),
Some(local_justified_block_hash) => {
verify_or!(
data.justified_block_hash == local_justified_block_hash,
reject!(Invalid::JustifiedBlockHashMismatch)
);
}
};
accept!()
}
/// Returns the hash (or None) of a block at a slot in the chain that is specified by
/// `chain_tip_hash`.
///
/// Given that the database stores multiple chains, it is possible for there to be multiple blocks
/// at the given slot. `chain_tip_hash` specifies exactly which chain should be used.
fn block_hash_at_slot<T>(
chain_tip_hash: &Hash256,
slot: u64,
block_store: &Arc<BeaconBlockStore<T>>,
) -> Result<Option<Hash256>, Error>
where
T: ClientDB + Sized,
{
match block_store.block_at_slot(&chain_tip_hash, slot)? {
None => Ok(None),
Some((hash_bytes, _)) => Ok(Some(Hash256::from(&hash_bytes[..]))),
}
}
impl From<BeaconBlockAtSlotError> for Error {
fn from(e: BeaconBlockAtSlotError) -> Self {
match e {
BeaconBlockAtSlotError::DBError(s) => Error::DBError(s),
_ => Error::UnableToLookupBlockAtSlot,
}
}
}
#[cfg(test)]
mod tests {
/*
* TODO: Implement tests.
*
* These tests will require the `BeaconBlock` and `BeaconBlockBody` updates, which are not
* yet included in the code base. Adding tests now will result in duplicated work.
*
* https://github.com/sigp/lighthouse/issues/97
*/
}

View File

@ -1,39 +0,0 @@
use super::types::{AttestationData, BeaconState};
use super::{Error, Invalid, Outcome};
/// Verify that an attestation's `data.justified_slot` matches the justified slot known to the
/// `state`.
///
/// In the case that an attestation references a slot _before_ the latest state transition, is
/// acceptable for the attestation to reference the previous known `justified_slot`. If this were
/// not the case, all attestations created _prior_ to the last state recalculation would be rejected
/// if a block was justified in that state recalculation. It is both ideal and likely that blocks
/// will be justified during a state recalcuation.
pub fn validate_attestation_justified_slot(
data: &AttestationData,
state: &BeaconState,
epoch_length: u64,
) -> Result<Outcome, Error> {
let permissable_justified_slot = if data.slot >= state.slot - (state.slot % epoch_length) {
state.justified_slot
} else {
state.previous_justified_slot
};
verify_or!(
data.justified_slot == permissable_justified_slot,
reject!(Invalid::JustifiedSlotImpermissable)
);
accept!()
}
#[cfg(test)]
mod tests {
/*
* TODO: Implement tests.
*
* These tests will require the `BeaconBlock` and `BeaconBlockBody` updates, which are not
* yet included in the code base. Adding tests now will result in duplicated work.
*
* https://github.com/sigp/lighthouse/issues/97
*/
}

View File

@ -1,22 +0,0 @@
extern crate bls;
extern crate db;
extern crate hashing;
extern crate ssz;
extern crate types;
#[macro_use]
mod macros;
mod block_inclusion;
mod enums;
mod justified_block;
mod justified_slot;
mod shard_block;
mod signature;
pub use crate::block_inclusion::validate_attestation_for_block;
pub use crate::enums::{Error, Invalid, Outcome};
pub use crate::justified_block::validate_attestation_justified_block_hash;
pub use crate::justified_slot::validate_attestation_justified_slot;
pub use crate::shard_block::validate_attestation_data_shard_block_hash;
pub use crate::signature::validate_attestation_signature;

View File

@ -1,19 +0,0 @@
macro_rules! verify_or {
($condition: expr, $result: expr) => {
if !$condition {
$result
}
};
}
macro_rules! reject {
($result: expr) => {
return Ok(Outcome::Invalid($result));
};
}
macro_rules! accept {
() => {
Ok(Outcome::Valid)
};
}

View File

@ -1,46 +0,0 @@
use super::db::ClientDB;
use super::types::{AttestationData, BeaconState};
use super::{Error, Invalid, Outcome};
/// Check that an attestation is valid with reference to some state.
pub fn validate_attestation_data_shard_block_hash<T>(
data: &AttestationData,
state: &BeaconState,
) -> Result<Outcome, Error>
where
T: ClientDB + Sized,
{
/*
* The `shard_block_hash` in the state's `latest_crosslinks` must match either the
* `latest_crosslink_hash` or the `shard_block_hash` on the attestation.
*
* TODO: figure out the reasoning behind this.
*/
match state.latest_crosslinks.get(data.shard as usize) {
None => reject!(Invalid::UnknownShard),
Some(crosslink) => {
let local_shard_block_hash = crosslink.shard_block_root;
let shard_block_hash_is_permissable = {
(local_shard_block_hash == data.latest_crosslink_hash)
|| (local_shard_block_hash == data.shard_block_hash)
};
verify_or!(
shard_block_hash_is_permissable,
reject!(Invalid::ShardBlockHashMismatch)
);
}
};
accept!()
}
#[cfg(test)]
mod tests {
/*
* TODO: Implement tests.
*
* These tests will require the `BeaconBlock` and `BeaconBlockBody` updates, which are not
* yet included in the code base. Adding tests now will result in duplicated work.
*
* https://github.com/sigp/lighthouse/issues/97
*/
}

View File

@ -1,151 +0,0 @@
use super::bls::{AggregatePublicKey, AggregateSignature};
use super::db::stores::{ValidatorStore, ValidatorStoreError};
use super::db::ClientDB;
use super::types::{AttestationData, Bitfield, BitfieldError};
use super::{Error, Invalid, Outcome};
/// Validate that some signature is correct for some attestation data and known validator set.
pub fn validate_attestation_signature<T>(
attestation_data: &AttestationData,
participation_bitfield: &Bitfield,
aggregate_signature: &AggregateSignature,
attestation_indices: &[usize],
validator_store: &ValidatorStore<T>,
) -> Result<Outcome, Error>
where
T: ClientDB + Sized,
{
let mut agg_pub_key = AggregatePublicKey::new();
for i in 0..attestation_indices.len() {
let voted = participation_bitfield.get(i)?;
if voted {
// De-reference the attestation index into a canonical ValidatorRecord index.
let validator = *attestation_indices.get(i).ok_or(Error::BadValidatorIndex)?;
// Load the public key.
let pub_key = validator_store
.get_public_key_by_index(validator)?
.ok_or(Error::NoPublicKeyForValidator)?;
// Aggregate the public key.
agg_pub_key.add(&pub_key.as_raw());
}
}
let signed_message = attestation_data_signing_message(attestation_data);
verify_or!(
// TODO: ensure "domain" for aggregate signatures is included.
// https://github.com/sigp/lighthouse/issues/91
aggregate_signature.verify(&signed_message, &agg_pub_key),
reject!(Invalid::SignatureInvalid)
);
accept!()
}
fn attestation_data_signing_message(attestation_data: &AttestationData) -> Vec<u8> {
let mut signed_message = attestation_data.canonical_root().to_vec();
signed_message.append(&mut vec![0]);
signed_message
}
impl From<ValidatorStoreError> for Error {
fn from(error: ValidatorStoreError) -> Self {
match error {
ValidatorStoreError::DBError(s) => Error::DBError(s),
ValidatorStoreError::DecodeError => Error::PublicKeyCorrupt,
}
}
}
impl From<BitfieldError> for Error {
fn from(_error: BitfieldError) -> Self {
Error::OutOfBoundsBitfieldIndex
}
}
#[cfg(test)]
mod tests {
use super::super::bls::{Keypair, Signature};
use super::super::db::MemoryDB;
use super::*;
use std::sync::Arc;
/*
* TODO: Test cases are not comprehensive.
* https://github.com/sigp/lighthouse/issues/94
*/
#[test]
fn test_signature_verification() {
let attestation_data = AttestationData::zero();
let message = attestation_data_signing_message(&attestation_data);
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<usize> = (0..all_keypairs.len()).collect();
let mut bitfield = Bitfield::from_elem(all_keypairs.len(), false);
for i in 0..signing_keypairs.len() {
bitfield.set(i, true).unwrap();
}
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);
}
/*
* Test using all valid parameters.
*/
let outcome = validate_attestation_signature(
&attestation_data,
&bitfield,
&agg_sig,
&attestation_indices,
&store,
)
.unwrap();
assert_eq!(outcome, Outcome::Valid);
/*
* Add another validator to the bitfield, run validation will all other
* parameters the same and assert that it fails.
*/
bitfield.set(signing_keypairs.len() + 1, true).unwrap();
let outcome = validate_attestation_signature(
&attestation_data,
&bitfield,
&agg_sig,
&attestation_indices,
&store,
)
.unwrap();
assert_eq!(outcome, Outcome::Invalid(Invalid::SignatureInvalid));
}
}