Merge pull request #228 from sigp/fork-choice
Fork choice longest chain update.
This commit is contained in:
commit
c632edcf17
@ -6,7 +6,7 @@ use db::{
|
|||||||
stores::{BeaconBlockStore, BeaconStateStore},
|
stores::{BeaconBlockStore, BeaconStateStore},
|
||||||
MemoryDB,
|
MemoryDB,
|
||||||
};
|
};
|
||||||
use fork_choice::{optimised_lmd_ghost::OptimisedLMDGhost, slow_lmd_ghost::SlowLMDGhost}; // import all the algorithms
|
use fork_choice::OptimisedLMDGhost;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use slot_clock::TestingSlotClock;
|
use slot_clock::TestingSlotClock;
|
||||||
|
@ -10,7 +10,7 @@ use block_producer::{BlockProducer, Error as BlockPollError};
|
|||||||
use db::MemoryDB;
|
use db::MemoryDB;
|
||||||
use direct_beacon_node::DirectBeaconNode;
|
use direct_beacon_node::DirectBeaconNode;
|
||||||
use direct_duties::DirectDuties;
|
use direct_duties::DirectDuties;
|
||||||
use fork_choice::{optimised_lmd_ghost::OptimisedLMDGhost, slow_lmd_ghost::SlowLMDGhost};
|
use fork_choice::OptimisedLMDGhost;
|
||||||
use local_signer::LocalSigner;
|
use local_signer::LocalSigner;
|
||||||
use slot_clock::TestingSlotClock;
|
use slot_clock::TestingSlotClock;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -10,3 +10,9 @@ ssz = { path = "../utils/ssz" }
|
|||||||
types = { path = "../types" }
|
types = { path = "../types" }
|
||||||
fast-math = "0.1.1"
|
fast-math = "0.1.1"
|
||||||
byteorder = "1.3.1"
|
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" }
|
||||||
|
@ -44,13 +44,15 @@ extern crate types;
|
|||||||
|
|
||||||
pub mod longest_chain;
|
pub mod longest_chain;
|
||||||
pub mod optimised_lmd_ghost;
|
pub mod optimised_lmd_ghost;
|
||||||
pub mod protolambda_lmd_ghost;
|
|
||||||
pub mod slow_lmd_ghost;
|
pub mod slow_lmd_ghost;
|
||||||
|
|
||||||
use db::stores::BeaconBlockAtSlotError;
|
use db::stores::BeaconBlockAtSlotError;
|
||||||
use db::DBError;
|
use db::DBError;
|
||||||
use types::{BeaconBlock, Hash256};
|
use types::{BeaconBlock, Hash256};
|
||||||
|
|
||||||
|
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
|
/// 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.
|
/// 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
|
/// The main fork choice algorithm is specified in `find_head
|
||||||
@ -83,6 +85,7 @@ pub enum ForkChoiceError {
|
|||||||
CannotFindBestChild,
|
CannotFindBestChild,
|
||||||
ChildrenNotFound,
|
ChildrenNotFound,
|
||||||
StorageError(String),
|
StorageError(String),
|
||||||
|
HeadNotFound,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<DBError> for ForkChoiceError {
|
impl From<DBError> for ForkChoiceError {
|
||||||
@ -113,6 +116,4 @@ pub enum ForkChoiceAlgorithms {
|
|||||||
SlowLMDGhost,
|
SlowLMDGhost,
|
||||||
/// An optimised version of LMD-GHOST by Vitalik.
|
/// An optimised version of LMD-GHOST by Vitalik.
|
||||||
OptimisedLMDGhost,
|
OptimisedLMDGhost,
|
||||||
/// An optimised version of LMD-GHOST by Protolambda.
|
|
||||||
ProtoLMDGhost,
|
|
||||||
}
|
}
|
||||||
|
@ -1,52 +1,73 @@
|
|||||||
use db::stores::BeaconBlockStore;
|
use crate::{ForkChoice, ForkChoiceError};
|
||||||
use db::{ClientDB, DBError};
|
use db::{stores::BeaconBlockStore, ClientDB};
|
||||||
use ssz::{Decodable, DecodeError};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use types::{BeaconBlock, Hash256, Slot};
|
use types::{BeaconBlock, Hash256, Slot};
|
||||||
|
|
||||||
pub enum ForkChoiceError {
|
pub struct LongestChain<T>
|
||||||
BadSszInDatabase,
|
|
||||||
MissingBlock,
|
|
||||||
DBError(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn longest_chain<T>(
|
|
||||||
head_block_hashes: &[Hash256],
|
|
||||||
block_store: &Arc<BeaconBlockStore<T>>,
|
|
||||||
) -> Result<Option<usize>, ForkChoiceError>
|
|
||||||
where
|
where
|
||||||
T: ClientDB + Sized,
|
T: ClientDB + Sized,
|
||||||
{
|
{
|
||||||
let mut head_blocks: Vec<(usize, BeaconBlock)> = vec![];
|
/// List of head block hashes
|
||||||
|
head_block_hashes: Vec<Hash256>,
|
||||||
|
/// Block storage access.
|
||||||
|
block_store: Arc<BeaconBlockStore<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> LongestChain<T>
|
||||||
|
where
|
||||||
|
T: ClientDB + Sized,
|
||||||
|
{
|
||||||
|
pub fn new(block_store: Arc<BeaconBlockStore<T>>) -> Self {
|
||||||
|
LongestChain {
|
||||||
|
head_block_hashes: Vec::new(),
|
||||||
|
block_store,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ClientDB + Sized> ForkChoice for LongestChain<T> {
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_attestation(&mut self, _: u64, _: &Hash256) -> Result<(), ForkChoiceError> {
|
||||||
|
// do nothing
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_head(&mut self, _: &Hash256) -> Result<Hash256, ForkChoiceError> {
|
||||||
|
let mut head_blocks: Vec<(usize, BeaconBlock)> = vec![];
|
||||||
/*
|
/*
|
||||||
* Load all the head_block hashes from the DB as SszBeaconBlocks.
|
* Load all the head_block hashes from the DB as SszBeaconBlocks.
|
||||||
*/
|
*/
|
||||||
for (index, block_hash) in head_block_hashes.iter().enumerate() {
|
for (index, block_hash) in self.head_block_hashes.iter().enumerate() {
|
||||||
let ssz = block_store
|
let block = self
|
||||||
.get(&block_hash)?
|
.block_store
|
||||||
.ok_or(ForkChoiceError::MissingBlock)?;
|
.get_deserialized(&block_hash)?
|
||||||
let (block, _) = BeaconBlock::ssz_decode(&ssz, 0)?;
|
.ok_or_else(|| ForkChoiceError::MissingBeaconBlock(*block_hash))?;
|
||||||
head_blocks.push((index, block));
|
head_blocks.push((index, block));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Loop through all the head blocks and find the highest slot.
|
* Loop through all the head blocks and find the highest slot.
|
||||||
*/
|
*/
|
||||||
let highest_slot: Option<Slot> = None;
|
let highest_slot = head_blocks
|
||||||
for (_, block) in &head_blocks {
|
.iter()
|
||||||
let slot = block.slot;
|
.fold(Slot::from(0u64), |highest, (_, block)| {
|
||||||
|
std::cmp::max(block.slot, highest)
|
||||||
|
});
|
||||||
|
|
||||||
match highest_slot {
|
// if we find no blocks, return Error
|
||||||
None => Some(slot),
|
if highest_slot == 0 {
|
||||||
Some(winning_slot) => {
|
return Err(ForkChoiceError::HeadNotFound);
|
||||||
if slot > winning_slot {
|
|
||||||
Some(slot)
|
|
||||||
} else {
|
|
||||||
Some(winning_slot)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -55,39 +76,27 @@ where
|
|||||||
* Ultimately, the index of the head_block hash with the highest slot and highest block
|
* Ultimately, the index of the head_block hash with the highest slot and highest block
|
||||||
* hash will be the winner.
|
* hash will be the winner.
|
||||||
*/
|
*/
|
||||||
match highest_slot {
|
|
||||||
None => Ok(None),
|
let head_index: Option<usize> =
|
||||||
Some(highest_slot) => {
|
head_blocks
|
||||||
let mut highest_blocks = vec![];
|
.iter()
|
||||||
for (index, block) in head_blocks {
|
.fold(None, |smallest_index, (index, block)| {
|
||||||
if block.slot == highest_slot {
|
if block.slot == highest_slot {
|
||||||
highest_blocks.push((index, block))
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
highest_blocks.sort_by(|a, b| head_block_hashes[a.0].cmp(&head_block_hashes[b.0]));
|
Ok(self.head_block_hashes[head_index.unwrap()])
|
||||||
let (index, _) = highest_blocks[0];
|
|
||||||
Ok(Some(index))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<DecodeError> for ForkChoiceError {
|
|
||||||
fn from(_: DecodeError) -> Self {
|
|
||||||
ForkChoiceError::BadSszInDatabase
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<DBError> for ForkChoiceError {
|
|
||||||
fn from(e: DBError) -> Self {
|
|
||||||
ForkChoiceError::DBError(e.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
#[test]
|
|
||||||
fn test_naive_fork_choice() {
|
|
||||||
assert_eq!(2 + 2, 4);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,10 +30,8 @@ use fast_math::log2_raw;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use types::{
|
use types::{
|
||||||
readers::BeaconBlockReader,
|
readers::BeaconBlockReader, validator_registry::get_active_validator_indices, BeaconBlock,
|
||||||
slot_epoch_height::{Height, Slot},
|
Hash256, Slot, SlotHeight,
|
||||||
validator_registry::get_active_validator_indices,
|
|
||||||
BeaconBlock, Hash256,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//TODO: Pruning - Children
|
//TODO: Pruning - Children
|
||||||
@ -77,7 +75,7 @@ pub struct OptimisedLMDGhost<T: ClientDB + Sized> {
|
|||||||
block_store: Arc<BeaconBlockStore<T>>,
|
block_store: Arc<BeaconBlockStore<T>>,
|
||||||
/// State storage access.
|
/// State storage access.
|
||||||
state_store: Arc<BeaconStateStore<T>>,
|
state_store: Arc<BeaconStateStore<T>>,
|
||||||
max_known_height: Height,
|
max_known_height: SlotHeight,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> OptimisedLMDGhost<T>
|
impl<T> OptimisedLMDGhost<T>
|
||||||
@ -93,7 +91,7 @@ where
|
|||||||
ancestors: vec![HashMap::new(); 16],
|
ancestors: vec![HashMap::new(); 16],
|
||||||
latest_attestation_targets: HashMap::new(),
|
latest_attestation_targets: HashMap::new(),
|
||||||
children: HashMap::new(),
|
children: HashMap::new(),
|
||||||
max_known_height: Height::new(0),
|
max_known_height: SlotHeight::new(0),
|
||||||
block_store,
|
block_store,
|
||||||
state_store,
|
state_store,
|
||||||
}
|
}
|
||||||
@ -118,7 +116,7 @@ where
|
|||||||
.ok_or_else(|| ForkChoiceError::MissingBeaconState(*state_root))?;
|
.ok_or_else(|| ForkChoiceError::MissingBeaconState(*state_root))?;
|
||||||
|
|
||||||
let active_validator_indices = get_active_validator_indices(
|
let active_validator_indices = get_active_validator_indices(
|
||||||
¤t_state.validator_registry,
|
¤t_state.validator_registry[..],
|
||||||
block_slot.epoch(EPOCH_LENGTH),
|
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`.
|
/// 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<Hash256> {
|
fn get_ancestor(&mut self, block_hash: Hash256, at_height: SlotHeight) -> Option<Hash256> {
|
||||||
// return None if we can't get the block from the db.
|
// return None if we can't get the block from the db.
|
||||||
let block_height = {
|
let block_height = {
|
||||||
let block_slot = self
|
let block_slot = self
|
||||||
@ -186,7 +184,7 @@ where
|
|||||||
fn get_clear_winner(
|
fn get_clear_winner(
|
||||||
&mut self,
|
&mut self,
|
||||||
latest_votes: &HashMap<Hash256, u64>,
|
latest_votes: &HashMap<Hash256, u64>,
|
||||||
block_height: Height,
|
block_height: SlotHeight,
|
||||||
) -> Option<Hash256> {
|
) -> Option<Hash256> {
|
||||||
// map of vote counts for every hash at this height
|
// map of vote counts for every hash at this height
|
||||||
let mut current_votes: HashMap<Hash256, u64> = HashMap::new();
|
let mut current_votes: HashMap<Hash256, u64> = HashMap::new();
|
||||||
|
@ -28,10 +28,8 @@ use db::{
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use types::{
|
use types::{
|
||||||
readers::{BeaconBlockReader, BeaconStateReader},
|
readers::BeaconBlockReader, validator_registry::get_active_validator_indices, BeaconBlock,
|
||||||
slot_epoch_height::Slot,
|
Hash256, Slot,
|
||||||
validator_registry::get_active_validator_indices,
|
|
||||||
BeaconBlock, Hash256,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//TODO: Pruning and syncing
|
//TODO: Pruning and syncing
|
||||||
|
@ -24,6 +24,8 @@ pub mod readers;
|
|||||||
pub mod shard_reassignment_record;
|
pub mod shard_reassignment_record;
|
||||||
pub mod slashable_attestation;
|
pub mod slashable_attestation;
|
||||||
pub mod slashable_vote_data;
|
pub mod slashable_vote_data;
|
||||||
|
#[macro_use]
|
||||||
|
pub mod slot_epoch_macros;
|
||||||
pub mod slot_epoch;
|
pub mod slot_epoch;
|
||||||
pub mod slot_height;
|
pub mod slot_height;
|
||||||
pub mod spec;
|
pub mod spec;
|
||||||
|
@ -21,255 +21,6 @@ use std::hash::{Hash, Hasher};
|
|||||||
use std::iter::Iterator;
|
use std::iter::Iterator;
|
||||||
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign};
|
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign};
|
||||||
|
|
||||||
macro_rules! impl_from_into_u64 {
|
|
||||||
($main: ident) => {
|
|
||||||
impl From<u64> for $main {
|
|
||||||
fn from(n: u64) -> $main {
|
|
||||||
$main(n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<u64> 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<usize> for $main {
|
|
||||||
fn from(n: usize) -> $main {
|
|
||||||
$main(n as u64)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<usize> 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<Ordering> {
|
|
||||||
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<T: Into<$type>>(&self, other: T) -> $type {
|
|
||||||
*self - other.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn saturating_add<T: Into<$type>>(&self, other: T) -> $type {
|
|
||||||
*self + other.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn checked_div<T: Into<$type>>(&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<u8> {
|
|
||||||
let mut result: Vec<u8> = vec![];
|
|
||||||
result.append(&mut self.0.hash_tree_root());
|
|
||||||
hash(&result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: RngCore> TestRandom<T> 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<H: Hasher>(&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)]
|
#[derive(Eq, Debug, Clone, Copy, Default, Serialize)]
|
||||||
pub struct Slot(u64);
|
pub struct Slot(u64);
|
||||||
|
|
||||||
@ -349,373 +100,19 @@ impl<'a> Iterator for SlotIter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod slot_tests {
|
||||||
use super::*;
|
|
||||||
|
|
||||||
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<u64>| {
|
|
||||||
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 super::*;
|
||||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||||
use ssz::ssz_encode;
|
use ssz::ssz_encode;
|
||||||
|
|
||||||
all_tests!(Slot);
|
all_tests!(Slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod epoch_tests {
|
mod epoch_tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||||
use ssz::ssz_encode;
|
use ssz::ssz_encode;
|
||||||
|
|
||||||
all_tests!(Epoch);
|
all_tests!(Epoch);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
621
eth2/types/src/slot_epoch_macros.rs
Normal file
621
eth2/types/src/slot_epoch_macros.rs
Normal file
@ -0,0 +1,621 @@
|
|||||||
|
macro_rules! impl_from_into_u64 {
|
||||||
|
($main: ident) => {
|
||||||
|
impl From<u64> for $main {
|
||||||
|
fn from(n: u64) -> $main {
|
||||||
|
$main(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<u64> 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<u32> 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<usize> for $main {
|
||||||
|
fn from(n: usize) -> $main {
|
||||||
|
$main(n as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<usize> 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<Ordering> {
|
||||||
|
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<T: Into<$type>>(&self, other: T) -> $type {
|
||||||
|
*self - other.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn saturating_add<T: Into<$type>>(&self, other: T) -> $type {
|
||||||
|
*self + other.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn checked_div<T: Into<$type>>(&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<u8> {
|
||||||
|
let mut result: Vec<u8> = vec![];
|
||||||
|
result.append(&mut self.0.hash_tree_root());
|
||||||
|
hash(&result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: RngCore> TestRandom<T> 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<H: Hasher>(&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);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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<u64>| {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -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::slot_epoch::{Epoch, Slot};
|
||||||
|
use crate::test_utils::TestRandom;
|
||||||
|
use rand::RngCore;
|
||||||
use serde_derive::Serialize;
|
use serde_derive::Serialize;
|
||||||
use slog;
|
|
||||||
use ssz::{hash, ssz_encode, Decodable, DecodeError, Encodable, SszStream, TreeHash};
|
use ssz::{hash, ssz_encode, Decodable, DecodeError, Encodable, SszStream, TreeHash};
|
||||||
use std::cmp::{Ord, Ordering};
|
use std::cmp::{Ord, Ordering};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign};
|
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign};
|
||||||
|
|
||||||
macro_rules! impl_from_into_u64 {
|
|
||||||
($main: ident) => {
|
|
||||||
impl From<u64> for $main {
|
|
||||||
fn from(n: u64) -> $main {
|
|
||||||
$main(n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<u64> 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<u32> 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<usize> for $main {
|
|
||||||
fn from(n: usize) -> $main {
|
|
||||||
$main(n as u64)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<usize> 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<Ordering> {
|
|
||||||
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<T: Into<$type>>(&self, other: T) -> $type {
|
|
||||||
*self - other.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn saturating_add<T: Into<$type>>(&self, other: T) -> $type {
|
|
||||||
*self + other.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn checked_div<T: Into<$type>>(&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<u8> {
|
|
||||||
let mut result: Vec<u8> = 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<H: Hasher>(&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`.
|
/// Beacon block height, effectively `Slot/GENESIS_START_BLOCK`.
|
||||||
#[derive(Eq, Debug, Clone, Copy, Default, Serialize)]
|
#[derive(Eq, Debug, Clone, Copy, Default, Serialize)]
|
||||||
pub struct SlotHeight(u64);
|
pub struct SlotHeight(u64);
|
||||||
@ -309,3 +32,13 @@ impl SlotHeight {
|
|||||||
SlotHeight(u64::max_value())
|
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);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user