diff --git a/Cargo.toml b/Cargo.toml index 26f7d293af..5a25e34846 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,5 @@ [workspace] members = [ - "eth2/attestation_validation", "eth2/block_producer", "eth2/genesis", "eth2/naive_fork_choice", diff --git a/eth2/attestation_validation/Cargo.toml b/eth2/attestation_validation/Cargo.toml deleted file mode 100644 index b944f90f95..0000000000 --- a/eth2/attestation_validation/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "attestation_validation" -version = "0.1.0" -authors = ["Paul Hauner "] -edition = "2018" - -[dependencies] -bls = { path = "../utils/bls" } -db = { path = "../../beacon_node/db" } -hashing = { path = "../utils/hashing" } -ssz = { path = "../utils/ssz" } -types = { path = "../types" } diff --git a/eth2/attestation_validation/src/block_inclusion.rs b/eth2/attestation_validation/src/block_inclusion.rs deleted file mode 100644 index 76a5c97979..0000000000 --- a/eth2/attestation_validation/src/block_inclusion.rs +++ /dev/null @@ -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 { - /* - * 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))); - } -} diff --git a/eth2/attestation_validation/src/enums.rs b/eth2/attestation_validation/src/enums.rs deleted file mode 100644 index 6c94c628cd..0000000000 --- a/eth2/attestation_validation/src/enums.rs +++ /dev/null @@ -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), -} diff --git a/eth2/attestation_validation/src/justified_block.rs b/eth2/attestation_validation/src/justified_block.rs deleted file mode 100644 index e910819c82..0000000000 --- a/eth2/attestation_validation/src/justified_block.rs +++ /dev/null @@ -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( - data: &AttestationData, - chain_tip_block_hash: &Hash256, - block_store: &Arc>, -) -> Result -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( - chain_tip_hash: &Hash256, - slot: u64, - block_store: &Arc>, -) -> Result, 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 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 - */ -} diff --git a/eth2/attestation_validation/src/justified_slot.rs b/eth2/attestation_validation/src/justified_slot.rs deleted file mode 100644 index ea71f2616f..0000000000 --- a/eth2/attestation_validation/src/justified_slot.rs +++ /dev/null @@ -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 { - 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 - */ -} diff --git a/eth2/attestation_validation/src/lib.rs b/eth2/attestation_validation/src/lib.rs deleted file mode 100644 index 825371ed09..0000000000 --- a/eth2/attestation_validation/src/lib.rs +++ /dev/null @@ -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; diff --git a/eth2/attestation_validation/src/macros.rs b/eth2/attestation_validation/src/macros.rs deleted file mode 100644 index faae00fcfa..0000000000 --- a/eth2/attestation_validation/src/macros.rs +++ /dev/null @@ -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) - }; -} diff --git a/eth2/attestation_validation/src/shard_block.rs b/eth2/attestation_validation/src/shard_block.rs deleted file mode 100644 index e1d9487de8..0000000000 --- a/eth2/attestation_validation/src/shard_block.rs +++ /dev/null @@ -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( - data: &AttestationData, - state: &BeaconState, -) -> Result -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 - */ -} diff --git a/eth2/attestation_validation/src/signature.rs b/eth2/attestation_validation/src/signature.rs deleted file mode 100644 index 7d08be337b..0000000000 --- a/eth2/attestation_validation/src/signature.rs +++ /dev/null @@ -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( - attestation_data: &AttestationData, - participation_bitfield: &Bitfield, - aggregate_signature: &AggregateSignature, - attestation_indices: &[usize], - validator_store: &ValidatorStore, -) -> Result -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 { - let mut signed_message = attestation_data.canonical_root().to_vec(); - signed_message.append(&mut vec![0]); - signed_message -} - -impl From for Error { - fn from(error: ValidatorStoreError) -> Self { - match error { - ValidatorStoreError::DBError(s) => Error::DBError(s), - ValidatorStoreError::DecodeError => Error::PublicKeyCorrupt, - } - } -} - -impl From 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 = (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)); - } -}