diff --git a/src/state/crystallized_state.rs b/src/state/crystallized_state.rs index b2acb7da6..cf451b6d5 100644 --- a/src/state/crystallized_state.rs +++ b/src/state/crystallized_state.rs @@ -40,6 +40,11 @@ impl CrystallizedState { } } + pub fn finality_distance(&self) -> u64 { + assert!(self.current_epoch >= self.last_finalized_epoch); + self.current_epoch - self.last_finalized_epoch + } + pub fn num_active_validators(&self) -> usize { self.active_validators.len() } diff --git a/src/state/mod.rs b/src/state/mod.rs index c602fcc1c..a68c591d7 100644 --- a/src/state/mod.rs +++ b/src/state/mod.rs @@ -14,4 +14,5 @@ pub mod crosslink_record; pub mod partial_crosslink_record; pub mod recent_proposer_record; pub mod state_transition; +pub mod transition; pub mod validator_record; diff --git a/src/state/state_transition.rs b/src/state/state_transition.rs index aff548acb..a9f1c41ba 100644 --- a/src/state/state_transition.rs +++ b/src/state/state_transition.rs @@ -1,56 +1,14 @@ -use super::utils::types::{ Sha256Digest }; -use super::blake2::{ Blake2s, Digest }; +use super::utils::types::{ Bitfield, U256 }; use super::bytes::{ BytesMut, BufMut }; use super::crystallized_state::CrystallizedState; use super::active_state::ActiveState; use super::aggregate_vote::AggregateVote; + +use super::transition::shuffling::get_shuffling; use super::config::Config; const AGG_VOTE_MSG_SIZE: i32 = 2 + 32 + 32 + 8 + 8; -// Interprets a 3-byte slice from a [u8] as an integer. -fn get_shift_from_source(source: &[u8], offset: usize) -> usize { - (source[offset + 2] as usize) | - ((source[offset + 1] as usize) << 8) | - ((source[offset ] as usize) << 16) -} - -// Given entropy in the form of `seed`, return a shuffled list of validators -// indicies of size `validator_count` or `sample`. -pub fn get_shuffling( - seed: &Sha256Digest, - validator_count: &usize, - config: &Config) - -> Vec -{ - assert!(*validator_count > 0, "cannot shuffle 0 validators"); - let mut output: Vec = (0..*validator_count).collect(); - assert!(*validator_count <= (config.max_validators as usize), - "validator_count exceeds max_validators"); - - // Do the first blake hash round - let mut source = Blake2s::new(); - source.input(&seed); - - let mut v = 0; - while v < *validator_count { - let current_source = source.result(); - let mut source_offset = 0; - while source_offset < 30 { - let m = get_shift_from_source(¤t_source, source_offset); - let shuffled_position: usize = (m % (validator_count - v)) + v; - output.swap(v as usize, shuffled_position as usize); - v += 1; - if v >= *validator_count { break; } - source_offset += 3; - } - // Re-hash the source (TODO: this does one extra hash, can be optimised) - source = Blake2s::new(); - source.input(¤t_source); - } - output -} - // Given an aggregate_vote and a crystallized_state, // return a byte array for signing or verification. pub fn get_crosslink_aggvote_msg( @@ -96,63 +54,49 @@ pub fn get_attesters_and_proposer( } } +pub fn process_ffg_deposits( + cry_state: &CrystallizedState, + ffg_vote_bitfield: Bitfield) + -> (Vec, u64, U256, bool, bool) +{ + let active_validators: usize = cry_state.num_active_validators(); + let finality_distance: u64 = cry_state.finality_distance(); + let online_reward: u64 = if finality_distance <= 2 { 6 } else { 0 }; + let offline_penalty: u64 = finality_distance.saturating_mul(3); + let mut total_vote_count: u64 = 0; + let mut total_vote_deposits = U256::zero(); + + let mut deltas = vec![0_u64; active_validators]; + for i in 0..active_validators { + if ffg_vote_bitfield.get_bit(&i) { + total_vote_deposits = total_vote_deposits + .saturating_add(cry_state.active_validators[i].balance); + deltas[i] += online_reward; + total_vote_count += 1; + } else { + deltas[i] = deltas[i].saturating_sub(offline_penalty); + } + } + + let mut should_finalize = false; + let should_justify = total_vote_deposits.saturating_mul(U256::from(3)) + >= cry_state.total_deposits.saturating_mul(U256::from(2)); + if should_justify { + if cry_state.last_justified_epoch == cry_state.current_epoch - 1 { + should_finalize = true; + } + } + (deltas, total_vote_count, total_vote_deposits, should_justify, should_finalize) +} #[cfg(test)] mod tests { use super::*; use super::super::validator_record::ValidatorRecord; - use super::super::utils::types::Address; + use super::super::utils::types::{ Address, Sha256Digest }; use super::super:: utils::test_helpers::get_dangerous_test_keypair; - #[test] - fn test_shuffling_shift_fn() { - let mut x = get_shift_from_source( - &vec![0_u8, 0, 1], - 0); - assert_eq!((x as u32), 1); - - x = get_shift_from_source( - &vec![0_u8, 1, 1], - 0); - assert_eq!(x, 257); - - x = get_shift_from_source( - &vec![1_u8, 1, 1], - 0); - assert_eq!(x, 65793); - - x = get_shift_from_source( - &vec![255_u8, 1, 1], - 0); - assert_eq!(x, 16711937); - } - - - #[test] - fn test_shuffling() { - let s = get_shuffling( - &Sha256Digest::zero(), - &10, - &Config::standard()); - assert_eq!(s, - vec!(0, 9, 7, 6, 4, 1, 8, 5, 2, 3), - "10 validator shuffle was not as expected"); - } - - #[test] - fn test_shuffling_with_gt_half_max_validators() { - let mut config = Config::standard(); - config.max_validators = 19; - let s = get_shuffling( - &Sha256Digest::zero(), - &10, - &Config::standard()); - assert_eq!(s, - vec!(0, 9, 7, 6, 4, 1, 8, 5, 2, 3), - "10 validator shuffle was not as expected"); - } - #[test] fn test_crosslink_aggvote_msg() { let mut cs_state = CrystallizedState::zero(); @@ -183,7 +127,7 @@ mod tests { withdrawal_shard: 0, withdrawal_address: Address::zero(), randao_commitment: Sha256Digest::zero(), - balance: 0, + balance: U256::zero(), switch_dynasty: 0 }); } diff --git a/src/state/transition/mod.rs b/src/state/transition/mod.rs new file mode 100644 index 000000000..2f722f2de --- /dev/null +++ b/src/state/transition/mod.rs @@ -0,0 +1,5 @@ +use super::config; +use super::utils; +use super::blake2; + +pub mod shuffling; diff --git a/src/state/transition/shuffling.rs b/src/state/transition/shuffling.rs new file mode 100644 index 000000000..3525ec17c --- /dev/null +++ b/src/state/transition/shuffling.rs @@ -0,0 +1,99 @@ +use super::config::Config; +use super::blake2::{ Blake2s, Digest }; +use super::utils::types::Sha256Digest; + +// Interprets a 3-byte slice from a [u8] as an integer. +fn get_shift_from_source(source: &[u8], offset: usize) -> usize { + (source[offset + 2] as usize) | + ((source[offset + 1] as usize) << 8) | + ((source[offset ] as usize) << 16) +} + +// Given entropy in the form of `seed`, return a shuffled list of validators +// indicies of size `validator_count` or `sample`. +pub fn get_shuffling( + seed: &Sha256Digest, + validator_count: &usize, + config: &Config) + -> Vec +{ + assert!(*validator_count > 0, "cannot shuffle 0 validators"); + let mut output: Vec = (0..*validator_count).collect(); + assert!(*validator_count <= (config.max_validators as usize), + "validator_count exceeds max_validators"); + + // Do the first blake hash round + let mut source = Blake2s::new(); + source.input(&seed); + + let mut v = 0; + while v < *validator_count { + let current_source = source.result(); + let mut source_offset = 0; + while source_offset < 30 { + let m = get_shift_from_source(¤t_source, source_offset); + let shuffled_position: usize = (m % (validator_count - v)) + v; + output.swap(v as usize, shuffled_position as usize); + v += 1; + if v >= *validator_count { break; } + source_offset += 3; + } + // Re-hash the source (TODO: this does one extra hash, can be optimised) + source = Blake2s::new(); + source.input(¤t_source); + } + output +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_shuffling_shift_fn() { + let mut x = get_shift_from_source( + &vec![0_u8, 0, 1], + 0); + assert_eq!((x as u32), 1); + + x = get_shift_from_source( + &vec![0_u8, 1, 1], + 0); + assert_eq!(x, 257); + + x = get_shift_from_source( + &vec![1_u8, 1, 1], + 0); + assert_eq!(x, 65793); + + x = get_shift_from_source( + &vec![255_u8, 1, 1], + 0); + assert_eq!(x, 16711937); + } + + + #[test] + fn test_shuffling() { + let s = get_shuffling( + &Sha256Digest::zero(), + &10, + &Config::standard()); + assert_eq!(s, + vec!(0, 9, 7, 6, 4, 1, 8, 5, 2, 3), + "10 validator shuffle was not as expected"); + } + + #[test] + fn test_shuffling_with_gt_half_max_validators() { + let mut config = Config::standard(); + config.max_validators = 19; + let s = get_shuffling( + &Sha256Digest::zero(), + &10, + &Config::standard()); + assert_eq!(s, + vec!(0, 9, 7, 6, 4, 1, 8, 5, 2, 3), + "10 validator shuffle was not as expected"); + } +} diff --git a/src/state/validator_record.rs b/src/state/validator_record.rs index 87146073a..5835437c8 100644 --- a/src/state/validator_record.rs +++ b/src/state/validator_record.rs @@ -1,4 +1,4 @@ -use super::utils::types::{ Sha256Digest, Address }; +use super::utils::types::{ Sha256Digest, Address, U256 }; use super::utils::bls::PublicKey; use super::rlp::{ RlpStream, Encodable }; @@ -7,7 +7,7 @@ pub struct ValidatorRecord { pub withdrawal_shard: u16, pub withdrawal_address: Address, pub randao_commitment: Sha256Digest, - pub balance: u64, + pub balance: U256, pub switch_dynasty: u64 } @@ -16,17 +16,17 @@ impl ValidatorRecord { withdrawal_shard: u16, withdrawal_address: Address, randao_commitment: Sha256Digest, - balance: u64, + balance: U256, switch_dynasty: u64) -> Self { Self { - pubkey: pubkey, - withdrawal_shard: withdrawal_shard, - withdrawal_address: withdrawal_address, - randao_commitment: randao_commitment, - balance: balance, - switch_dynasty: switch_dynasty + pubkey, + withdrawal_shard, + withdrawal_address, + randao_commitment, + balance, + switch_dynasty } } } @@ -61,7 +61,7 @@ mod tests { let withdrawal_shard = 1; let withdrawal_address = Address::random(); let randao_commitment = Sha256Digest::random(); - let balance = 100; + let balance = U256::from(100); let switch_dynasty = 10; let v = ValidatorRecord::new( @@ -88,7 +88,7 @@ mod tests { withdrawal_shard: 100, withdrawal_address: Address::zero(), randao_commitment: Sha256Digest::zero(), - balance: 120, + balance: U256::from(120), switch_dynasty: 30 }; let e = rlp::encode(&v); diff --git a/src/utils/test_helpers.rs b/src/utils/test_helpers.rs index 8d4e6078f..da28de63a 100644 --- a/src/utils/test_helpers.rs +++ b/src/utils/test_helpers.rs @@ -1,13 +1,10 @@ extern crate rand; use super::bls::Keypair; -use self::rand::{ SeedableRng, XorShiftRng }; +use self::rand::thread_rng; // Returns a keypair for use in testing purposes. pub fn get_dangerous_test_keypair() -> Keypair { - let mut rng = XorShiftRng::from_seed([0xbc4f6d44, - 0xd62f276c, - 0xb963afd0, - 0x5455863d]); + let mut rng = thread_rng(); Keypair::generate(&mut rng) } diff --git a/src/utils/types.rs b/src/utils/types.rs index 6bc672600..8ed504874 100644 --- a/src/utils/types.rs +++ b/src/utils/types.rs @@ -4,6 +4,7 @@ use super::crystallized_state::CrystallizedState; use super::boolean_bitfield::BooleanBitfield; pub use super::blake2::Blake2s; +pub use super::ethereum_types::U256; // TODO: presently the compiler accepts these two types // as interchangable. This is somewhat loose typing,