diff --git a/src/state/active_state.rs b/src/state/active_state.rs index 2e59beea2..5f14e05cc 100644 --- a/src/state/active_state.rs +++ b/src/state/active_state.rs @@ -1,121 +1,31 @@ -use super::partial_crosslink_record::PartialCrosslinkRecord; -use super::recent_proposer_record::RecentPropserRecord; -use super::rlp::{ RlpStream, Encodable }; -use super::rlp::encode as rlp_encode; -use super::blake2::{ Blake2s, Digest }; -use super::utils::types::*; +use super::utils::types::Hash256; +use super::attestation_record::AttestationRecord; #[derive(Clone)] pub struct ActiveState { - pub height: u64, - pub randao: Sha256Digest, - pub ffg_voter_bitfield: Bitfield, - pub recent_attesters: Vec, // TODO: should be u24 - pub partial_crosslinks: Vec, - pub total_skip_count: u64, - pub recent_proposers: Vec + pub pending_attestations: Vec, + pub recent_block_hashes: Vec, } impl ActiveState { + /// Returns a new instance where all fields are empty vectors. pub fn zero() -> Self { Self { - height: 0, - randao: Sha256Digest::zero(), - ffg_voter_bitfield: Bitfield::new(), - recent_attesters: Vec::new(), - partial_crosslinks: Vec::new(), - total_skip_count: 0, - recent_proposers: Vec::new() + pending_attestations: vec![], + recent_block_hashes: vec![], } } - - pub fn num_recent_proposers(&self) -> usize { - self.recent_proposers.len() - } - - pub fn num_recent_attesters(&self) -> usize { - self.recent_attesters.len() - } - - pub fn blake2s_hash(&self) -> Blake2sDigest { - let mut hasher = Blake2s::new(); - hasher.input(&rlp_encode(self).into_vec()); - let mut digest = Blake2sDigest::new(); - digest.clone_from_slice(hasher.result().as_slice()); - digest - } -} - -/* - * RLP Encoding - */ -impl Encodable for ActiveState { - fn rlp_append(&self, s: &mut RlpStream) { - s.append(&self.height); - s.append(&self.randao); - s.append(&self.ffg_voter_bitfield); - s.append_list(&self.recent_attesters); - s.append_list(&self.partial_crosslinks); - s.append(&self.total_skip_count); - s.append_list(&self.recent_proposers); - } } #[cfg(test)] mod tests { - use super::super::rlp; use super::*; #[test] - fn test_zero_fn() { + fn test_act_state_zero() { let a = ActiveState::zero(); - assert_eq!(a.height, 0); - // TODO: test all the things - assert_eq!(a.total_skip_count, 0); - } - - #[test] - fn test_num_recent_proposers() { - let mut a = ActiveState::zero(); - for _ in 1..5 { - a.recent_proposers.push(RecentPropserRecord::new( - 1, - Sha256Digest::random(), - 2)); - } - assert_eq!(a.num_recent_proposers(), 4) - } - - #[test] - fn test_num_recent_attesters() { - let mut a = ActiveState::zero(); - for _ in 1..5 { - a.recent_attesters.push(1); - } - assert_eq!(a.num_recent_attesters(), 4) - } - - #[test] - fn test_rlp_serialization() { - let a = ActiveState { - height: 100, - randao: Sha256Digest::zero(), - ffg_voter_bitfield: Bitfield::new(), - recent_attesters: Vec::new(), - partial_crosslinks: Vec::new(), - total_skip_count: 99, - recent_proposers: Vec::new() - }; - let e = rlp::encode(&a); - assert_eq!(e.len(), 39); - assert_eq!(e[0], 100); - assert_eq!(e[1], 160); - assert_eq!(e[2..34], [0; 32]); - assert_eq!(e[34], 128); - assert_eq!(e[35], 192); - assert_eq!(e[36], 192); - assert_eq!(e[37], 99); - assert_eq!(e[38], 192); + assert_eq!(a.pending_attestations.len(), 0); + assert_eq!(a.recent_block_hashes.len(), 0); } } diff --git a/src/state/attestation_record.rs b/src/state/attestation_record.rs new file mode 100644 index 000000000..bb41694f9 --- /dev/null +++ b/src/state/attestation_record.rs @@ -0,0 +1,26 @@ +use super::utils::types::{ Hash256, Bitfield }; +use super::utils::bls::{ AggregateSignature }; + + +#[derive(Clone)] +pub struct AttestationRecord { + slot: u64, + shard_id: u16, + oblique_parent_hashes: Vec, + shard_block_hash: Hash256, + attester_bitfield: Bitfield, + aggregate_sig: Option, +} + +impl AttestationRecord { + pub fn zero() -> Self { + Self { + slot: 0, + shard_id: 0, + oblique_parent_hashes: vec![], + shard_block_hash: Hash256::zero(), + attester_bitfield: Bitfield::new(), + aggregate_sig: None, + } + } +} diff --git a/src/state/block.rs b/src/state/block.rs index adc028a12..201bc73ae 100644 --- a/src/state/block.rs +++ b/src/state/block.rs @@ -1,114 +1,56 @@ -use super::utils::types::{ Sha256Digest, Bitfield, StateHash }; +use super::utils::types::Hash256; use super::utils::bls::{ Signature, AggregateSignature, Keypair, PublicKey }; -use super::aggregate_vote::AggregateVote; -use super::rlp::{ RlpStream, Encodable } ; +use super::attestation_record::AttestationRecord; +use super::ssz; + +use std::hash::{ Hash, Hasher }; + +const SSZ_BLOCK_LENGTH: usize = 192; pub struct Block { - pub parent_hash: Sha256Digest, - pub skip_count: u64, - pub randao_reveal: Sha256Digest, - pub attestation_bitfield: Bitfield, - pub attestation_aggregate_sig: AggregateSignature, - pub shard_aggregate_votes: Vec, - pub main_chain_ref: Sha256Digest, - pub state_hash: StateHash, - pub sig: Option + pub parent_hash: Hash256, + pub slot_number: u64, + pub randao_reveal: Hash256, + pub attestations: Vec, + pub pow_chain_ref: Hash256, + pub active_state_root: Hash256, + pub crystallized_state_root: Hash256, } + impl Block { - pub fn new(parent_hash: Sha256Digest, - randao_reveal: Sha256Digest, - main_chain_ref: Sha256Digest, - state_hash: StateHash) -> Block { - Block { - parent_hash: parent_hash, - skip_count: 0, - randao_reveal: randao_reveal, - attestation_bitfield: Bitfield::new(), - attestation_aggregate_sig: AggregateSignature::new(), - shard_aggregate_votes: Vec::new(), - main_chain_ref: main_chain_ref, - state_hash: state_hash, - sig: None - } - } - - pub fn zero() -> Block { - Block { - parent_hash: Sha256Digest::zero(), - skip_count: 0, - randao_reveal: Sha256Digest::zero(), - attestation_bitfield: Bitfield::new(), - attestation_aggregate_sig: AggregateSignature::new(), - shard_aggregate_votes: vec![], - main_chain_ref: Sha256Digest::zero(), - state_hash: StateHash::zero(), - sig: None + pub fn zero() -> Self { + Self { + parent_hash: Hash256::zero(), + slot_number: 0, + randao_reveal: Hash256::zero(), + attestations: vec![], + pow_chain_ref: Hash256::zero(), + active_state_root: Hash256::zero(), + crystallized_state_root: Hash256::zero(), } } - /* - * Take a Block and covert it into an array of u8 for BLS signing - * or verfication. The `sig` field is purposefully omitted. - */ - pub fn encode_to_signable_message(&self) -> [u8; 9140] { - // Using biggest avg. block size from v2 spec - let mut message: [u8; 9140] = [0; 9140]; - - // Create the RLP vector - let mut s = RlpStream::new(); + /// Returns a Vec + pub fn ssz_encode_without_attestations(&self) + -> [u8; SSZ_BLOCK_LENGTH] + { + let mut s = ssz::SszStream::new(); s.append(&self.parent_hash); - s.append(&self.skip_count); + s.append(&self.slot_number); s.append(&self.randao_reveal); - s.append(&self.attestation_bitfield); - // s.append(&self.attestation_aggregate_sig); // TODO: RLP this - s.append_list(&self.shard_aggregate_votes); - s.append(&self.main_chain_ref); - // TODO: state hash serialization is probably incorrect. - s.append(&self.state_hash.crystallized_state); - s.append(&self.state_hash.active_state); - let rlp_vec = s.out(); - - // Parse the RLP vector into an array compatible with the BLS signer - let len = rlp_vec.len(); - message[..len].copy_from_slice(&rlp_vec[..len]); - message - } - - /* - * Sign the block with the given keypair. - */ - pub fn sig_sign(&mut self, keypair: &Keypair) { - let message = self.encode_to_signable_message(); - self.sig = Some(keypair.sign(&message)); - } - - /* - * Verify a block signature given some keypair. - */ - pub fn sig_verify(&self, pub_key: &PublicKey) -> bool { - let message = self.encode_to_signable_message(); - match &self.sig { - None => false, - Some(sig) => { - pub_key.verify(&message, &sig) - }, - } + s.append(&self.pow_chain_ref); + s.append(&self.active_state_root); + s.append(&self.crystallized_state_root); + let vec = s.drain(); + let mut encoded = [0; SSZ_BLOCK_LENGTH]; + encoded.copy_from_slice(&vec); encoded } } -impl Encodable for Block { - fn rlp_append(&self, s: &mut RlpStream) { - s.append(&self.parent_hash); - s.append(&self.skip_count); - s.append(&self.randao_reveal); - s.append(&self.attestation_bitfield); - // s.append(&self.attestation_aggregate_sig); // TODO: RLP this - s.append_list(&self.shard_aggregate_votes); - s.append(&self.main_chain_ref); - // TODO: state hash serialization is probably incorrect. - s.append(&self.state_hash.crystallized_state); - s.append(&self.state_hash.active_state); - // s.append(&self.sig); // TODO: RLP this +impl Hash for Block { + fn hash(&self, state: &mut H) { + let bytes = self.ssz_encode_without_attestations(); + bytes.hash(state); } } @@ -161,7 +103,7 @@ mod tests { } #[test] - fn test_rlp_serialization() { + fn test_ssz_serialization() { let b = Block { parent_hash: Sha256Digest::zero(), skip_count: 100, diff --git a/src/state/crosslink_record.rs b/src/state/crosslink_record.rs index ad94a3cec..fbeb4f43b 100644 --- a/src/state/crosslink_record.rs +++ b/src/state/crosslink_record.rs @@ -1,59 +1,30 @@ -use super::utils::types::Sha256Digest; -use super::rlp::{ RlpStream, Encodable }; +use super::utils::types::Hash256; -#[derive(Copy)] +#[derive(Clone)] pub struct CrosslinkRecord { - pub epoch: u64, - pub hash: Sha256Digest + pub dynasty: u64, + pub hash: Hash256, } impl CrosslinkRecord { - pub fn new(epoch: u64, hash: Sha256Digest) -> CrosslinkRecord { - CrosslinkRecord { - epoch: epoch, - hash: hash + /// Generates a new instance where `dynasty` and `hash` are both zero. + pub fn zero() -> Self { + Self { + dynasty: 0, + hash: Hash256::zero(), } } } -impl Clone for CrosslinkRecord { - fn clone(&self) -> CrosslinkRecord { *self } -} - -/* - * RLP Encoding - */ -impl Encodable for CrosslinkRecord { - fn rlp_append(&self, s: &mut RlpStream) { - s.append(&self.epoch); - s.append(&self.hash); - } -} - #[cfg(test)] mod tests { use super::super::rlp; use super::*; #[test] - fn test_new() { - let epoch = 1; - let hash = Sha256Digest::random(); - let c = CrosslinkRecord::new(epoch, hash); - assert_eq!(c.epoch, epoch); - assert_eq!(c.hash, hash); - } - - #[test] - fn test_rlp_serialization() { - let c = CrosslinkRecord { - epoch: 100, - hash: Sha256Digest::zero() - }; - let e = rlp::encode(&c); - assert_eq!(e.len(), 34); - assert_eq!(e[0], 100); - assert_eq!(e[1], 160); - assert_eq!(e[2..34], [0; 32]); + fn test_crosslink_record_zero() { + let c = CrosslinkRecord::zero(); + assert_eq!(c.dynasty, 0); + assert!(c.hash.is_zero()); } } diff --git a/src/state/crystallized_state.rs b/src/state/crystallized_state.rs index 00e5c2304..c799ad6bc 100644 --- a/src/state/crystallized_state.rs +++ b/src/state/crystallized_state.rs @@ -1,129 +1,66 @@ -use super::utils::types::{ Sha256Digest, Blake2sDigest }; use super::validator_record::ValidatorRecord; use super::crosslink_record::CrosslinkRecord; -use super::rlp::{ RlpStream, Encodable }; -use super::rlp::encode as rlp_encode; +use super::shard_and_committee::ShardAndCommittee; use super::ethereum_types::U256; -use super::blake2::{ Blake2s, Digest }; +use super::utils::types::{ Hash256 }; + #[derive(Clone)] pub struct CrystallizedState { - pub active_validators: Vec, - pub queued_validators: Vec, - pub exited_validators: Vec, - pub current_shuffling: Vec, // TODO: should be u24 - pub current_epoch: u64, - pub last_justified_epoch: u64, - pub last_finalized_epoch: u64, - pub dynasty: u64, - pub next_shard: u16, - pub current_checkpoint: Sha256Digest, + pub validators: Vec, + pub epoch_number: u64, + pub indicies_for_heights: Vec, + pub last_justified_slot: u64, + pub justified_streak: u16, + pub last_finalized_slot: u64, + pub current_dynasty: u64, + pub crosslinking_shard_start: u16, pub crosslink_records: Vec, pub total_deposits: U256, + pub dynasty_seed: Hash256, + pub dynasty_seed_last_reset: u64, } impl CrystallizedState { - // Returns a new instance with all values set to zero. + /// Returns a new instance where all fields are either zero or an + /// empty vector. pub fn zero() -> Self { Self { - active_validators: Vec::new(), - queued_validators: Vec::new(), - exited_validators: Vec::new(), - current_shuffling: Vec::new(), - current_epoch: 0, - last_justified_epoch: 0, - last_finalized_epoch: 0, - dynasty: 0, - next_shard: 0, - current_checkpoint: Sha256Digest::zero(), - crosslink_records: Vec::new(), + validators: vec![], + epoch_number: 0, + indicies_for_heights: vec![], + last_justified_slot: 0, + justified_streak: 0, + last_finalized_slot: 0, + current_dynasty: 0, + crosslinking_shard_start: 0, + crosslink_records: vec![], total_deposits: U256::zero(), + dynasty_seed: Hash256::zero(), + dynasty_seed_last_reset: 0, } } - - 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() - } - - pub fn num_queued_validators(&self) -> usize { - self.queued_validators.len() - } - - pub fn num_exited_validators(&self) -> usize { - self.exited_validators.len() - } - - pub fn num_crosslink_records(&self) -> usize { - self.crosslink_records.len() - - } - - pub fn blake2s_hash(&self) -> Blake2sDigest { - let mut hasher = Blake2s::new(); - hasher.input(&rlp_encode(self).into_vec()); - let mut digest = Blake2sDigest::new(); - digest.clone_from_slice(hasher.result().as_slice()); - digest - } -} - -/* - * RLP Encoding - */ -impl Encodable for CrystallizedState { - fn rlp_append(&self, s: &mut RlpStream) { - s.append_list(&self.active_validators); - s.append_list(&self.queued_validators); - s.append_list(&self.exited_validators); - s.append_list(&self.current_shuffling); - s.append(&self.current_epoch); - s.append(&self.last_justified_epoch); - s.append(&self.last_finalized_epoch); - s.append(&self.dynasty); - s.append(&self.next_shard); - s.append(&self.current_checkpoint); - s.append_list(&self.crosslink_records); - s.append(&self.total_deposits); - } } #[cfg(test)] mod tests { - use super::super::rlp; use super::*; #[test] - fn test_rlp_serialization() { - let a = CrystallizedState { - active_validators: Vec::new(), - queued_validators: Vec::new(), - exited_validators: Vec::new(), - current_shuffling: Vec::new(), - current_epoch: 10, - last_justified_epoch: 8, - last_finalized_epoch: 2, - dynasty: 3, - next_shard: 12, - current_checkpoint: Sha256Digest::zero(), - crosslink_records: Vec::new(), - total_deposits: U256::zero(), - }; - let e = rlp::encode(&a); - assert_eq!(e.len(), 44); - assert_eq!(e[0..4], [192; 4]); - assert_eq!(e[4], 10); - assert_eq!(e[5], 8); - assert_eq!(e[6], 2); - assert_eq!(e[7], 3); - assert_eq!(e[8], 12); - assert_eq!(e[9], 160); - assert_eq!(e[10..42], [0; 32]); - assert_eq!(e[42], 192); - assert_eq!(e[43], 128); + fn test_cry_state_zero() { + let c = CrystallizedState::zero(); + assert_eq!(c.validators.len(), 0); + assert_eq!(c.epoch_number, 0); + assert_eq!(c.indicies_for_heights.len(), 0); + assert_eq!(c.last_justified_slot, 0); + assert_eq!(c.justified_streak, 0); + assert_eq!(c.last_finalized_slot, 0); + assert_eq!(c.current_dynasty, 0); + assert_eq!(c.crosslinking_shard_start, 0); + assert_eq!(c.crosslink_records.len(), 0); + assert!(c.total_deposits.is_zero()); + assert!(c.dynasty_seed.is_zero()); + assert_eq!(c.dynasty_seed_last_reset, 0); } + } diff --git a/src/state/mod.rs b/src/state/mod.rs index 72830910f..4f19dc66a 100644 --- a/src/state/mod.rs +++ b/src/state/mod.rs @@ -2,17 +2,20 @@ extern crate rlp; extern crate ethereum_types; extern crate blake2; extern crate bytes; +extern crate ssz; use super::utils; -use super::pubkeystore; +// use super::pubkeystore; pub mod active_state; +pub mod attestation_record; pub mod crystallized_state; pub mod config; -pub mod aggregate_vote; +// pub mod aggregate_vote; pub mod block; pub mod crosslink_record; -pub mod partial_crosslink_record; -pub mod recent_proposer_record; -pub mod transition; +// pub mod partial_crosslink_record; +// pub mod recent_proposer_record; +// pub mod transition; +pub mod shard_and_committee; pub mod validator_record; diff --git a/src/state/shard_and_committee.rs b/src/state/shard_and_committee.rs new file mode 100644 index 000000000..81e83be30 --- /dev/null +++ b/src/state/shard_and_committee.rs @@ -0,0 +1,28 @@ +#[derive(Clone)] +pub struct ShardAndCommittee { + shard_id: u16, + committee: Vec +} + +impl ShardAndCommittee { + /// Returns a new instance where the `shard_id` is zero and the + /// committee is an empty vector. + pub fn zero() -> Self { + Self { + shard_id: 0, + committee: vec![], + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_shard_and_committee_zero() { + let s = CrystallizedState::zero(); + assert_eq!(s.shard_id, 0); + assert_eq!(s.committee.len(), 0); + } +} diff --git a/src/state/validator_record.rs b/src/state/validator_record.rs index 28250af9b..b943f7973 100644 --- a/src/state/validator_record.rs +++ b/src/state/validator_record.rs @@ -1,8 +1,7 @@ extern crate rand; -use super::utils::types::{ Sha256Digest, Address, U256 }; +use super::utils::types::{ Hash256, Address, U256 }; use super::utils::bls::{ PublicKey, Keypair }; -use super::rlp::{ RlpStream, Encodable }; use self::rand::thread_rng; @@ -10,43 +9,17 @@ pub struct ValidatorRecord { pub pubkey: PublicKey, pub withdrawal_shard: u16, pub withdrawal_address: Address, - pub randao_commitment: Sha256Digest, + pub randao_commitment: Hash256, pub balance: U256, - pub switch_dynasty: u64 + pub start_dynasty: u64, + pub end_dynasty: u64, } impl ValidatorRecord { - pub fn new(pubkey: PublicKey, - withdrawal_shard: u16, - withdrawal_address: Address, - randao_commitment: Sha256Digest, - balance: U256, - switch_dynasty: u64) - -> Self - { - Self { - pubkey, - withdrawal_shard, - withdrawal_address, - randao_commitment, - balance, - switch_dynasty - } - } - - pub fn zero_with_thread_rand_pub_key() -> Self { - let mut rng = thread_rng(); - let keypair = Keypair::generate(&mut rng); - Self { - pubkey: keypair.public, - withdrawal_shard: 0, - withdrawal_address: Address::zero(), - randao_commitment: Sha256Digest::zero(), - balance: U256::zero(), - switch_dynasty: 0 - } - } - + /// Generates a new instance where the keypair is generated using + /// `rand::thread_rng` entropy and all other fields are set to zero. + /// + /// Returns the new instance and new keypair. pub fn zero_with_thread_rand_keypair() -> (Self, Keypair) { let mut rng = thread_rng(); let keypair = Keypair::generate(&mut rng); @@ -54,9 +27,10 @@ impl ValidatorRecord { pubkey: keypair.public.clone(), withdrawal_shard: 0, withdrawal_address: Address::zero(), - randao_commitment: Sha256Digest::zero(), + randao_commitment: Hash256::zero(), balance: U256::zero(), - switch_dynasty: 0 + start_dynasty: 0, + end_dynasty: 0, }; (s, keypair) } @@ -72,20 +46,6 @@ impl Clone for ValidatorRecord { } } -/* - * RLP Encoding - */ -impl Encodable for ValidatorRecord { - fn rlp_append(&self, s: &mut RlpStream) { - // s.append(&self.pubkey); // TODO: serialize this - s.append(&self.withdrawal_shard); - s.append(&self.withdrawal_address); - s.append(&self.randao_commitment); - s.append(&self.balance); - s.append(&self.switch_dynasty); - } -} - #[cfg(test)] mod tests { @@ -97,50 +57,14 @@ mod tests { utils::test_helpers::get_dangerous_test_keypair; #[test] - fn test_new() { - let keypair = get_dangerous_test_keypair();; - let withdrawal_shard = 1; - let withdrawal_address = Address::random(); - let randao_commitment = Sha256Digest::random(); - let balance = U256::from(100); - let switch_dynasty = 10; - - let v = ValidatorRecord::new( - keypair.public, - withdrawal_shard, - withdrawal_address, - randao_commitment, - balance, - switch_dynasty); - // TODO: figure out how to compare keys - // assert_eq!(v.pubkey, keypair.public); - assert_eq!(v.withdrawal_shard, withdrawal_shard); - assert_eq!(v.withdrawal_address, withdrawal_address); - assert_eq!(v.randao_commitment, randao_commitment); - assert_eq!(v.balance, balance); - assert_eq!(v.switch_dynasty, switch_dynasty); - } - - #[test] - fn test_rlp_serialization() { - let keypair = get_dangerous_test_keypair(); - let v = ValidatorRecord { - pubkey: keypair.public, - withdrawal_shard: 100, - withdrawal_address: Address::zero(), - randao_commitment: Sha256Digest::zero(), - balance: U256::from(120), - switch_dynasty: 30 - }; - let e = rlp::encode(&v); - assert_eq!(e.len(), 57); // TODO: fix when pubkey is serialized - // TODO: test for serialized pubkey - assert_eq!(e[0], 100); - assert_eq!(e[1], 148); - assert_eq!(e[2..22], [0; 20]); - assert_eq!(e[22], 160); - assert_eq!(e[23..55], [0; 32]); - assert_eq!(e[55], 120); - assert_eq!(e[56], 30); + fn test_validator_record_zero_rand_keypair() { + let (v, kp) = ValidatorRecord::zero_with_thread_rand_keypair(); + // TODO: check keys + assert_eq!(v.withdrawal_shard, 0); + assert!(v.withdrawal_address.is_zero()); + assert!(v.randao_commitment.is_zero()); + assert!(v.balance.is_zero()); + assert_eq!(v.start_dynasty, 0); + assert_eq!(v.end_dynasty, 0); } } diff --git a/src/utils/types.rs b/src/utils/types.rs index 8ed504874..bf6546698 100644 --- a/src/utils/types.rs +++ b/src/utils/types.rs @@ -11,6 +11,7 @@ pub use super::ethereum_types::U256; // which is bad. Make the compiler think they're incompatible. pub type Sha256Digest = H256; pub type Blake2sDigest = H256; +pub type Hash256 = H256; pub type Address = H160;