diff --git a/src/state/mod.rs b/src/state/mod.rs index a68c591d78..922b54aa2f 100644 --- a/src/state/mod.rs +++ b/src/state/mod.rs @@ -13,6 +13,5 @@ pub mod block; 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 deleted file mode 100644 index 77472de324..0000000000 --- a/src/state/state_transition.rs +++ /dev/null @@ -1,122 +0,0 @@ -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; - -// Given an aggregate_vote and a crystallized_state, -// return a byte array for signing or verification. -pub fn get_crosslink_aggvote_msg( - agg_vote: &AggregateVote, - cry_state: &CrystallizedState) - -> Vec -{ - let mut buf = BytesMut::with_capacity(AGG_VOTE_MSG_SIZE as usize); - buf.put_u16_be(agg_vote.shard_id); - buf.extend_from_slice(&agg_vote.shard_block_hash.to_vec()); - buf.extend_from_slice(&cry_state.current_checkpoint.to_vec()); - buf.put_u64_be(cry_state.current_epoch); - buf.put_u64_be(cry_state.last_justified_epoch); - buf.to_vec() -} - -// For a given state set and skip_count, return a proposer and set -// of attestors. -pub fn get_attesters_and_proposer( - cry_state: &CrystallizedState, - act_state: &ActiveState, - skip_count: &u64, - config: &Config) - -> (Vec, usize) -{ - let active_validator_count = cry_state.num_active_validators(); - assert!(active_validator_count >= 2, "must be >=2 active validators"); - let shuffled_validator_indicies = get_shuffling( - &act_state.randao, - &active_validator_count, - config); - let proposer_count: usize = 1; - let ideal_validator_count: usize = (config.attester_count as usize) - + (*skip_count as usize) + proposer_count; - assert!(ideal_validator_count >= 2, - "ideal_validator_count must be >=2"); - if ideal_validator_count > active_validator_count { - return ( - shuffled_validator_indicies[0..active_validator_count - 1].to_vec(), - shuffled_validator_indicies[active_validator_count - 1]); - } else { - return ( - shuffled_validator_indicies[0..ideal_validator_count - 1].to_vec(), - shuffled_validator_indicies[ideal_validator_count - 1]); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use super::super::validator_record::ValidatorRecord; - use super::super::utils::types::{ Address, Sha256Digest, U256 }; - use super::super:: - utils::test_helpers::get_dangerous_test_keypair; - - #[test] - fn test_crosslink_aggvote_msg() { - let mut cs_state = CrystallizedState::zero(); - let mut agg_vote = AggregateVote::zero(); - // All zeros - let m1 = get_crosslink_aggvote_msg(&agg_vote, &cs_state); - assert_eq!(m1, - vec![0_u8; AGG_VOTE_MSG_SIZE as usize], - "failed all zeros test"); - // With some values - agg_vote.shard_id = 42; - cs_state.current_epoch = 99; - cs_state.last_justified_epoch = 123; - let m2 = get_crosslink_aggvote_msg(&agg_vote, &cs_state); - assert_eq!(m2[0..2], [0, 42]); - assert_eq!(m2[2..34], [0; 32]); // TODO: test with non-zero hash - assert_eq!(m2[34..66], [0; 32]); // TODO: test with non-zero hash - assert_eq!(m2[66..74], [0, 0, 0, 0, 0, 0, 0, 99]); - assert_eq!(m2[74..82], [0, 0, 0, 0, 0, 0, 0, 123]); - } - - #[test] - fn test_attester_and_proposer_selection() { - let mut cry_state = CrystallizedState::zero(); - for _ in 0..10 { - cry_state.active_validators.push(ValidatorRecord { - pubkey: get_dangerous_test_keypair().public, - withdrawal_shard: 0, - withdrawal_address: Address::zero(), - randao_commitment: Sha256Digest::zero(), - balance: U256::zero(), - switch_dynasty: 0 - }); - } - let act_state = ActiveState::zero(); - let (attestors, proposer) = get_attesters_and_proposer( - &cry_state, - &act_state, - &0, - &Config::standard()); - assert_eq!(attestors, [0, 9, 7, 6, 4, 1, 8, 5, 2]); - assert_eq!(proposer, 3); - } - - #[test] - #[should_panic(expected = "must be >=2 active validators")] - fn test_attester_and_proposer_selection_with_zero_active_validators() { - let mut cry_state = CrystallizedState::zero(); - cry_state.active_validators = Vec::new(); - let act_state = ActiveState::zero(); - let (_attestors, _proposer) = get_attesters_and_proposer( - &cry_state, - &act_state, - &0, - &Config::standard()); - } -} diff --git a/src/state/transition/attestations.rs b/src/state/transition/attestations.rs deleted file mode 100644 index 9ffa02ea7c..0000000000 --- a/src/state/transition/attestations.rs +++ /dev/null @@ -1,77 +0,0 @@ -use super::validator_record::ValidatorRecord; -use super::utils::types::Bitfield; -use super::utils::bls::{ AggregateSignature, PublicKey }; - -#[allow(unused_variables)] -pub fn process_attestations( - validators: &Vec, - attestation_indicies: &Vec, - attestation_bitfield: &Bitfield, - msg: &Vec, - aggregate_sig: &AggregateSignature) - -> Option> -{ - let mut key_msg_tuples: Vec<(&PublicKey, &[u8])> = vec![]; - let mut attesters: Vec = vec![]; - - assert_eq!(attestation_indicies.len(), attestation_bitfield.len()); - for (bitfield_bit, validators_index) in attestation_indicies.iter().enumerate() { - if attestation_bitfield.get_bit(&bitfield_bit) { - key_msg_tuples.push( - (&validators[*validators_index].pubkey, - &msg) - ); - attesters.push(*validators_index); - } - } - // TODO: figure out why this assert exists in the Python impl. - assert!(attesters.len() <= 128, "Max attesters is 128."); - - /* - // TODO: ensure signature verification actually takes place. - // It is completely bypassed here. - match aggregate_sig.verify(&key_msg_tuples) { - false => None, - true => Some(attesters) - } - */ - Some(attesters) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_attestation_processing() { - let validator_count = 10; - let mut validators: Vec = vec![]; - let mut attestation_indicies: Vec = vec![]; - let mut bitfield = Bitfield::new(); - let mut agg_sig = AggregateSignature::new(); - let msg = "Message that's longer than 16 chars".as_bytes(); - - for i in 0..validator_count { - let (v, keypair) = - ValidatorRecord::zero_with_thread_rand_keypair(); - validators.push(v); - attestation_indicies.push(i); - bitfield.set_bit(&i, &true); - let sig = keypair.sign(&msg); - agg_sig.aggregate(&sig); - } - - let result = process_attestations( - &validators, - &attestation_indicies, - &bitfield, - &msg.to_vec(), - &agg_sig); - - match result { - None => panic!("Verification failed."), - Some(x) => println!("{:?}", x) - }; - } -} - diff --git a/src/state/transition/attestors.rs b/src/state/transition/attestors.rs new file mode 100644 index 0000000000..06223f98ab --- /dev/null +++ b/src/state/transition/attestors.rs @@ -0,0 +1,143 @@ +use super::validator_record::ValidatorRecord; +use super::utils::types::Bitfield; +use super::utils::bls::{ AggregateSignature, PublicKey }; +use super::crystallized_state::CrystallizedState; +use super::active_state::ActiveState; +use super::config::Config; +use super::shuffling::get_shuffling; + +// For a given state set and skip_count, return a proposer and set +// of attestors. +pub fn get_attesters_and_proposer( + cry_state: &CrystallizedState, + act_state: &ActiveState, + skip_count: &u64, + config: &Config) + -> (Vec, usize) +{ + let active_validator_count = cry_state.num_active_validators(); + assert!(active_validator_count >= 2, "must be >=2 active validators"); + let shuffled_validator_indicies = get_shuffling( + &act_state.randao, + &active_validator_count, + config); + let proposer_count: usize = 1; + let ideal_validator_count: usize = (config.attester_count as usize) + + (*skip_count as usize) + proposer_count; + assert!(ideal_validator_count >= 2, + "ideal_validator_count must be >=2"); + if ideal_validator_count > active_validator_count { + return ( + shuffled_validator_indicies[0..active_validator_count - 1].to_vec(), + shuffled_validator_indicies[active_validator_count - 1]); + } else { + return ( + shuffled_validator_indicies[0..ideal_validator_count - 1].to_vec(), + shuffled_validator_indicies[ideal_validator_count - 1]); + } +} + +#[allow(unused_variables)] +pub fn process_attestations( + validators: &Vec, + attestation_indicies: &Vec, + attestation_bitfield: &Bitfield, + msg: &Vec, + aggregate_sig: &AggregateSignature) + -> Option> +{ + let mut key_msg_tuples: Vec<(&PublicKey, &[u8])> = vec![]; + let mut attesters: Vec = vec![]; + + assert_eq!(attestation_indicies.len(), attestation_bitfield.len()); + for (bitfield_bit, validators_index) in attestation_indicies.iter().enumerate() { + if attestation_bitfield.get_bit(&bitfield_bit) { + key_msg_tuples.push( + (&validators[*validators_index].pubkey, + &msg) + ); + attesters.push(*validators_index); + } + } + // TODO: figure out why this assert exists in the Python impl. + assert!(attesters.len() <= 128, "Max attesters is 128."); + + /* + // TODO: ensure signature verification actually takes place. + // It is completely bypassed here. + match aggregate_sig.verify(&key_msg_tuples) { + false => None, + true => Some(attesters) + } + */ + Some(attesters) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_attester_and_proposer_selection() { + let mut cry_state = CrystallizedState::zero(); + + (0..10).for_each( + |_| cry_state.active_validators.push( + ValidatorRecord::zero_with_thread_rand_pub_key())); + + let act_state = ActiveState::zero(); + let (attestors, proposer) = get_attesters_and_proposer( + &cry_state, + &act_state, + &0, + &Config::standard()); + assert_eq!(attestors, [0, 9, 7, 6, 4, 1, 8, 5, 2]); + assert_eq!(proposer, 3); + } + + #[test] + #[should_panic(expected = "must be >=2 active validators")] + fn test_attester_and_proposer_selection_with_zero_active_validators() { + let mut cry_state = CrystallizedState::zero(); + cry_state.active_validators = Vec::new(); + let act_state = ActiveState::zero(); + let (_attestors, _proposer) = get_attesters_and_proposer( + &cry_state, + &act_state, + &0, + &Config::standard()); + } + + #[test] + fn test_attestation_processing() { + let validator_count = 10; + let mut validators: Vec = vec![]; + let mut attestation_indicies: Vec = vec![]; + let mut bitfield = Bitfield::new(); + let mut agg_sig = AggregateSignature::new(); + let msg = "Message that's longer than 16 chars".as_bytes(); + + for i in 0..validator_count { + let (v, keypair) = + ValidatorRecord::zero_with_thread_rand_keypair(); + validators.push(v); + attestation_indicies.push(i); + bitfield.set_bit(&i, &true); + let sig = keypair.sign(&msg); + agg_sig.aggregate(&sig); + } + + let result = process_attestations( + &validators, + &attestation_indicies, + &bitfield, + &msg.to_vec(), + &agg_sig); + + match result { + None => panic!("Verification failed."), + Some(x) => println!("{:?}", x) + }; + } +} + diff --git a/src/state/transition/crosslinks.rs b/src/state/transition/crosslinks.rs index 6f73826554..d94cfc195b 100644 --- a/src/state/transition/crosslinks.rs +++ b/src/state/transition/crosslinks.rs @@ -1,11 +1,31 @@ use std::collections::HashMap; use std::cmp::min; +use super::bytes::{ BytesMut, BufMut }; +use super::aggregate_vote::AggregateVote; use super::crystallized_state::CrystallizedState; use super::crosslink_record::CrosslinkRecord; use super::partial_crosslink_record::PartialCrosslinkRecord; use super::config::Config; +const AGG_VOTE_MSG_SIZE: i32 = 2 + 32 + 32 + 8 + 8; + +// Given an aggregate_vote and a crystallized_state, +// return a byte array for signing or verification. +pub fn get_crosslink_aggvote_msg( + agg_vote: &AggregateVote, + cry_state: &CrystallizedState) + -> Vec +{ + let mut buf = BytesMut::with_capacity(AGG_VOTE_MSG_SIZE as usize); + buf.put_u16_be(agg_vote.shard_id); + buf.extend_from_slice(&agg_vote.shard_block_hash.to_vec()); + buf.extend_from_slice(&cry_state.current_checkpoint.to_vec()); + buf.put_u64_be(cry_state.current_epoch); + buf.put_u64_be(cry_state.last_justified_epoch); + buf.to_vec() +} + // Returns the maximum possible shards for a given validator_count // and configuration. pub fn get_crosslink_shards_count( @@ -185,6 +205,27 @@ mod tests { use super::super::shuffling::get_shuffling; use super::super::super::validator_record::ValidatorRecord; use super::super::super::super::utils::types::{ Sha256Digest, Bitfield }; + + #[test] + fn test_crosslink_aggvote_msg() { + let mut cs_state = CrystallizedState::zero(); + let mut agg_vote = AggregateVote::zero(); + // All zeros + let m1 = get_crosslink_aggvote_msg(&agg_vote, &cs_state); + assert_eq!(m1, + vec![0_u8; AGG_VOTE_MSG_SIZE as usize], + "failed all zeros test"); + // With some values + agg_vote.shard_id = 42; + cs_state.current_epoch = 99; + cs_state.last_justified_epoch = 123; + let m2 = get_crosslink_aggvote_msg(&agg_vote, &cs_state); + assert_eq!(m2[0..2], [0, 42]); + assert_eq!(m2[2..34], [0; 32]); // TODO: test with non-zero hash + assert_eq!(m2[34..66], [0; 32]); // TODO: test with non-zero hash + assert_eq!(m2[66..74], [0, 0, 0, 0, 0, 0, 0, 99]); + assert_eq!(m2[74..82], [0, 0, 0, 0, 0, 0, 0, 123]); + } #[test] fn test_crosslink_shard_count_with_varying_active_vals() { diff --git a/src/state/transition/mod.rs b/src/state/transition/mod.rs index e7d81eefd0..2393dc72ee 100644 --- a/src/state/transition/mod.rs +++ b/src/state/transition/mod.rs @@ -1,6 +1,9 @@ +use super::bytes; use super::config; use super::utils; use super::blake2; +use super::active_state; +use super::aggregate_vote; use super::crystallized_state; use super::crosslink_record; use super::partial_crosslink_record; @@ -10,4 +13,4 @@ pub mod crosslinks; pub mod deposits; pub mod shuffling; pub mod validators; -pub mod attestations; +pub mod attestors;