diff --git a/lighthouse/main.rs b/lighthouse/main.rs index 257ef1b6b5..1631cc4e74 100644 --- a/lighthouse/main.rs +++ b/lighthouse/main.rs @@ -6,11 +6,12 @@ extern crate clap; extern crate network_libp2p; extern crate futures; +#[macro_use] +pub mod utils; pub mod db; pub mod client; pub mod state; pub mod sync; -pub mod utils; pub mod config; use std::path::PathBuf; diff --git a/lighthouse/state/helpers.rs b/lighthouse/state/helpers.rs new file mode 100644 index 0000000000..388fedafbf --- /dev/null +++ b/lighthouse/state/helpers.rs @@ -0,0 +1,71 @@ +/* + * Collection of helper functions used in the state transition modules + */ +use super::active_state::ActiveState; +use super::block::Block; +use super::chain_config::ChainConfig; +use super::utils::errors::ParameterError; +use super::utils::types::Hash256; + +/* + pub fn get_signed_parent_hashes( + active_state: &ActiveState, + block: &Block, + attestation: &AttestationRecord, + chain_config: &ChainConfig) + -> Vec { + } + */ + +pub fn get_block_hash( + active_state_recent_block_hashes: &Vec, + current_block_slot: &u64, + slot: &u64, + cycle_length: &u64, // convert from standard u8 +) -> Result { + // active_state must have at 2*cycle_length hashes + assert_error!( + active_state_recent_block_hashes.len() as u64 == cycle_length * 2, + ParameterError::InvalidInput(String::from( + "active state has incorrect number of block hashes" + )) + ); + + let state_start_slot = (*current_block_slot) + .checked_sub(cycle_length * 2) + .unwrap_or(0); + + assert_error!( + (state_start_slot <= *slot) && (*slot < *current_block_slot), + ParameterError::InvalidInput(String::from("incorrect slot number")) + ); + + let index = 2 * cycle_length + (*slot) - *current_block_slot; // should always be positive + Ok(active_state_recent_block_hashes[index as usize]) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_get_block_hash() { + let block_slot: u64 = 10; + let slot: u64 = 3; + let cycle_length: u64 = 8; + + let mut block_hashes: Vec = Vec::new(); + for _i in 0..2 * cycle_length { + block_hashes.push(Hash256::random()); + } + + let result = get_block_hash(&block_hashes, &block_slot, &slot, &cycle_length).unwrap(); + + assert_eq!( + result, + block_hashes[(2 * cycle_length + slot - block_slot) as usize] + ); + + println!("{:?}", result); + } +} diff --git a/lighthouse/state/mod.rs b/lighthouse/state/mod.rs index 8f8f748d87..fbb789490c 100644 --- a/lighthouse/state/mod.rs +++ b/lighthouse/state/mod.rs @@ -17,3 +17,5 @@ pub mod crosslink_record; pub mod shard_and_committee; pub mod transition; pub mod validator_record; +pub mod validation; +pub mod helpers; diff --git a/lighthouse/state/transition/attestation_parent_hashes.rs b/lighthouse/state/transition/attestation_parent_hashes.rs index 35866159fe..2ba4712d55 100644 --- a/lighthouse/state/transition/attestation_parent_hashes.rs +++ b/lighthouse/state/transition/attestation_parent_hashes.rs @@ -1,5 +1,5 @@ use super::Hash256; -use super::TransitionError; +use super::ParameterError; /// This function is used to select the hashes used in /// the signing of an AttestationRecord. @@ -18,22 +18,22 @@ pub fn attestation_parent_hashes( attestation_slot: u64, current_hashes: &[Hash256], oblique_hashes: &[Hash256]) - -> Result, TransitionError> + -> Result, ParameterError> { // This cast places a limit on cycle_length. If you change it, check math // for overflow. let cycle_length: u64 = u64::from(cycle_length); if current_hashes.len() as u64 != (cycle_length * 2) { - return Err(TransitionError::InvalidInput(String::from( + return Err(ParameterError::InvalidInput(String::from( "current_hashes.len() must equal cycle_length * 2"))); } if attestation_slot >= block_slot { - return Err(TransitionError::InvalidInput(String::from( + return Err(ParameterError::InvalidInput(String::from( "attestation_slot must be less than block_slot"))); } if oblique_hashes.len() as u64 > cycle_length { - return Err(TransitionError::InvalidInput(String::from( + return Err(ParameterError::InvalidInput(String::from( "oblique_hashes.len() must be <= cycle_length * 2"))); } @@ -44,7 +44,7 @@ pub fn attestation_parent_hashes( let attestation_distance = block_slot - attestation_slot; if attestation_distance > cycle_length { - return Err(TransitionError::InvalidInput(String::from( + return Err(ParameterError::InvalidInput(String::from( "attestation_slot must be withing one cycle of block_slot"))); } @@ -63,7 +63,7 @@ pub fn attestation_parent_hashes( */ let end = start.checked_add(cycle_length) .and_then(|x| x.checked_sub(oblique_hashes.len() as u64)) - .ok_or(TransitionError::IntWrapping)?; + .ok_or(ParameterError::IntWrapping)?; let mut hashes = Vec::new(); diff --git a/lighthouse/state/transition/mod.rs b/lighthouse/state/transition/mod.rs index 2d4592f303..de80e4961d 100644 --- a/lighthouse/state/transition/mod.rs +++ b/lighthouse/state/transition/mod.rs @@ -1,6 +1,7 @@ use super::block; use super::Logger; use super::utils::types::Hash256; +use super::utils::errors::ParameterError; use super::db; mod attestation_parent_hashes; @@ -10,12 +11,6 @@ mod validate_block; pub use self::attestation_parent_hashes::attestation_parent_hashes; pub use self::shuffling::shuffle; -#[derive(Debug)] -pub enum TransitionError { - IntWrapping, - OutOfBounds, - InvalidInput(String), -} diff --git a/lighthouse/state/validation/attestation_validation.rs b/lighthouse/state/validation/attestation_validation.rs new file mode 100644 index 0000000000..4704320638 --- /dev/null +++ b/lighthouse/state/validation/attestation_validation.rs @@ -0,0 +1,81 @@ +use super::CrystallizedState; +use super::ActiveState; +use super::AttestationRecord; +use super::Block; +use super::ChainConfig; + +use ::utils::errors::AttestationValidationError; + +// implementation of validate_attestation in the v2.1 python reference implementation +// see: https://github.com/ethereum/beacon_chain/blob/a79ab2c6f03cbdabf2b6d9d435c26e2b216e09a5/beacon_chain/state/state_transition.py#L61 +pub fn validate_attestation( + crystallized_state: &CrystallizedState, + active_state: &ActiveState, + attestation: &AttestationRecord, + block: &Block, + chain_config: &ChainConfig) + -> Result { + + if !(attestation.slot < block.slot_number) { + return Err(AttestationValidationError::SlotTooHigh); + } + + if !(attestation.slot > (block.slot_number - chain_config.cycle_length as u64)) { + return Err(AttestationValidationError::SlotTooLow(format!("Attestation slot number too low\n\tFound: {:?}, Needed greater than: {:?}", attestation.slot, block.slot_number - chain_config.cycle_length as u64))); + } + + Ok(true) + } + + +#[cfg(test)] +mod tests { + + use super::*; + // test helper functions + + fn generate_standard_state() -> ( + CrystallizedState, + ActiveState, + AttestationRecord, + Block, + ChainConfig) { + + let crystallized_state = CrystallizedState::zero(); + let active_state = ActiveState::zero(); + let attestation_record = AttestationRecord::zero(); + let block = Block::zero(); + let chain_config = ChainConfig::standard(); + + return (crystallized_state, active_state, attestation_record, block, chain_config); + } + + #[test] + fn test_attestation_validation_slot_high() { + // generate standard state + let (crystallized_state, active_state, mut attestation_record, mut block, chain_config) = generate_standard_state(); + // set slot too high + attestation_record.slot = 30; + block.slot_number = 10; + + let result = validate_attestation(&crystallized_state, &active_state, &attestation_record, &block, &chain_config); + assert_eq!(result, Err(AttestationValidationError::SlotTooHigh)); + } + + #[test] + fn test_attestation_validation_slot_low() { + // generate standard state + let (crystallized_state, active_state, mut attestation_record, mut block, chain_config) = generate_standard_state(); + // set slot too high + attestation_record.slot = 2; + block.slot_number = 10; + + let result = validate_attestation( + &crystallized_state, + &active_state, + &attestation_record, + &block, + &chain_config); + //assert_eq!(result, Err(AttestationValidationError::SlotTooLow)); + } +} diff --git a/lighthouse/state/validation/mod.rs b/lighthouse/state/validation/mod.rs new file mode 100644 index 0000000000..c11b652612 --- /dev/null +++ b/lighthouse/state/validation/mod.rs @@ -0,0 +1,7 @@ +use super::crystallized_state::CrystallizedState; +use super::active_state::ActiveState; +use super::attestation_record::AttestationRecord; +use super::block::Block; +use super::chain_config::ChainConfig; + +mod attestation_validation; diff --git a/lighthouse/utils/errors.rs b/lighthouse/utils/errors.rs new file mode 100644 index 0000000000..2a25b29b8e --- /dev/null +++ b/lighthouse/utils/errors.rs @@ -0,0 +1,17 @@ +// Collection of custom errors + +#[derive(Debug,PartialEq)] +pub enum AttestationValidationError { + SlotTooHigh, + SlotTooLow(String), + IncorrectBitField, + NonZeroTrailingBits, + AggregateSignatureFail +} + +#[derive(Debug,PartialEq)] +pub enum ParameterError { + IntWrapping, + OutOfBounds, + InvalidInput(String), +} diff --git a/lighthouse/utils/macros.rs b/lighthouse/utils/macros.rs new file mode 100644 index 0000000000..e6e3be01ec --- /dev/null +++ b/lighthouse/utils/macros.rs @@ -0,0 +1,10 @@ +#[macro_export] +macro_rules! assert_error { + ($exp: expr, $err: expr) => { + if ( !$exp ) { + return Err($err); + } + } +} + + diff --git a/lighthouse/utils/mod.rs b/lighthouse/utils/mod.rs index cfb302d4b4..4afe15c454 100644 --- a/lighthouse/utils/mod.rs +++ b/lighthouse/utils/mod.rs @@ -3,8 +3,13 @@ extern crate blake2_rfc as blake2; extern crate crypto_mac; extern crate boolean_bitfield; +#[macro_use] +pub mod macros; pub mod hash; pub mod types; pub mod bls; pub mod test_helpers; pub mod logging; +pub mod errors; + +