diff --git a/beacon_chain/attestation_validation/src/validate_for_block.rs b/beacon_chain/attestation_validation/src/block_inclusion.rs similarity index 99% rename from beacon_chain/attestation_validation/src/validate_for_block.rs rename to beacon_chain/attestation_validation/src/block_inclusion.rs index 7742b5325..76a5c9797 100644 --- a/beacon_chain/attestation_validation/src/validate_for_block.rs +++ b/beacon_chain/attestation_validation/src/block_inclusion.rs @@ -37,6 +37,7 @@ pub fn validate_attestation_for_block( #[cfg(test)] mod tests { use super::*; + /* * Invalid::AttestationTooOld tests. */ diff --git a/beacon_chain/attestation_validation/src/validate_for_state.rs b/beacon_chain/attestation_validation/src/justified_block.rs similarity index 50% rename from beacon_chain/attestation_validation/src/validate_for_state.rs rename to beacon_chain/attestation_validation/src/justified_block.rs index 9312dd125..e910819c8 100644 --- a/beacon_chain/attestation_validation/src/validate_for_state.rs +++ b/beacon_chain/attestation_validation/src/justified_block.rs @@ -1,42 +1,25 @@ use super::db::stores::{BeaconBlockAtSlotError, BeaconBlockStore}; use super::db::ClientDB; +use super::types::AttestationData; use super::types::Hash256; -use super::types::{AttestationData, BeaconState}; use super::{Error, Invalid, Outcome}; use std::sync::Arc; -/// Check that an attestation is valid with reference to some state. -pub fn validate_attestation_data_for_state( +/// 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, - state: &BeaconState, block_store: &Arc>, ) -> Result where T: ClientDB + Sized, { - /* - * The attestation's `justified_slot` must be the same as the last justified slot known to this - * client. - * - * In the case that an attestation references a slot _before_ the latest state transition, it - * 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. - */ - { - let permissable_justified_slot = if data.slot >= state.latest_state_recalculation_slot { - state.justified_slot - } else { - state.previous_justified_slot - }; - verify_or!( - data.justified_slot == permissable_justified_slot, - reject!(Invalid::JustifiedSlotImpermissable) - ); - } - /* * The `justified_block_hash` in the attestation must match exactly the hash of the block at * that slot in the local chain. @@ -53,28 +36,6 @@ where ); } }; - - /* - * 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_hash; - 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!() } @@ -105,3 +66,15 @@ impl From for Error { } } } + +#[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/beacon_chain/attestation_validation/src/justified_slot.rs b/beacon_chain/attestation_validation/src/justified_slot.rs new file mode 100644 index 000000000..c27dfe8f2 --- /dev/null +++ b/beacon_chain/attestation_validation/src/justified_slot.rs @@ -0,0 +1,38 @@ +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, +) -> Result { + let permissable_justified_slot = if data.slot >= state.latest_state_recalculation_slot { + 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/beacon_chain/attestation_validation/src/lib.rs b/beacon_chain/attestation_validation/src/lib.rs index 8a54a3194..8544be862 100644 --- a/beacon_chain/attestation_validation/src/lib.rs +++ b/beacon_chain/attestation_validation/src/lib.rs @@ -7,12 +7,17 @@ extern crate types; #[macro_use] mod macros; + +mod block_inclusion; mod enums; -mod validate_for_block; -mod validate_for_state; -mod validate_signature; +mod justified_block; +mod justified_slot; +mod shard_block; +mod signature; pub use enums::{Invalid, Outcome, Error}; -pub use validate_for_block::validate_attestation_for_block; -pub use validate_for_state::validate_attestation_data_for_state; -pub use validate_signature::validate_attestation_signature; +pub use block_inclusion::validate_attestation_for_block; +pub use justified_slot::validate_attestation_justified_slot; +pub use justified_block::validate_attestation_justified_block_hash; +pub use signature::validate_attestation_signature; +pub use shard_block::validate_attestation_data_shard_block_hash; diff --git a/beacon_chain/attestation_validation/src/shard_block.rs b/beacon_chain/attestation_validation/src/shard_block.rs new file mode 100644 index 000000000..7fb48ff62 --- /dev/null +++ b/beacon_chain/attestation_validation/src/shard_block.rs @@ -0,0 +1,46 @@ +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_hash; + 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/beacon_chain/attestation_validation/src/validate_signature.rs b/beacon_chain/attestation_validation/src/signature.rs similarity index 100% rename from beacon_chain/attestation_validation/src/validate_signature.rs rename to beacon_chain/attestation_validation/src/signature.rs diff --git a/beacon_chain/types/src/attestation_data.rs b/beacon_chain/types/src/attestation_data.rs index 43c1b8350..d75c43b9d 100644 --- a/beacon_chain/types/src/attestation_data.rs +++ b/beacon_chain/types/src/attestation_data.rs @@ -12,7 +12,7 @@ pub const SSZ_ATTESTION_DATA_LENGTH: usize = { 32 // justified_block_hash }; -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Default)] pub struct AttestationData { pub slot: u64, pub shard: u64, diff --git a/beacon_chain/types/src/beacon_block.rs b/beacon_chain/types/src/beacon_block.rs index e4217fd9a..6d67c907b 100644 --- a/beacon_chain/types/src/beacon_block.rs +++ b/beacon_chain/types/src/beacon_block.rs @@ -15,7 +15,7 @@ pub const MIN_SSZ_BLOCK_LENGTH: usize = { }; pub const MAX_SSZ_BLOCK_LENGTH: usize = MIN_SSZ_BLOCK_LENGTH + (1 << 24); -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Default)] pub struct BeaconBlock { pub slot: u64, pub randao_reveal: Hash256, diff --git a/beacon_chain/types/src/beacon_block_body.rs b/beacon_chain/types/src/beacon_block_body.rs new file mode 100644 index 000000000..e69de29bb