From 7a03ee79aaab7907429f9eb962a1236995759606 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Thu, 14 Feb 2019 16:53:53 +1100 Subject: [PATCH 1/9] Update SlowLMDGhost to use SlotHeight. --- eth2/fork_choice/src/optimised_lmd_ghost.rs | 16 +++++++--------- eth2/fork_choice/src/slow_lmd_ghost.rs | 4 +--- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/eth2/fork_choice/src/optimised_lmd_ghost.rs b/eth2/fork_choice/src/optimised_lmd_ghost.rs index 7104834cb2..40dc029be0 100644 --- a/eth2/fork_choice/src/optimised_lmd_ghost.rs +++ b/eth2/fork_choice/src/optimised_lmd_ghost.rs @@ -30,10 +30,8 @@ use fast_math::log2_raw; use std::collections::HashMap; use std::sync::Arc; use types::{ - readers::BeaconBlockReader, - slot_epoch_height::{Height, Slot}, - validator_registry::get_active_validator_indices, - BeaconBlock, Hash256, + readers::BeaconBlockReader, slot_epoch::Slot, slot_height::SlotHeight, + validator_registry::get_active_validator_indices, BeaconBlock, Hash256, }; //TODO: Pruning - Children @@ -77,7 +75,7 @@ pub struct OptimisedLMDGhost { block_store: Arc>, /// State storage access. state_store: Arc>, - max_known_height: Height, + max_known_height: SlotHeight, } impl OptimisedLMDGhost @@ -93,7 +91,7 @@ where ancestors: vec![HashMap::new(); 16], latest_attestation_targets: HashMap::new(), children: HashMap::new(), - max_known_height: Height::new(0), + max_known_height: SlotHeight::new(0), block_store, state_store, } @@ -118,7 +116,7 @@ where .ok_or_else(|| ForkChoiceError::MissingBeaconState(*state_root))?; let active_validator_indices = get_active_validator_indices( - ¤t_state.validator_registry, + ¤t_state.validator_registry[..], block_slot.epoch(EPOCH_LENGTH), ); @@ -137,7 +135,7 @@ where } /// Gets the ancestor at a given height `at_height` of a block specified by `block_hash`. - fn get_ancestor(&mut self, block_hash: Hash256, at_height: Height) -> Option { + fn get_ancestor(&mut self, block_hash: Hash256, at_height: SlotHeight) -> Option { // return None if we can't get the block from the db. let block_height = { let block_slot = self @@ -186,7 +184,7 @@ where fn get_clear_winner( &mut self, latest_votes: &HashMap, - block_height: Height, + block_height: SlotHeight, ) -> Option { // map of vote counts for every hash at this height let mut current_votes: HashMap = HashMap::new(); diff --git a/eth2/fork_choice/src/slow_lmd_ghost.rs b/eth2/fork_choice/src/slow_lmd_ghost.rs index e0e347cefe..609d28ab2a 100644 --- a/eth2/fork_choice/src/slow_lmd_ghost.rs +++ b/eth2/fork_choice/src/slow_lmd_ghost.rs @@ -28,9 +28,7 @@ use db::{ use std::collections::HashMap; use std::sync::Arc; use types::{ - readers::{BeaconBlockReader, BeaconStateReader}, - slot_epoch_height::Slot, - validator_registry::get_active_validator_indices, + readers::BeaconBlockReader, slot_epoch::Slot, validator_registry::get_active_validator_indices, BeaconBlock, Hash256, }; From 7b39dad232f97b2d69f0189602cf4d30c948373d Mon Sep 17 00:00:00 2001 From: Age Manning Date: Thu, 14 Feb 2019 16:56:12 +1100 Subject: [PATCH 2/9] Removes protolambda as an option of fork-choice. --- eth2/fork_choice/src/lib.rs | 7 ++++--- eth2/fork_choice/src/protolambda_lmd_ghost.rs | 0 2 files changed, 4 insertions(+), 3 deletions(-) delete mode 100644 eth2/fork_choice/src/protolambda_lmd_ghost.rs diff --git a/eth2/fork_choice/src/lib.rs b/eth2/fork_choice/src/lib.rs index f79f7e8c14..820a33ec1d 100644 --- a/eth2/fork_choice/src/lib.rs +++ b/eth2/fork_choice/src/lib.rs @@ -44,7 +44,7 @@ extern crate types; pub mod longest_chain; pub mod optimised_lmd_ghost; -pub mod protolambda_lmd_ghost; +//pub mod protolambda_lmd_ghost; pub mod slow_lmd_ghost; use db::stores::BeaconBlockAtSlotError; @@ -113,6 +113,7 @@ pub enum ForkChoiceAlgorithms { SlowLMDGhost, /// An optimised version of LMD-GHOST by Vitalik. OptimisedLMDGhost, - /// An optimised version of LMD-GHOST by Protolambda. - ProtoLMDGhost, + // Protolambda currently not implemented + // /// An optimised version of LMD-GHOST by Protolambda. + //ProtoLMDGhost, } diff --git a/eth2/fork_choice/src/protolambda_lmd_ghost.rs b/eth2/fork_choice/src/protolambda_lmd_ghost.rs deleted file mode 100644 index e69de29bb2..0000000000 From fe13d98469d9abff5479fd66ac06cb8862dd37bd Mon Sep 17 00:00:00 2001 From: Age Manning Date: Thu, 14 Feb 2019 18:09:09 +1100 Subject: [PATCH 3/9] Updates longest chain to match new fork-choice structure. --- eth2/fork_choice/src/lib.rs | 1 + eth2/fork_choice/src/longest_chain.rs | 161 ++++++++++++++------------ 2 files changed, 86 insertions(+), 76 deletions(-) diff --git a/eth2/fork_choice/src/lib.rs b/eth2/fork_choice/src/lib.rs index 820a33ec1d..a6e4ea422e 100644 --- a/eth2/fork_choice/src/lib.rs +++ b/eth2/fork_choice/src/lib.rs @@ -83,6 +83,7 @@ pub enum ForkChoiceError { CannotFindBestChild, ChildrenNotFound, StorageError(String), + HeadNotFound, } impl From for ForkChoiceError { diff --git a/eth2/fork_choice/src/longest_chain.rs b/eth2/fork_choice/src/longest_chain.rs index 277d6b9509..c25cd43a17 100644 --- a/eth2/fork_choice/src/longest_chain.rs +++ b/eth2/fork_choice/src/longest_chain.rs @@ -1,93 +1,102 @@ -use db::stores::BeaconBlockStore; -use db::{ClientDB, DBError}; -use ssz::{Decodable, DecodeError}; +use crate::{ForkChoice, ForkChoiceError}; +use db::{stores::BeaconBlockStore, ClientDB}; use std::sync::Arc; use types::{BeaconBlock, Hash256, Slot}; -pub enum ForkChoiceError { - BadSszInDatabase, - MissingBlock, - DBError(String), -} - -pub fn longest_chain( - head_block_hashes: &[Hash256], - block_store: &Arc>, -) -> Result, ForkChoiceError> +pub struct LongestChain where T: ClientDB + Sized, { - let mut head_blocks: Vec<(usize, BeaconBlock)> = vec![]; + /// List of head block hashes + head_block_hashes: Vec, + /// Block storage access. + block_store: Arc>, +} - /* - * Load all the head_block hashes from the DB as SszBeaconBlocks. - */ - for (index, block_hash) in head_block_hashes.iter().enumerate() { - let ssz = block_store - .get(&block_hash)? - .ok_or(ForkChoiceError::MissingBlock)?; - let (block, _) = BeaconBlock::ssz_decode(&ssz, 0)?; - head_blocks.push((index, block)); - } - - /* - * Loop through all the head blocks and find the highest slot. - */ - let highest_slot: Option = None; - for (_, block) in &head_blocks { - let slot = block.slot; - - match highest_slot { - None => Some(slot), - Some(winning_slot) => { - if slot > winning_slot { - Some(slot) - } else { - Some(winning_slot) - } - } - }; - } - - /* - * Loop through all the highest blocks and sort them by highest hash. - * - * Ultimately, the index of the head_block hash with the highest slot and highest block - * hash will be the winner. - */ - match highest_slot { - None => Ok(None), - Some(highest_slot) => { - let mut highest_blocks = vec![]; - for (index, block) in head_blocks { - if block.slot == highest_slot { - highest_blocks.push((index, block)) - } - } - - highest_blocks.sort_by(|a, b| head_block_hashes[a.0].cmp(&head_block_hashes[b.0])); - let (index, _) = highest_blocks[0]; - Ok(Some(index)) +impl LongestChain +where + T: ClientDB + Sized, +{ + pub fn new(block_store: Arc>) -> Self { + LongestChain { + head_block_hashes: Vec::new(), + block_store, } } } -impl From for ForkChoiceError { - fn from(_: DecodeError) -> Self { - ForkChoiceError::BadSszInDatabase +impl ForkChoice for LongestChain { + fn add_block( + &mut self, + block: &BeaconBlock, + block_hash: &Hash256, + ) -> Result<(), ForkChoiceError> { + // add the block hash to head_block_hashes removing the parent if it exists + self.head_block_hashes + .retain(|hash| *hash != block.parent_root); + self.head_block_hashes.push(*block_hash); + Ok(()) } -} -impl From for ForkChoiceError { - fn from(e: DBError) -> Self { - ForkChoiceError::DBError(e.message) + fn add_attestation(&mut self, _: u64, _: &Hash256) -> Result<(), ForkChoiceError> { + // do nothing + Ok(()) } -} -#[cfg(test)] -mod tests { - #[test] - fn test_naive_fork_choice() { - assert_eq!(2 + 2, 4); + fn find_head(&mut self, _: &Hash256) -> Result { + let mut head_blocks: Vec<(usize, BeaconBlock)> = vec![]; + /* + * Load all the head_block hashes from the DB as SszBeaconBlocks. + */ + for (index, block_hash) in self.head_block_hashes.iter().enumerate() { + let block = self + .block_store + .get_deserialized(&block_hash)? + .ok_or(ForkChoiceError::MissingBeaconBlock(*block_hash))?; + head_blocks.push((index, block)); + } + + /* + * Loop through all the head blocks and find the highest slot. + */ + let highest_slot = head_blocks + .iter() + .fold(Slot::from(0u64), |highest, (_, block)| { + std::cmp::max(block.slot, highest) + }); + + // if we find no blocks, return Error + if highest_slot == 0 { + return Err(ForkChoiceError::HeadNotFound); + } + + /* + * Loop through all the highest blocks and sort them by highest hash. + * + * Ultimately, the index of the head_block hash with the highest slot and highest block + * hash will be the winner. + */ + + let head_index: Option = + head_blocks + .iter() + .fold(None, |smallest_index, (index, block)| { + if block.slot == highest_slot { + if smallest_index.is_none() { + return Some(*index); + } + return Some(std::cmp::min( + *index, + smallest_index.expect("Cannot be None"), + )); + } + smallest_index + }); + + if head_index.is_none() { + return Err(ForkChoiceError::HeadNotFound); + } + + Ok(self.head_block_hashes[head_index.unwrap()]) } } From 91ba26a351c623014d2c0c34c493c15b14ffdc5b Mon Sep 17 00:00:00 2001 From: Age Manning Date: Fri, 15 Feb 2019 00:21:26 +1100 Subject: [PATCH 4/9] Add test dependencies for fork-choice. --- eth2/fork_choice/Cargo.toml | 4 ++++ eth2/fork_choice/src/lib.rs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/eth2/fork_choice/Cargo.toml b/eth2/fork_choice/Cargo.toml index 566334c761..82d5785b96 100644 --- a/eth2/fork_choice/Cargo.toml +++ b/eth2/fork_choice/Cargo.toml @@ -10,3 +10,7 @@ ssz = { path = "../utils/ssz" } types = { path = "../types" } fast-math = "0.1.1" byteorder = "1.3.1" + +[dev-dependencies] +yaml-rust = "0.4.2" +bls = { path = "../utils/bls" } diff --git a/eth2/fork_choice/src/lib.rs b/eth2/fork_choice/src/lib.rs index a6e4ea422e..e876ed60fc 100644 --- a/eth2/fork_choice/src/lib.rs +++ b/eth2/fork_choice/src/lib.rs @@ -51,6 +51,10 @@ use db::stores::BeaconBlockAtSlotError; use db::DBError; use types::{BeaconBlock, Hash256}; +// export the main structs +pub use longest_chain::LongestChain; +pub use optimised_lmd_ghost::OptimisedLMDGhost; + /// Defines the interface for Fork Choices. Each Fork choice will define their own data structures /// which can be built in block processing through the `add_block` and `add_attestation` functions. /// The main fork choice algorithm is specified in `find_head From 2cab2952a8d8ba38a35e82c6ae830f46ba2eb2c6 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Fri, 15 Feb 2019 13:29:09 +1100 Subject: [PATCH 5/9] Removes duplication of macros in types. --- eth2/fork_choice/Cargo.toml | 2 + eth2/types/src/lib.rs | 2 + eth2/types/src/slot_epoch.rs | 249 ------------------------ eth2/types/src/slot_epoch_macros.rs | 265 ++++++++++++++++++++++++++ eth2/types/src/slot_height.rs | 281 +--------------------------- 5 files changed, 271 insertions(+), 528 deletions(-) create mode 100644 eth2/types/src/slot_epoch_macros.rs diff --git a/eth2/fork_choice/Cargo.toml b/eth2/fork_choice/Cargo.toml index 82d5785b96..72a653032e 100644 --- a/eth2/fork_choice/Cargo.toml +++ b/eth2/fork_choice/Cargo.toml @@ -14,3 +14,5 @@ byteorder = "1.3.1" [dev-dependencies] yaml-rust = "0.4.2" bls = { path = "../utils/bls" } +slot_clock = { path = "../utils/slot_clock" } +beacon_chain = { path = "../../beacon_node/beacon_chain" } diff --git a/eth2/types/src/lib.rs b/eth2/types/src/lib.rs index 5895934827..233d1cc3ed 100644 --- a/eth2/types/src/lib.rs +++ b/eth2/types/src/lib.rs @@ -24,6 +24,8 @@ pub mod readers; pub mod shard_reassignment_record; pub mod slashable_attestation; pub mod slashable_vote_data; +#[macro_use] +pub mod slot_epoch_macros; pub mod slot_epoch; pub mod slot_height; pub mod spec; diff --git a/eth2/types/src/slot_epoch.rs b/eth2/types/src/slot_epoch.rs index b7a8e8f8dd..c1554034ad 100644 --- a/eth2/types/src/slot_epoch.rs +++ b/eth2/types/src/slot_epoch.rs @@ -21,255 +21,6 @@ use std::hash::{Hash, Hasher}; use std::iter::Iterator; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign}; -macro_rules! impl_from_into_u64 { - ($main: ident) => { - impl From for $main { - fn from(n: u64) -> $main { - $main(n) - } - } - - impl Into for $main { - fn into(self) -> u64 { - self.0 - } - } - - impl $main { - pub fn as_u64(&self) -> u64 { - self.0 - } - } - }; -} - -macro_rules! impl_from_into_usize { - ($main: ident) => { - impl From for $main { - fn from(n: usize) -> $main { - $main(n as u64) - } - } - - impl Into for $main { - fn into(self) -> usize { - self.0 as usize - } - } - - impl $main { - pub fn as_usize(&self) -> usize { - self.0 as usize - } - } - }; -} - -macro_rules! impl_math_between { - ($main: ident, $other: ident) => { - impl PartialOrd<$other> for $main { - /// Utilizes `partial_cmp` on the underlying `u64`. - fn partial_cmp(&self, other: &$other) -> Option { - Some(self.0.cmp(&(*other).into())) - } - } - - impl PartialEq<$other> for $main { - fn eq(&self, other: &$other) -> bool { - let other: u64 = (*other).into(); - self.0 == other - } - } - - impl Add<$other> for $main { - type Output = $main; - - fn add(self, other: $other) -> $main { - $main::from(self.0.saturating_add(other.into())) - } - } - - impl AddAssign<$other> for $main { - fn add_assign(&mut self, other: $other) { - self.0 = self.0.saturating_add(other.into()); - } - } - - impl Sub<$other> for $main { - type Output = $main; - - fn sub(self, other: $other) -> $main { - $main::from(self.0.saturating_sub(other.into())) - } - } - - impl SubAssign<$other> for $main { - fn sub_assign(&mut self, other: $other) { - self.0 = self.0.saturating_sub(other.into()); - } - } - - impl Mul<$other> for $main { - type Output = $main; - - fn mul(self, rhs: $other) -> $main { - let rhs: u64 = rhs.into(); - $main::from(self.0.saturating_mul(rhs)) - } - } - - impl MulAssign<$other> for $main { - fn mul_assign(&mut self, rhs: $other) { - let rhs: u64 = rhs.into(); - self.0 = self.0.saturating_mul(rhs) - } - } - - impl Div<$other> for $main { - type Output = $main; - - fn div(self, rhs: $other) -> $main { - let rhs: u64 = rhs.into(); - if rhs == 0 { - panic!("Cannot divide by zero-valued Slot/Epoch") - } - $main::from(self.0 / rhs) - } - } - - impl DivAssign<$other> for $main { - fn div_assign(&mut self, rhs: $other) { - let rhs: u64 = rhs.into(); - if rhs == 0 { - panic!("Cannot divide by zero-valued Slot/Epoch") - } - self.0 = self.0 / rhs - } - } - - impl Rem<$other> for $main { - type Output = $main; - - fn rem(self, modulus: $other) -> $main { - let modulus: u64 = modulus.into(); - $main::from(self.0 % modulus) - } - } - }; -} - -macro_rules! impl_math { - ($type: ident) => { - impl $type { - pub fn saturating_sub>(&self, other: T) -> $type { - *self - other.into() - } - - pub fn saturating_add>(&self, other: T) -> $type { - *self + other.into() - } - - pub fn checked_div>(&self, rhs: T) -> Option<$type> { - let rhs: $type = rhs.into(); - if rhs == 0 { - None - } else { - Some(*self / rhs) - } - } - - pub fn is_power_of_two(&self) -> bool { - self.0.is_power_of_two() - } - } - - impl Ord for $type { - fn cmp(&self, other: &$type) -> Ordering { - let other: u64 = (*other).into(); - self.0.cmp(&other) - } - } - }; -} - -macro_rules! impl_display { - ($type: ident) => { - impl fmt::Display for $type { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } - } - - impl slog::Value for $type { - fn serialize( - &self, - record: &slog::Record, - key: slog::Key, - serializer: &mut slog::Serializer, - ) -> slog::Result { - self.0.serialize(record, key, serializer) - } - } - }; -} - -macro_rules! impl_ssz { - ($type: ident) => { - impl Encodable for $type { - fn ssz_append(&self, s: &mut SszStream) { - s.append(&self.0); - } - } - - impl Decodable for $type { - fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { - let (value, i) = <_>::ssz_decode(bytes, i)?; - - Ok(($type(value), i)) - } - } - - impl TreeHash for $type { - fn hash_tree_root(&self) -> Vec { - let mut result: Vec = vec![]; - result.append(&mut self.0.hash_tree_root()); - hash(&result) - } - } - - impl TestRandom for $type { - fn random_for_test(rng: &mut T) -> Self { - $type::from(u64::random_for_test(rng)) - } - } - }; -} - -macro_rules! impl_hash { - ($type: ident) => { - // Implemented to stop clippy lint: - // https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq - impl Hash for $type { - fn hash(&self, state: &mut H) { - ssz_encode(self).hash(state) - } - } - }; -} - -macro_rules! impl_common { - ($type: ident) => { - impl_from_into_u64!($type); - impl_from_into_usize!($type); - impl_math_between!($type, $type); - impl_math_between!($type, u64); - impl_math!($type); - impl_display!($type); - impl_ssz!($type); - impl_hash!($type); - }; -} - #[derive(Eq, Debug, Clone, Copy, Default, Serialize)] pub struct Slot(u64); diff --git a/eth2/types/src/slot_epoch_macros.rs b/eth2/types/src/slot_epoch_macros.rs new file mode 100644 index 0000000000..6813bfeaf7 --- /dev/null +++ b/eth2/types/src/slot_epoch_macros.rs @@ -0,0 +1,265 @@ +macro_rules! impl_from_into_u64 { + ($main: ident) => { + impl From for $main { + fn from(n: u64) -> $main { + $main(n) + } + } + + impl Into for $main { + fn into(self) -> u64 { + self.0 + } + } + + impl $main { + pub fn as_u64(&self) -> u64 { + self.0 + } + } + }; +} + +// need to truncate for some fork-choice algorithms +macro_rules! impl_into_u32 { + ($main: ident) => { + impl Into for $main { + fn into(self) -> u32 { + self.0 as u32 + } + } + + impl $main { + pub fn as_u32(&self) -> u32 { + self.0 as u32 + } + } + }; +} + +macro_rules! impl_from_into_usize { + ($main: ident) => { + impl From for $main { + fn from(n: usize) -> $main { + $main(n as u64) + } + } + + impl Into for $main { + fn into(self) -> usize { + self.0 as usize + } + } + + impl $main { + pub fn as_usize(&self) -> usize { + self.0 as usize + } + } + }; +} + +macro_rules! impl_math_between { + ($main: ident, $other: ident) => { + impl PartialOrd<$other> for $main { + /// Utilizes `partial_cmp` on the underlying `u64`. + fn partial_cmp(&self, other: &$other) -> Option { + Some(self.0.cmp(&(*other).into())) + } + } + + impl PartialEq<$other> for $main { + fn eq(&self, other: &$other) -> bool { + let other: u64 = (*other).into(); + self.0 == other + } + } + + impl Add<$other> for $main { + type Output = $main; + + fn add(self, other: $other) -> $main { + $main::from(self.0.saturating_add(other.into())) + } + } + + impl AddAssign<$other> for $main { + fn add_assign(&mut self, other: $other) { + self.0 = self.0.saturating_add(other.into()); + } + } + + impl Sub<$other> for $main { + type Output = $main; + + fn sub(self, other: $other) -> $main { + $main::from(self.0.saturating_sub(other.into())) + } + } + + impl SubAssign<$other> for $main { + fn sub_assign(&mut self, other: $other) { + self.0 = self.0.saturating_sub(other.into()); + } + } + + impl Mul<$other> for $main { + type Output = $main; + + fn mul(self, rhs: $other) -> $main { + let rhs: u64 = rhs.into(); + $main::from(self.0.saturating_mul(rhs)) + } + } + + impl MulAssign<$other> for $main { + fn mul_assign(&mut self, rhs: $other) { + let rhs: u64 = rhs.into(); + self.0 = self.0.saturating_mul(rhs) + } + } + + impl Div<$other> for $main { + type Output = $main; + + fn div(self, rhs: $other) -> $main { + let rhs: u64 = rhs.into(); + if rhs == 0 { + panic!("Cannot divide by zero-valued Slot/Epoch") + } + $main::from(self.0 / rhs) + } + } + + impl DivAssign<$other> for $main { + fn div_assign(&mut self, rhs: $other) { + let rhs: u64 = rhs.into(); + if rhs == 0 { + panic!("Cannot divide by zero-valued Slot/Epoch") + } + self.0 = self.0 / rhs + } + } + + impl Rem<$other> for $main { + type Output = $main; + + fn rem(self, modulus: $other) -> $main { + let modulus: u64 = modulus.into(); + $main::from(self.0 % modulus) + } + } + }; +} + +macro_rules! impl_math { + ($type: ident) => { + impl $type { + pub fn saturating_sub>(&self, other: T) -> $type { + *self - other.into() + } + + pub fn saturating_add>(&self, other: T) -> $type { + *self + other.into() + } + + pub fn checked_div>(&self, rhs: T) -> Option<$type> { + let rhs: $type = rhs.into(); + if rhs == 0 { + None + } else { + Some(*self / rhs) + } + } + + pub fn is_power_of_two(&self) -> bool { + self.0.is_power_of_two() + } + } + + impl Ord for $type { + fn cmp(&self, other: &$type) -> Ordering { + let other: u64 = (*other).into(); + self.0.cmp(&other) + } + } + }; +} + +macro_rules! impl_display { + ($type: ident) => { + impl fmt::Display for $type { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } + } + + impl slog::Value for $type { + fn serialize( + &self, + record: &slog::Record, + key: slog::Key, + serializer: &mut slog::Serializer, + ) -> slog::Result { + self.0.serialize(record, key, serializer) + } + } + }; +} + +macro_rules! impl_ssz { + ($type: ident) => { + impl Encodable for $type { + fn ssz_append(&self, s: &mut SszStream) { + s.append(&self.0); + } + } + + impl Decodable for $type { + fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { + let (value, i) = <_>::ssz_decode(bytes, i)?; + + Ok(($type(value), i)) + } + } + + impl TreeHash for $type { + fn hash_tree_root(&self) -> Vec { + let mut result: Vec = vec![]; + result.append(&mut self.0.hash_tree_root()); + hash(&result) + } + } + + impl TestRandom for $type { + fn random_for_test(rng: &mut T) -> Self { + $type::from(u64::random_for_test(rng)) + } + } + }; +} + +macro_rules! impl_hash { + ($type: ident) => { + // Implemented to stop clippy lint: + // https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq + impl Hash for $type { + fn hash(&self, state: &mut H) { + ssz_encode(self).hash(state) + } + } + }; +} + +macro_rules! impl_common { + ($type: ident) => { + impl_from_into_u64!($type); + impl_from_into_usize!($type); + impl_math_between!($type, $type); + impl_math_between!($type, u64); + impl_math!($type); + impl_display!($type); + impl_ssz!($type); + impl_hash!($type); + }; +} diff --git a/eth2/types/src/slot_height.rs b/eth2/types/src/slot_height.rs index 77dd17ad96..3efd00cae8 100644 --- a/eth2/types/src/slot_height.rs +++ b/eth2/types/src/slot_height.rs @@ -1,290 +1,13 @@ -// Copyright 2019 Sigma Prime Pty Ltd. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - use crate::slot_epoch::{Epoch, Slot}; +use crate::test_utils::TestRandom; +use rand::RngCore; use serde_derive::Serialize; -use slog; use ssz::{hash, ssz_encode, Decodable, DecodeError, Encodable, SszStream, TreeHash}; use std::cmp::{Ord, Ordering}; use std::fmt; use std::hash::{Hash, Hasher}; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign}; -macro_rules! impl_from_into_u64 { - ($main: ident) => { - impl From for $main { - fn from(n: u64) -> $main { - $main(n) - } - } - - impl Into for $main { - fn into(self) -> u64 { - self.0 - } - } - - impl $main { - pub fn as_u64(&self) -> u64 { - self.0 - } - } - }; -} - -// need to truncate for some fork-choice algorithms -macro_rules! impl_into_u32 { - ($main: ident) => { - impl Into for $main { - fn into(self) -> u32 { - self.0 as u32 - } - } - - impl $main { - pub fn as_u32(&self) -> u32 { - self.0 as u32 - } - } - }; -} -macro_rules! impl_from_into_usize { - ($main: ident) => { - impl From for $main { - fn from(n: usize) -> $main { - $main(n as u64) - } - } - - impl Into for $main { - fn into(self) -> usize { - self.0 as usize - } - } - - impl $main { - pub fn as_usize(&self) -> usize { - self.0 as usize - } - } - }; -} - -macro_rules! impl_math_between { - ($main: ident, $other: ident) => { - impl PartialOrd<$other> for $main { - /// Utilizes `partial_cmp` on the underlying `u64`. - fn partial_cmp(&self, other: &$other) -> Option { - Some(self.0.cmp(&(*other).into())) - } - } - - impl PartialEq<$other> for $main { - fn eq(&self, other: &$other) -> bool { - let other: u64 = (*other).into(); - self.0 == other - } - } - - impl Add<$other> for $main { - type Output = $main; - - fn add(self, other: $other) -> $main { - $main::from(self.0.saturating_add(other.into())) - } - } - - impl AddAssign<$other> for $main { - fn add_assign(&mut self, other: $other) { - self.0 = self.0.saturating_add(other.into()); - } - } - - impl Sub<$other> for $main { - type Output = $main; - - fn sub(self, other: $other) -> $main { - $main::from(self.0.saturating_sub(other.into())) - } - } - - impl SubAssign<$other> for $main { - fn sub_assign(&mut self, other: $other) { - self.0 = self.0.saturating_sub(other.into()); - } - } - - impl Mul<$other> for $main { - type Output = $main; - - fn mul(self, rhs: $other) -> $main { - let rhs: u64 = rhs.into(); - $main::from(self.0.saturating_mul(rhs)) - } - } - - impl MulAssign<$other> for $main { - fn mul_assign(&mut self, rhs: $other) { - let rhs: u64 = rhs.into(); - self.0 = self.0.saturating_mul(rhs) - } - } - - impl Div<$other> for $main { - type Output = $main; - - fn div(self, rhs: $other) -> $main { - let rhs: u64 = rhs.into(); - if rhs == 0 { - panic!("Cannot divide by zero-valued Slot/Epoch") - } - $main::from(self.0 / rhs) - } - } - - impl DivAssign<$other> for $main { - fn div_assign(&mut self, rhs: $other) { - let rhs: u64 = rhs.into(); - if rhs == 0 { - panic!("Cannot divide by zero-valued Slot/Epoch") - } - self.0 = self.0 / rhs - } - } - - impl Rem<$other> for $main { - type Output = $main; - - fn rem(self, modulus: $other) -> $main { - let modulus: u64 = modulus.into(); - $main::from(self.0 % modulus) - } - } - }; -} - -macro_rules! impl_math { - ($type: ident) => { - impl $type { - pub fn saturating_sub>(&self, other: T) -> $type { - *self - other.into() - } - - pub fn saturating_add>(&self, other: T) -> $type { - *self + other.into() - } - - pub fn checked_div>(&self, rhs: T) -> Option<$type> { - let rhs: $type = rhs.into(); - if rhs == 0 { - None - } else { - Some(*self / rhs) - } - } - - pub fn is_power_of_two(&self) -> bool { - self.0.is_power_of_two() - } - } - - impl Ord for $type { - fn cmp(&self, other: &$type) -> Ordering { - let other: u64 = (*other).into(); - self.0.cmp(&other) - } - } - }; -} - -macro_rules! impl_display { - ($type: ident) => { - impl fmt::Display for $type { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } - } - - impl slog::Value for $type { - fn serialize( - &self, - record: &slog::Record, - key: slog::Key, - serializer: &mut slog::Serializer, - ) -> slog::Result { - self.0.serialize(record, key, serializer) - } - } - }; -} - -macro_rules! impl_ssz { - ($type: ident) => { - impl Encodable for $type { - fn ssz_append(&self, s: &mut SszStream) { - s.append(&self.0); - } - } - - impl Decodable for $type { - fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { - let (value, i) = <_>::ssz_decode(bytes, i)?; - - Ok(($type(value), i)) - } - } - - impl TreeHash for $type { - fn hash_tree_root(&self) -> Vec { - let mut result: Vec = vec![]; - result.append(&mut self.0.hash_tree_root()); - hash(&result) - } - } - }; -} - -macro_rules! impl_hash { - ($type: ident) => { - // Implemented to stop clippy lint: - // https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq - impl Hash for $type { - fn hash(&self, state: &mut H) { - ssz_encode(self).hash(state) - } - } - }; -} - -macro_rules! impl_common { - ($type: ident) => { - impl_from_into_u64!($type); - impl_from_into_usize!($type); - impl_math_between!($type, $type); - impl_math_between!($type, u64); - impl_math!($type); - impl_display!($type); - impl_ssz!($type); - impl_hash!($type); - }; -} /// Beacon block height, effectively `Slot/GENESIS_START_BLOCK`. #[derive(Eq, Debug, Clone, Copy, Default, Serialize)] pub struct SlotHeight(u64); From 5031ee5505924071f46029a944794cf7c0782862 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Fri, 15 Feb 2019 13:32:37 +1100 Subject: [PATCH 6/9] Remove comments from fork choice. --- eth2/fork_choice/src/lib.rs | 5 ----- eth2/fork_choice/src/longest_chain.rs | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/eth2/fork_choice/src/lib.rs b/eth2/fork_choice/src/lib.rs index e876ed60fc..c0df820c69 100644 --- a/eth2/fork_choice/src/lib.rs +++ b/eth2/fork_choice/src/lib.rs @@ -44,14 +44,12 @@ extern crate types; pub mod longest_chain; pub mod optimised_lmd_ghost; -//pub mod protolambda_lmd_ghost; pub mod slow_lmd_ghost; use db::stores::BeaconBlockAtSlotError; use db::DBError; use types::{BeaconBlock, Hash256}; -// export the main structs pub use longest_chain::LongestChain; pub use optimised_lmd_ghost::OptimisedLMDGhost; @@ -118,7 +116,4 @@ pub enum ForkChoiceAlgorithms { SlowLMDGhost, /// An optimised version of LMD-GHOST by Vitalik. OptimisedLMDGhost, - // Protolambda currently not implemented - // /// An optimised version of LMD-GHOST by Protolambda. - //ProtoLMDGhost, } diff --git a/eth2/fork_choice/src/longest_chain.rs b/eth2/fork_choice/src/longest_chain.rs index c25cd43a17..8056c11f22 100644 --- a/eth2/fork_choice/src/longest_chain.rs +++ b/eth2/fork_choice/src/longest_chain.rs @@ -52,7 +52,7 @@ impl ForkChoice for LongestChain { let block = self .block_store .get_deserialized(&block_hash)? - .ok_or(ForkChoiceError::MissingBeaconBlock(*block_hash))?; + .ok_or_else(|| ForkChoiceError::MissingBeaconBlock(*block_hash))?; head_blocks.push((index, block)); } From b16ac40fd51da6aac00418bb4547e03d0dbc6baf Mon Sep 17 00:00:00 2001 From: Age Manning Date: Fri, 15 Feb 2019 14:16:45 +1100 Subject: [PATCH 7/9] Add tests to SlotHeight. --- eth2/types/src/slot_epoch.rs | 380 +--------------------------- eth2/types/src/slot_epoch_macros.rs | 356 ++++++++++++++++++++++++++ eth2/types/src/slot_height.rs | 10 + 3 files changed, 379 insertions(+), 367 deletions(-) diff --git a/eth2/types/src/slot_epoch.rs b/eth2/types/src/slot_epoch.rs index c1554034ad..eb5a8dced7 100644 --- a/eth2/types/src/slot_epoch.rs +++ b/eth2/types/src/slot_epoch.rs @@ -100,373 +100,19 @@ impl<'a> Iterator for SlotIter<'a> { } #[cfg(test)] -mod tests { +mod slot_tests { use super::*; + use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng}; + use ssz::ssz_encode; - macro_rules! new_tests { - ($type: ident) => { - #[test] - fn new() { - assert_eq!($type(0), $type::new(0)); - assert_eq!($type(3), $type::new(3)); - assert_eq!($type(u64::max_value()), $type::new(u64::max_value())); - } - }; - } - - macro_rules! from_into_tests { - ($type: ident, $other: ident) => { - #[test] - fn into() { - let x: $other = $type(0).into(); - assert_eq!(x, 0); - - let x: $other = $type(3).into(); - assert_eq!(x, 3); - - let x: $other = $type(u64::max_value()).into(); - // Note: this will fail on 32 bit systems. This is expected as we don't have a proper - // 32-bit system strategy in place. - assert_eq!(x, $other::max_value()); - } - - #[test] - fn from() { - assert_eq!($type(0), $type::from(0_u64)); - assert_eq!($type(3), $type::from(3_u64)); - assert_eq!($type(u64::max_value()), $type::from($other::max_value())); - } - }; - } - - macro_rules! math_between_tests { - ($type: ident, $other: ident) => { - #[test] - fn partial_ord() { - let assert_partial_ord = |a: u64, partial_ord: Ordering, b: u64| { - let other: $other = $type(b).into(); - assert_eq!($type(a).partial_cmp(&other), Some(partial_ord)); - }; - - assert_partial_ord(1, Ordering::Less, 2); - assert_partial_ord(2, Ordering::Greater, 1); - assert_partial_ord(0, Ordering::Less, u64::max_value()); - assert_partial_ord(u64::max_value(), Ordering::Greater, 0); - } - - #[test] - fn partial_eq() { - let assert_partial_eq = |a: u64, b: u64, is_equal: bool| { - let other: $other = $type(b).into(); - assert_eq!($type(a).eq(&other), is_equal); - }; - - assert_partial_eq(0, 0, true); - assert_partial_eq(0, 1, false); - assert_partial_eq(1, 0, false); - assert_partial_eq(1, 1, true); - - assert_partial_eq(u64::max_value(), u64::max_value(), true); - assert_partial_eq(0, u64::max_value(), false); - assert_partial_eq(u64::max_value(), 0, false); - } - - #[test] - fn add_and_add_assign() { - let assert_add = |a: u64, b: u64, result: u64| { - let other: $other = $type(b).into(); - assert_eq!($type(a) + other, $type(result)); - - let mut add_assigned = $type(a); - add_assigned += other; - - assert_eq!(add_assigned, $type(result)); - }; - - assert_add(0, 1, 1); - assert_add(1, 0, 1); - assert_add(1, 2, 3); - assert_add(2, 1, 3); - assert_add(7, 7, 14); - - // Addition should be saturating. - assert_add(u64::max_value(), 1, u64::max_value()); - assert_add(u64::max_value(), u64::max_value(), u64::max_value()); - } - - #[test] - fn sub_and_sub_assign() { - let assert_sub = |a: u64, b: u64, result: u64| { - let other: $other = $type(b).into(); - assert_eq!($type(a) - other, $type(result)); - - let mut sub_assigned = $type(a); - sub_assigned -= other; - - assert_eq!(sub_assigned, $type(result)); - }; - - assert_sub(1, 0, 1); - assert_sub(2, 1, 1); - assert_sub(14, 7, 7); - assert_sub(u64::max_value(), 1, u64::max_value() - 1); - assert_sub(u64::max_value(), u64::max_value(), 0); - - // Subtraction should be saturating - assert_sub(0, 1, 0); - assert_sub(1, 2, 0); - } - - #[test] - fn mul_and_mul_assign() { - let assert_mul = |a: u64, b: u64, result: u64| { - let other: $other = $type(b).into(); - assert_eq!($type(a) * other, $type(result)); - - let mut mul_assigned = $type(a); - mul_assigned *= other; - - assert_eq!(mul_assigned, $type(result)); - }; - - assert_mul(2, 2, 4); - assert_mul(1, 2, 2); - assert_mul(0, 2, 0); - - // Multiplication should be saturating. - assert_mul(u64::max_value(), 2, u64::max_value()); - } - - #[test] - fn div_and_div_assign() { - let assert_div = |a: u64, b: u64, result: u64| { - let other: $other = $type(b).into(); - assert_eq!($type(a) / other, $type(result)); - - let mut div_assigned = $type(a); - div_assigned /= other; - - assert_eq!(div_assigned, $type(result)); - }; - - assert_div(0, 2, 0); - assert_div(2, 2, 1); - assert_div(100, 50, 2); - assert_div(128, 2, 64); - assert_div(u64::max_value(), 2, 2_u64.pow(63) - 1); - } - - #[test] - #[should_panic] - fn div_panics_with_divide_by_zero() { - let other: $other = $type(0).into(); - let _ = $type(2) / other; - } - - #[test] - #[should_panic] - fn div_assign_panics_with_divide_by_zero() { - let other: $other = $type(0).into(); - let mut assigned = $type(2); - assigned /= other; - } - - #[test] - fn rem() { - let assert_rem = |a: u64, b: u64, result: u64| { - let other: $other = $type(b).into(); - assert_eq!($type(a) % other, $type(result)); - }; - - assert_rem(3, 2, 1); - assert_rem(40, 2, 0); - assert_rem(10, 100, 10); - assert_rem(302042, 3293, 2379); - } - }; - } - - macro_rules! math_tests { - ($type: ident) => { - #[test] - fn saturating_sub() { - let assert_saturating_sub = |a: u64, b: u64, result: u64| { - assert_eq!($type(a).saturating_sub($type(b)), $type(result)); - }; - - assert_saturating_sub(1, 0, 1); - assert_saturating_sub(2, 1, 1); - assert_saturating_sub(14, 7, 7); - assert_saturating_sub(u64::max_value(), 1, u64::max_value() - 1); - assert_saturating_sub(u64::max_value(), u64::max_value(), 0); - - // Subtraction should be saturating - assert_saturating_sub(0, 1, 0); - assert_saturating_sub(1, 2, 0); - } - - #[test] - fn saturating_add() { - let assert_saturating_add = |a: u64, b: u64, result: u64| { - assert_eq!($type(a).saturating_add($type(b)), $type(result)); - }; - - assert_saturating_add(0, 1, 1); - assert_saturating_add(1, 0, 1); - assert_saturating_add(1, 2, 3); - assert_saturating_add(2, 1, 3); - assert_saturating_add(7, 7, 14); - - // Addition should be saturating. - assert_saturating_add(u64::max_value(), 1, u64::max_value()); - assert_saturating_add(u64::max_value(), u64::max_value(), u64::max_value()); - } - - #[test] - fn checked_div() { - let assert_checked_div = |a: u64, b: u64, result: Option| { - let division_result_as_u64 = match $type(a).checked_div($type(b)) { - None => None, - Some(val) => Some(val.as_u64()), - }; - assert_eq!(division_result_as_u64, result); - }; - - assert_checked_div(0, 2, Some(0)); - assert_checked_div(2, 2, Some(1)); - assert_checked_div(100, 50, Some(2)); - assert_checked_div(128, 2, Some(64)); - assert_checked_div(u64::max_value(), 2, Some(2_u64.pow(63) - 1)); - - assert_checked_div(2, 0, None); - assert_checked_div(0, 0, None); - assert_checked_div(u64::max_value(), 0, None); - } - - #[test] - fn is_power_of_two() { - let assert_is_power_of_two = |a: u64, result: bool| { - assert_eq!( - $type(a).is_power_of_two(), - result, - "{}.is_power_of_two() != {}", - a, - result - ); - }; - - assert_is_power_of_two(0, false); - assert_is_power_of_two(1, true); - assert_is_power_of_two(2, true); - assert_is_power_of_two(3, false); - assert_is_power_of_two(4, true); - - assert_is_power_of_two(2_u64.pow(4), true); - assert_is_power_of_two(u64::max_value(), false); - } - - #[test] - fn ord() { - let assert_ord = |a: u64, ord: Ordering, b: u64| { - assert_eq!($type(a).cmp(&$type(b)), ord); - }; - - assert_ord(1, Ordering::Less, 2); - assert_ord(2, Ordering::Greater, 1); - assert_ord(0, Ordering::Less, u64::max_value()); - assert_ord(u64::max_value(), Ordering::Greater, 0); - } - }; - } - - macro_rules! ssz_tests { - ($type: ident) => { - #[test] - pub fn test_ssz_round_trip() { - let mut rng = XorShiftRng::from_seed([42; 16]); - let original = $type::random_for_test(&mut rng); - - let bytes = ssz_encode(&original); - let (decoded, _) = $type::ssz_decode(&bytes, 0).unwrap(); - - assert_eq!(original, decoded); - } - - #[test] - pub fn test_hash_tree_root() { - let mut rng = XorShiftRng::from_seed([42; 16]); - let original = $type::random_for_test(&mut rng); - - let result = original.hash_tree_root(); - - assert_eq!(result.len(), 32); - // TODO: Add further tests - // https://github.com/sigp/lighthouse/issues/170 - } - }; - } - - macro_rules! all_tests { - ($type: ident) => { - new_tests!($type); - math_between_tests!($type, $type); - math_tests!($type); - ssz_tests!($type); - - mod u64_tests { - use super::*; - - from_into_tests!($type, u64); - math_between_tests!($type, u64); - - #[test] - pub fn as_64() { - let x = $type(0).as_u64(); - assert_eq!(x, 0); - - let x = $type(3).as_u64(); - assert_eq!(x, 3); - - let x = $type(u64::max_value()).as_u64(); - assert_eq!(x, u64::max_value()); - } - } - - mod usize_tests { - use super::*; - - from_into_tests!($type, usize); - - #[test] - pub fn as_usize() { - let x = $type(0).as_usize(); - assert_eq!(x, 0); - - let x = $type(3).as_usize(); - assert_eq!(x, 3); - - let x = $type(u64::max_value()).as_usize(); - assert_eq!(x, usize::max_value()); - } - } - }; - } - - #[cfg(test)] - mod slot_tests { - use super::*; - use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng}; - use ssz::ssz_encode; - - all_tests!(Slot); - } - - #[cfg(test)] - mod epoch_tests { - use super::*; - use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng}; - use ssz::ssz_encode; - - all_tests!(Epoch); - } + all_tests!(Slot); +} + +#[cfg(test)] +mod epoch_tests { + use super::*; + use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng}; + use ssz::ssz_encode; + + all_tests!(Epoch); } diff --git a/eth2/types/src/slot_epoch_macros.rs b/eth2/types/src/slot_epoch_macros.rs index 6813bfeaf7..48bc219da1 100644 --- a/eth2/types/src/slot_epoch_macros.rs +++ b/eth2/types/src/slot_epoch_macros.rs @@ -263,3 +263,359 @@ macro_rules! impl_common { impl_hash!($type); }; } + +// test macros +#[allow(unused_macros)] +macro_rules! new_tests { + ($type: ident) => { + #[test] + fn new() { + assert_eq!($type(0), $type::new(0)); + assert_eq!($type(3), $type::new(3)); + assert_eq!($type(u64::max_value()), $type::new(u64::max_value())); + } + }; +} + +#[allow(unused_macros)] +macro_rules! from_into_tests { + ($type: ident, $other: ident) => { + #[test] + fn into() { + let x: $other = $type(0).into(); + assert_eq!(x, 0); + + let x: $other = $type(3).into(); + assert_eq!(x, 3); + + let x: $other = $type(u64::max_value()).into(); + // Note: this will fail on 32 bit systems. This is expected as we don't have a proper + // 32-bit system strategy in place. + assert_eq!(x, $other::max_value()); + } + + #[test] + fn from() { + assert_eq!($type(0), $type::from(0_u64)); + assert_eq!($type(3), $type::from(3_u64)); + assert_eq!($type(u64::max_value()), $type::from($other::max_value())); + } + }; +} + +#[allow(unused_macros)] +macro_rules! math_between_tests { + ($type: ident, $other: ident) => { + #[test] + fn partial_ord() { + let assert_partial_ord = |a: u64, partial_ord: Ordering, b: u64| { + let other: $other = $type(b).into(); + assert_eq!($type(a).partial_cmp(&other), Some(partial_ord)); + }; + + assert_partial_ord(1, Ordering::Less, 2); + assert_partial_ord(2, Ordering::Greater, 1); + assert_partial_ord(0, Ordering::Less, u64::max_value()); + assert_partial_ord(u64::max_value(), Ordering::Greater, 0); + } + + #[test] + fn partial_eq() { + let assert_partial_eq = |a: u64, b: u64, is_equal: bool| { + let other: $other = $type(b).into(); + assert_eq!($type(a).eq(&other), is_equal); + }; + + assert_partial_eq(0, 0, true); + assert_partial_eq(0, 1, false); + assert_partial_eq(1, 0, false); + assert_partial_eq(1, 1, true); + + assert_partial_eq(u64::max_value(), u64::max_value(), true); + assert_partial_eq(0, u64::max_value(), false); + assert_partial_eq(u64::max_value(), 0, false); + } + + #[test] + fn add_and_add_assign() { + let assert_add = |a: u64, b: u64, result: u64| { + let other: $other = $type(b).into(); + assert_eq!($type(a) + other, $type(result)); + + let mut add_assigned = $type(a); + add_assigned += other; + + assert_eq!(add_assigned, $type(result)); + }; + + assert_add(0, 1, 1); + assert_add(1, 0, 1); + assert_add(1, 2, 3); + assert_add(2, 1, 3); + assert_add(7, 7, 14); + + // Addition should be saturating. + assert_add(u64::max_value(), 1, u64::max_value()); + assert_add(u64::max_value(), u64::max_value(), u64::max_value()); + } + + #[test] + fn sub_and_sub_assign() { + let assert_sub = |a: u64, b: u64, result: u64| { + let other: $other = $type(b).into(); + assert_eq!($type(a) - other, $type(result)); + + let mut sub_assigned = $type(a); + sub_assigned -= other; + + assert_eq!(sub_assigned, $type(result)); + }; + + assert_sub(1, 0, 1); + assert_sub(2, 1, 1); + assert_sub(14, 7, 7); + assert_sub(u64::max_value(), 1, u64::max_value() - 1); + assert_sub(u64::max_value(), u64::max_value(), 0); + + // Subtraction should be saturating + assert_sub(0, 1, 0); + assert_sub(1, 2, 0); + } + + #[test] + fn mul_and_mul_assign() { + let assert_mul = |a: u64, b: u64, result: u64| { + let other: $other = $type(b).into(); + assert_eq!($type(a) * other, $type(result)); + + let mut mul_assigned = $type(a); + mul_assigned *= other; + + assert_eq!(mul_assigned, $type(result)); + }; + + assert_mul(2, 2, 4); + assert_mul(1, 2, 2); + assert_mul(0, 2, 0); + + // Multiplication should be saturating. + assert_mul(u64::max_value(), 2, u64::max_value()); + } + + #[test] + fn div_and_div_assign() { + let assert_div = |a: u64, b: u64, result: u64| { + let other: $other = $type(b).into(); + assert_eq!($type(a) / other, $type(result)); + + let mut div_assigned = $type(a); + div_assigned /= other; + + assert_eq!(div_assigned, $type(result)); + }; + + assert_div(0, 2, 0); + assert_div(2, 2, 1); + assert_div(100, 50, 2); + assert_div(128, 2, 64); + assert_div(u64::max_value(), 2, 2_u64.pow(63) - 1); + } + + #[test] + #[should_panic] + fn div_panics_with_divide_by_zero() { + let other: $other = $type(0).into(); + let _ = $type(2) / other; + } + + #[test] + #[should_panic] + fn div_assign_panics_with_divide_by_zero() { + let other: $other = $type(0).into(); + let mut assigned = $type(2); + assigned /= other; + } + + #[test] + fn rem() { + let assert_rem = |a: u64, b: u64, result: u64| { + let other: $other = $type(b).into(); + assert_eq!($type(a) % other, $type(result)); + }; + + assert_rem(3, 2, 1); + assert_rem(40, 2, 0); + assert_rem(10, 100, 10); + assert_rem(302042, 3293, 2379); + } + }; +} + +#[allow(unused_macros)] +macro_rules! math_tests { + ($type: ident) => { + #[test] + fn saturating_sub() { + let assert_saturating_sub = |a: u64, b: u64, result: u64| { + assert_eq!($type(a).saturating_sub($type(b)), $type(result)); + }; + + assert_saturating_sub(1, 0, 1); + assert_saturating_sub(2, 1, 1); + assert_saturating_sub(14, 7, 7); + assert_saturating_sub(u64::max_value(), 1, u64::max_value() - 1); + assert_saturating_sub(u64::max_value(), u64::max_value(), 0); + + // Subtraction should be saturating + assert_saturating_sub(0, 1, 0); + assert_saturating_sub(1, 2, 0); + } + + #[test] + fn saturating_add() { + let assert_saturating_add = |a: u64, b: u64, result: u64| { + assert_eq!($type(a).saturating_add($type(b)), $type(result)); + }; + + assert_saturating_add(0, 1, 1); + assert_saturating_add(1, 0, 1); + assert_saturating_add(1, 2, 3); + assert_saturating_add(2, 1, 3); + assert_saturating_add(7, 7, 14); + + // Addition should be saturating. + assert_saturating_add(u64::max_value(), 1, u64::max_value()); + assert_saturating_add(u64::max_value(), u64::max_value(), u64::max_value()); + } + + #[test] + fn checked_div() { + let assert_checked_div = |a: u64, b: u64, result: Option| { + let division_result_as_u64 = match $type(a).checked_div($type(b)) { + None => None, + Some(val) => Some(val.as_u64()), + }; + assert_eq!(division_result_as_u64, result); + }; + + assert_checked_div(0, 2, Some(0)); + assert_checked_div(2, 2, Some(1)); + assert_checked_div(100, 50, Some(2)); + assert_checked_div(128, 2, Some(64)); + assert_checked_div(u64::max_value(), 2, Some(2_u64.pow(63) - 1)); + + assert_checked_div(2, 0, None); + assert_checked_div(0, 0, None); + assert_checked_div(u64::max_value(), 0, None); + } + + #[test] + fn is_power_of_two() { + let assert_is_power_of_two = |a: u64, result: bool| { + assert_eq!( + $type(a).is_power_of_two(), + result, + "{}.is_power_of_two() != {}", + a, + result + ); + }; + + assert_is_power_of_two(0, false); + assert_is_power_of_two(1, true); + assert_is_power_of_two(2, true); + assert_is_power_of_two(3, false); + assert_is_power_of_two(4, true); + + assert_is_power_of_two(2_u64.pow(4), true); + assert_is_power_of_two(u64::max_value(), false); + } + + #[test] + fn ord() { + let assert_ord = |a: u64, ord: Ordering, b: u64| { + assert_eq!($type(a).cmp(&$type(b)), ord); + }; + + assert_ord(1, Ordering::Less, 2); + assert_ord(2, Ordering::Greater, 1); + assert_ord(0, Ordering::Less, u64::max_value()); + assert_ord(u64::max_value(), Ordering::Greater, 0); + } + }; +} + +#[allow(unused_macros)] +macro_rules! ssz_tests { + ($type: ident) => { + #[test] + pub fn test_ssz_round_trip() { + let mut rng = XorShiftRng::from_seed([42; 16]); + let original = $type::random_for_test(&mut rng); + + let bytes = ssz_encode(&original); + let (decoded, _) = $type::ssz_decode(&bytes, 0).unwrap(); + + assert_eq!(original, decoded); + } + + #[test] + pub fn test_hash_tree_root() { + let mut rng = XorShiftRng::from_seed([42; 16]); + let original = $type::random_for_test(&mut rng); + + let result = original.hash_tree_root(); + + assert_eq!(result.len(), 32); + // TODO: Add further tests + // https://github.com/sigp/lighthouse/issues/170 + } + }; +} + +#[allow(unused_macros)] +macro_rules! all_tests { + ($type: ident) => { + new_tests!($type); + math_between_tests!($type, $type); + math_tests!($type); + ssz_tests!($type); + + mod u64_tests { + use super::*; + + from_into_tests!($type, u64); + math_between_tests!($type, u64); + + #[test] + pub fn as_64() { + let x = $type(0).as_u64(); + assert_eq!(x, 0); + + let x = $type(3).as_u64(); + assert_eq!(x, 3); + + let x = $type(u64::max_value()).as_u64(); + assert_eq!(x, u64::max_value()); + } + } + + mod usize_tests { + use super::*; + + from_into_tests!($type, usize); + + #[test] + pub fn as_usize() { + let x = $type(0).as_usize(); + assert_eq!(x, 0); + + let x = $type(3).as_usize(); + assert_eq!(x, 3); + + let x = $type(u64::max_value()).as_usize(); + assert_eq!(x, usize::max_value()); + } + } + }; +} diff --git a/eth2/types/src/slot_height.rs b/eth2/types/src/slot_height.rs index 3efd00cae8..afa0ff775a 100644 --- a/eth2/types/src/slot_height.rs +++ b/eth2/types/src/slot_height.rs @@ -32,3 +32,13 @@ impl SlotHeight { SlotHeight(u64::max_value()) } } + +#[cfg(test)] + +mod slot_height_tests { + use super::*; + use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng}; + use ssz::ssz_encode; + + all_tests!(SlotHeight); +} From 94c87845236383e8506f257b4a2853b3e87e63b1 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Fri, 15 Feb 2019 14:17:22 +1100 Subject: [PATCH 8/9] Remove Slow LMD Ghost from test harness. --- .../beacon_chain/test_harness/src/beacon_chain_harness.rs | 2 +- .../beacon_chain/test_harness/src/validator_harness/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/beacon_node/beacon_chain/test_harness/src/beacon_chain_harness.rs b/beacon_node/beacon_chain/test_harness/src/beacon_chain_harness.rs index 09621268ca..acba2e0154 100644 --- a/beacon_node/beacon_chain/test_harness/src/beacon_chain_harness.rs +++ b/beacon_node/beacon_chain/test_harness/src/beacon_chain_harness.rs @@ -6,7 +6,7 @@ use db::{ stores::{BeaconBlockStore, BeaconStateStore}, MemoryDB, }; -use fork_choice::{optimised_lmd_ghost::OptimisedLMDGhost, slow_lmd_ghost::SlowLMDGhost}; // import all the algorithms +use fork_choice::OptimisedLMDGhost; use log::debug; use rayon::prelude::*; use slot_clock::TestingSlotClock; diff --git a/beacon_node/beacon_chain/test_harness/src/validator_harness/mod.rs b/beacon_node/beacon_chain/test_harness/src/validator_harness/mod.rs index e22ea1a2e2..3df32fa643 100644 --- a/beacon_node/beacon_chain/test_harness/src/validator_harness/mod.rs +++ b/beacon_node/beacon_chain/test_harness/src/validator_harness/mod.rs @@ -10,7 +10,7 @@ use block_producer::{BlockProducer, Error as BlockPollError}; use db::MemoryDB; use direct_beacon_node::DirectBeaconNode; use direct_duties::DirectDuties; -use fork_choice::{optimised_lmd_ghost::OptimisedLMDGhost, slow_lmd_ghost::SlowLMDGhost}; +use fork_choice::OptimisedLMDGhost; use local_signer::LocalSigner; use slot_clock::TestingSlotClock; use std::sync::Arc; From e06c4796e41ce67fb2136bcbf48de7f2486c08c8 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Fri, 15 Feb 2019 14:21:33 +1100 Subject: [PATCH 9/9] Import Slot directly from types in fork-choice. --- eth2/fork_choice/src/optimised_lmd_ghost.rs | 4 ++-- eth2/fork_choice/src/slow_lmd_ghost.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/eth2/fork_choice/src/optimised_lmd_ghost.rs b/eth2/fork_choice/src/optimised_lmd_ghost.rs index 40dc029be0..6b21e39f86 100644 --- a/eth2/fork_choice/src/optimised_lmd_ghost.rs +++ b/eth2/fork_choice/src/optimised_lmd_ghost.rs @@ -30,8 +30,8 @@ use fast_math::log2_raw; use std::collections::HashMap; use std::sync::Arc; use types::{ - readers::BeaconBlockReader, slot_epoch::Slot, slot_height::SlotHeight, - validator_registry::get_active_validator_indices, BeaconBlock, Hash256, + readers::BeaconBlockReader, validator_registry::get_active_validator_indices, BeaconBlock, + Hash256, Slot, SlotHeight, }; //TODO: Pruning - Children diff --git a/eth2/fork_choice/src/slow_lmd_ghost.rs b/eth2/fork_choice/src/slow_lmd_ghost.rs index 609d28ab2a..3184150fde 100644 --- a/eth2/fork_choice/src/slow_lmd_ghost.rs +++ b/eth2/fork_choice/src/slow_lmd_ghost.rs @@ -28,8 +28,8 @@ use db::{ use std::collections::HashMap; use std::sync::Arc; use types::{ - readers::BeaconBlockReader, slot_epoch::Slot, validator_registry::get_active_validator_indices, - BeaconBlock, Hash256, + readers::BeaconBlockReader, validator_registry::get_active_validator_indices, BeaconBlock, + Hash256, Slot, }; //TODO: Pruning and syncing