diff --git a/Cargo.toml b/Cargo.toml index 7490c3d000..e14bd336c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,6 @@ members = [ "beacon_chain/chain", "beacon_chain/naive_fork_choice", "beacon_chain/spec", - "beacon_chain/state-transition", "beacon_chain/types", "beacon_chain/utils/bls", "beacon_chain/utils/boolean-bitfield", @@ -45,7 +44,6 @@ members = [ "beacon_chain/utils/honey-badger-split", "beacon_chain/utils/slot-clock", "beacon_chain/utils/ssz", - "beacon_chain/utils/ssz_helpers", "beacon_chain/utils/vec_shuffle", "beacon_chain/validator_change", "beacon_chain/validator_induction", diff --git a/beacon_chain/attestation_validation/Cargo.toml b/beacon_chain/attestation_validation/Cargo.toml index b2f84fd1df..bc357a26a2 100644 --- a/beacon_chain/attestation_validation/Cargo.toml +++ b/beacon_chain/attestation_validation/Cargo.toml @@ -9,5 +9,4 @@ bls = { path = "../utils/bls" } db = { path = "../../lighthouse/db" } hashing = { path = "../utils/hashing" } ssz = { path = "../utils/ssz" } -ssz_helpers = { path = "../utils/ssz_helpers" } types = { path = "../types" } diff --git a/beacon_chain/attestation_validation/src/lib.rs b/beacon_chain/attestation_validation/src/lib.rs index 36fca97401..254d6134e0 100644 --- a/beacon_chain/attestation_validation/src/lib.rs +++ b/beacon_chain/attestation_validation/src/lib.rs @@ -2,7 +2,6 @@ extern crate bls; extern crate db; extern crate hashing; extern crate ssz; -extern crate ssz_helpers; extern crate types; #[macro_use] diff --git a/beacon_chain/chain/Cargo.toml b/beacon_chain/chain/Cargo.toml index 77555d9a90..31af587004 100644 --- a/beacon_chain/chain/Cargo.toml +++ b/beacon_chain/chain/Cargo.toml @@ -9,8 +9,6 @@ bls = { path = "../utils/bls" } db = { path = "../../lighthouse/db" } naive_fork_choice = { path = "../naive_fork_choice" } ssz = { path = "../utils/ssz" } -ssz_helpers = { path = "../utils/ssz_helpers" } -state-transition = { path = "../state-transition" } types = { path = "../types" } validator_induction = { path = "../validator_induction" } validator_shuffling = { path = "../validator_shuffling" } diff --git a/beacon_chain/chain/src/lib.rs b/beacon_chain/chain/src/lib.rs index 7976b34a1e..8eb076d478 100644 --- a/beacon_chain/chain/src/lib.rs +++ b/beacon_chain/chain/src/lib.rs @@ -1,8 +1,6 @@ extern crate db; extern crate naive_fork_choice; extern crate ssz; -extern crate ssz_helpers; -extern crate state_transition; extern crate types; extern crate validator_induction; extern crate validator_shuffling; @@ -11,7 +9,6 @@ mod block_processing; mod genesis; mod maps; mod stores; -mod transition; use db::ClientDB; use crate::genesis::{genesis_states, Error as GenesisError}; diff --git a/beacon_chain/chain/src/transition.rs b/beacon_chain/chain/src/transition.rs deleted file mode 100644 index 7598f25175..0000000000 --- a/beacon_chain/chain/src/transition.rs +++ /dev/null @@ -1,29 +0,0 @@ -use super::BeaconChain; -use db::ClientDB; -use state_transition::{extend_active_state, StateTransitionError}; -use types::{ActiveState, BeaconBlock, CrystallizedState, Hash256}; - -impl BeaconChain -where - T: ClientDB + Sized, -{ - pub(crate) fn transition_states( - &self, - act_state: &ActiveState, - cry_state: &CrystallizedState, - block: &BeaconBlock, - block_hash: &Hash256, - ) -> Result<(ActiveState, Option), StateTransitionError> { - let state_recalc_distance = block - .slot - .checked_sub(cry_state.last_state_recalculation_slot) - .ok_or(StateTransitionError::BlockSlotBeforeRecalcSlot)?; - - if state_recalc_distance >= u64::from(self.config.cycle_length) { - panic!("Not implemented!") - } else { - let new_act_state = extend_active_state(act_state, block, block_hash)?; - Ok((new_act_state, None)) - } - } -} diff --git a/beacon_chain/state-transition/Cargo.toml b/beacon_chain/state-transition/Cargo.toml deleted file mode 100644 index 12618a6cee..0000000000 --- a/beacon_chain/state-transition/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "state-transition" -version = "0.1.0" -authors = ["Paul Hauner "] -edition = "2018" - -[dependencies] -types = { path = "../types" } diff --git a/beacon_chain/state-transition/src/lib.rs b/beacon_chain/state-transition/src/lib.rs deleted file mode 100644 index ee8d841e60..0000000000 --- a/beacon_chain/state-transition/src/lib.rs +++ /dev/null @@ -1,194 +0,0 @@ -extern crate types; - -use types::{ActiveState, BeaconBlock, Hash256}; - -#[derive(Debug, PartialEq)] -pub enum StateTransitionError { - BlockSlotBeforeRecalcSlot, - InvalidParentHashes, - DBError(String), -} - -pub fn extend_active_state( - act_state: &ActiveState, - block: &BeaconBlock, - block_hash: &Hash256, -) -> Result { - /* - * Extend the pending attestations in the active state with the new attestations included - * in the block. - * - * Using the concat method to avoid reallocations. - */ - let pending_attestations = - [&act_state.pending_attestations[..], &block.attestations[..]].concat(); - - /* - * Extend the pending specials in the active state with the new specials included in the - * block. - * - * Using the concat method to avoid reallocations. - */ - let pending_specials = [&act_state.pending_specials[..], &block.specials[..]].concat(); - - /* - * Update the active state recent_block_hashes: - * - * - Drop the hash from the earliest position. - * - Push the block_hash into the latest position. - * - * Using the concat method to avoid reallocations. - */ - let (_first_hash, last_hashes) = act_state - .recent_block_hashes - .split_first() - .ok_or(StateTransitionError::InvalidParentHashes)?; - let new_hash = &[block_hash.clone()]; - let recent_block_hashes = [&last_hashes, &new_hash[..]].concat(); - - /* - * The new `randao_mix` is set to the XOR of the previous active state randao mix and the - * randao reveal in this block. - */ - let randao_mix = act_state.randao_mix ^ block.randao_reveal; - - Ok(ActiveState { - pending_attestations, - pending_specials, - recent_block_hashes, - randao_mix, - }) -} - -#[cfg(test)] -mod tests { - use super::*; - use types::SpecialRecord; - - fn empty_active_state() -> ActiveState { - ActiveState { - pending_attestations: vec![], - pending_specials: vec![], - recent_block_hashes: vec![], - randao_mix: Hash256::zero(), - } - } - - #[test] - fn test_extend_active_state_minimal() { - let mut act_state = empty_active_state(); - - let parent_hash = Hash256::from("parent_hash".as_bytes()); - act_state.recent_block_hashes = vec![parent_hash]; - - let block = BeaconBlock::zero(); - let block_hash = Hash256::from("block_hash".as_bytes()); - - let new_act_state = extend_active_state(&act_state, &block, &block_hash).unwrap(); - - assert_eq!(new_act_state.pending_attestations, vec![]); - assert_eq!(new_act_state.pending_specials, vec![]); - assert_eq!(new_act_state.recent_block_hashes, vec![block_hash]); - assert_eq!(new_act_state.randao_mix, Hash256::zero()); - } - - #[test] - fn test_extend_active_state_specials() { - let mut act_state = empty_active_state(); - - let parent_hash = Hash256::from("parent_hash".as_bytes()); - act_state.recent_block_hashes = vec![parent_hash]; - - let mut block = BeaconBlock::zero(); - let special = SpecialRecord { - kind: 0, - data: vec![42, 42], - }; - - block.specials.push(special.clone()); - - let block_hash = Hash256::from("block_hash".as_bytes()); - - let new_act_state = extend_active_state(&act_state, &block, &block_hash).unwrap(); - - assert_eq!(new_act_state.pending_attestations, vec![]); - assert_eq!(new_act_state.pending_specials, vec![special.clone()]); - assert_eq!(new_act_state.recent_block_hashes, vec![block_hash]); - assert_eq!(new_act_state.randao_mix, Hash256::zero()); - - let new_new_act_state = extend_active_state(&new_act_state, &block, &block_hash).unwrap(); - - assert_eq!(new_new_act_state.pending_attestations, vec![]); - assert_eq!( - new_new_act_state.pending_specials, - vec![special.clone(), special.clone()] - ); - assert_eq!(new_new_act_state.recent_block_hashes, vec![block_hash]); - assert_eq!(new_new_act_state.randao_mix, Hash256::zero()); - } - - #[test] - fn test_extend_active_state_empty_recent_block_hashes() { - let act_state = empty_active_state(); - - let block = BeaconBlock::zero(); - - let block_hash = Hash256::from("block_hash".as_bytes()); - - let result = extend_active_state(&act_state, &block, &block_hash); - - assert_eq!(result, Err(StateTransitionError::InvalidParentHashes)); - } - - #[test] - fn test_extend_active_recent_block_hashes() { - let mut act_state = empty_active_state(); - - let parent_hashes = vec![ - Hash256::from("one".as_bytes()), - Hash256::from("two".as_bytes()), - Hash256::from("three".as_bytes()), - ]; - act_state.recent_block_hashes = parent_hashes.clone(); - - let block = BeaconBlock::zero(); - - let block_hash = Hash256::from("four".as_bytes()); - - let new_act_state = extend_active_state(&act_state, &block, &block_hash).unwrap(); - - assert_eq!(new_act_state.pending_attestations, vec![]); - assert_eq!(new_act_state.pending_specials, vec![]); - assert_eq!( - new_act_state.recent_block_hashes, - vec![ - Hash256::from("two".as_bytes()), - Hash256::from("three".as_bytes()), - Hash256::from("four".as_bytes()), - ] - ); - assert_eq!(new_act_state.randao_mix, Hash256::zero()); - } - - #[test] - fn test_extend_active_state_randao() { - let mut act_state = empty_active_state(); - - let parent_hash = Hash256::from("parent_hash".as_bytes()); - act_state.recent_block_hashes = vec![parent_hash]; - - act_state.randao_mix = Hash256::from(0b00000000); - - let mut block = BeaconBlock::zero(); - block.randao_reveal = Hash256::from(0b00000001); - - let block_hash = Hash256::from("block_hash".as_bytes()); - - let new_act_state = extend_active_state(&act_state, &block, &block_hash).unwrap(); - - assert_eq!(new_act_state.pending_attestations, vec![]); - assert_eq!(new_act_state.pending_specials, vec![]); - assert_eq!(new_act_state.recent_block_hashes, vec![block_hash]); - assert_eq!(new_act_state.randao_mix, Hash256::from(0b00000001)); - } -} diff --git a/beacon_chain/types/Cargo.toml b/beacon_chain/types/Cargo.toml index 7b1f0d4409..f497022807 100644 --- a/beacon_chain/types/Cargo.toml +++ b/beacon_chain/types/Cargo.toml @@ -8,5 +8,5 @@ edition = "2018" bls = { path = "../utils/bls" } boolean-bitfield = { path = "../utils/boolean-bitfield" } ethereum-types = "0.4.0" -rand = "0.3" +rand = "0.5.5" ssz = { path = "../utils/ssz" } diff --git a/beacon_chain/types/src/attestation.rs b/beacon_chain/types/src/attestation.rs index 2c2015cd3c..7a94eee51c 100644 --- a/beacon_chain/types/src/attestation.rs +++ b/beacon_chain/types/src/attestation.rs @@ -1,14 +1,8 @@ -use super::attestation_data::SSZ_ATTESTION_DATA_LENGTH; -use super::bls::{AggregateSignature, BLS_AGG_SIG_BYTE_SIZE}; -use super::ssz::{decode_ssz_list, Decodable, DecodeError, Encodable, SszStream, LENGTH_BYTES}; +use super::bls::AggregateSignature; +use super::ssz::{Decodable, DecodeError, Encodable, SszStream}; use super::{AttestationData, Bitfield}; - -pub const MIN_SSZ_ATTESTION_RECORD_LENGTH: usize = { - SSZ_ATTESTION_DATA_LENGTH + // data - 5 + // participation_bitfield (assuming 1 byte of bitfield) - 5 + // custody_bitfield (assuming 1 byte of bitfield) - LENGTH_BYTES + BLS_AGG_SIG_BYTE_SIZE // aggregate sig -}; +use crate::test_utils::TestRandom; +use rand::RngCore; #[derive(Debug, Clone, PartialEq)] pub struct Attestation { @@ -23,7 +17,7 @@ impl Encodable for Attestation { s.append(&self.data); s.append(&self.participation_bitfield); s.append(&self.custody_bitfield); - s.append_vec(&self.aggregate_sig.as_bytes()); + s.append(&self.aggregate_sig); } } @@ -32,9 +26,7 @@ impl Decodable for Attestation { let (data, i) = AttestationData::ssz_decode(bytes, i)?; let (participation_bitfield, i) = Bitfield::ssz_decode(bytes, i)?; let (custody_bitfield, i) = Bitfield::ssz_decode(bytes, i)?; - let (agg_sig_bytes, i) = decode_ssz_list(bytes, i)?; - let aggregate_sig = - AggregateSignature::from_bytes(&agg_sig_bytes).map_err(|_| DecodeError::TooShort)?; // also could be TooLong + let (aggregate_sig, i) = AggregateSignature::ssz_decode(bytes, i)?; let attestation_record = Self { data, @@ -57,30 +49,31 @@ impl Attestation { } } +impl TestRandom for Attestation { + fn random_for_test(rng: &mut T) -> Self { + Self { + data: <_>::random_for_test(rng), + participation_bitfield: <_>::random_for_test(rng), + custody_bitfield: <_>::random_for_test(rng), + aggregate_sig: <_>::random_for_test(rng), + } + } +} + #[cfg(test)] mod tests { use super::super::ssz::ssz_encode; use super::*; + use crate::test_utils::TestRandom; + use rand::{prng::XorShiftRng, SeedableRng}; #[test] - pub fn test_attestation_record_min_ssz_length() { - let ar = Attestation::zero(); - let ssz = ssz_encode(&ar); + pub fn test_ssz_round_trip() { + let mut rng = XorShiftRng::from_seed([42; 16]); + let original = Attestation::random_for_test(&mut rng); - assert_eq!(ssz.len(), MIN_SSZ_ATTESTION_RECORD_LENGTH); - } - - #[test] - pub fn test_attestation_record_ssz_round_trip() { - let original = Attestation { - data: AttestationData::zero(), - participation_bitfield: Bitfield::from_bytes(&vec![17; 42][..]), - custody_bitfield: Bitfield::from_bytes(&vec![18; 12][..]), - aggregate_sig: AggregateSignature::new(), - }; - - let ssz = ssz_encode(&original); - let (decoded, _) = Attestation::ssz_decode(&ssz, 0).unwrap(); + let bytes = ssz_encode(&original); + let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap(); assert_eq!(original, decoded); } diff --git a/beacon_chain/types/src/attestation_data.rs b/beacon_chain/types/src/attestation_data.rs index d75c43b9db..616c52d790 100644 --- a/beacon_chain/types/src/attestation_data.rs +++ b/beacon_chain/types/src/attestation_data.rs @@ -1,5 +1,7 @@ use super::ssz::{Decodable, DecodeError, Encodable, SszStream}; use super::Hash256; +use crate::test_utils::TestRandom; +use rand::RngCore; pub const SSZ_ATTESTION_DATA_LENGTH: usize = { 8 + // slot @@ -83,27 +85,35 @@ impl Decodable for AttestationData { } } +impl TestRandom for AttestationData { + fn random_for_test(rng: &mut T) -> Self { + Self { + slot: <_>::random_for_test(rng), + shard: <_>::random_for_test(rng), + beacon_block_hash: <_>::random_for_test(rng), + epoch_boundary_hash: <_>::random_for_test(rng), + shard_block_hash: <_>::random_for_test(rng), + latest_crosslink_hash: <_>::random_for_test(rng), + justified_slot: <_>::random_for_test(rng), + justified_block_hash: <_>::random_for_test(rng), + } + } +} + #[cfg(test)] mod tests { use super::super::ssz::ssz_encode; use super::*; + use crate::test_utils::TestRandom; + use rand::{prng::XorShiftRng, SeedableRng}; #[test] - pub fn test_attestation_record_ssz_round_trip() { - let original = AttestationData { - slot: 42, - shard: 16, - beacon_block_hash: Hash256::from("beacon".as_bytes()), - epoch_boundary_hash: Hash256::from("epoch".as_bytes()), - shard_block_hash: Hash256::from("shard".as_bytes()), - latest_crosslink_hash: Hash256::from("xlink".as_bytes()), - justified_slot: 8, - justified_block_hash: Hash256::from("justified".as_bytes()), - }; + pub fn test_ssz_round_trip() { + let mut rng = XorShiftRng::from_seed([42; 16]); + let original = AttestationData::random_for_test(&mut rng); - let ssz = ssz_encode(&original); - - let (decoded, _) = AttestationData::ssz_decode(&ssz, 0).unwrap(); + let bytes = ssz_encode(&original); + let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap(); assert_eq!(original, decoded); } diff --git a/beacon_chain/types/src/beacon_block.rs b/beacon_chain/types/src/beacon_block.rs index 35ad18497f..df8f939e33 100644 --- a/beacon_chain/types/src/beacon_block.rs +++ b/beacon_chain/types/src/beacon_block.rs @@ -1,142 +1,86 @@ -use super::attestation::Attestation; -use super::special_record::SpecialRecord; use super::ssz::{Decodable, DecodeError, Encodable, SszStream}; -use super::Hash256; +use super::{BeaconBlockBody, Hash256}; +use crate::test_utils::TestRandom; +use bls::AggregateSignature; +use rand::RngCore; -pub const MIN_SSZ_BLOCK_LENGTH: usize = { - 8 + // slot - 32 + // randao_reveal - 32 + // pow_chain_reference - 4 + // ancestor hashes (assuming empty) - 32 + // active_state_root - 32 + // crystallized_state_root - 4 + // attestations (assuming empty) - 4 // specials (assuming empty) -}; -pub const MAX_SSZ_BLOCK_LENGTH: usize = MIN_SSZ_BLOCK_LENGTH + (1 << 24); - -#[derive(Debug, PartialEq, Clone, Default)] +#[derive(Debug, PartialEq, Clone)] pub struct BeaconBlock { pub slot: u64, + pub parent_root: Hash256, + pub state_root: Hash256, pub randao_reveal: Hash256, - pub pow_chain_reference: Hash256, - pub ancestor_hashes: Vec, - pub active_state_root: Hash256, - pub crystallized_state_root: Hash256, - pub attestations: Vec, - pub specials: Vec, -} - -impl BeaconBlock { - pub fn zero() -> Self { - Self { - slot: 0, - randao_reveal: Hash256::zero(), - pow_chain_reference: Hash256::zero(), - ancestor_hashes: vec![], - active_state_root: Hash256::zero(), - crystallized_state_root: Hash256::zero(), - attestations: vec![], - specials: vec![], - } - } - - /// Return a reference to `ancestor_hashes[0]`. - /// - /// The first hash in `ancestor_hashes` is the parent of the block. - pub fn parent_hash(&self) -> Option<&Hash256> { - self.ancestor_hashes.get(0) - } + pub candidate_pow_receipt_root: Hash256, + pub signature: AggregateSignature, + pub body: BeaconBlockBody, } impl Encodable for BeaconBlock { fn ssz_append(&self, s: &mut SszStream) { s.append(&self.slot); + s.append(&self.parent_root); + s.append(&self.state_root); s.append(&self.randao_reveal); - s.append(&self.pow_chain_reference); - s.append_vec(&self.ancestor_hashes); - s.append(&self.active_state_root); - s.append(&self.crystallized_state_root); - s.append_vec(&self.attestations); - s.append_vec(&self.specials); + s.append(&self.candidate_pow_receipt_root); + s.append(&self.signature); + s.append(&self.body); } } impl Decodable for BeaconBlock { fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { - let (slot, i) = u64::ssz_decode(bytes, i)?; - let (randao_reveal, i) = Hash256::ssz_decode(bytes, i)?; - let (pow_chain_reference, i) = Hash256::ssz_decode(bytes, i)?; - let (ancestor_hashes, i) = Decodable::ssz_decode(bytes, i)?; - let (active_state_root, i) = Hash256::ssz_decode(bytes, i)?; - let (crystallized_state_root, i) = Hash256::ssz_decode(bytes, i)?; - let (attestations, i) = Decodable::ssz_decode(bytes, i)?; - let (specials, i) = Decodable::ssz_decode(bytes, i)?; - let block = BeaconBlock { - slot, - randao_reveal, - pow_chain_reference, - ancestor_hashes, - active_state_root, - crystallized_state_root, - attestations, - specials, - }; - Ok((block, i)) + let (slot, i) = <_>::ssz_decode(bytes, i)?; + let (parent_root, i) = <_>::ssz_decode(bytes, i)?; + let (state_root, i) = <_>::ssz_decode(bytes, i)?; + let (randao_reveal, i) = <_>::ssz_decode(bytes, i)?; + let (candidate_pow_receipt_root, i) = <_>::ssz_decode(bytes, i)?; + let (signature, i) = <_>::ssz_decode(bytes, i)?; + let (body, i) = <_>::ssz_decode(bytes, i)?; + + Ok(( + Self { + slot, + parent_root, + state_root, + randao_reveal, + candidate_pow_receipt_root, + signature, + body, + }, + i, + )) + } +} + +impl TestRandom for BeaconBlock { + fn random_for_test(rng: &mut T) -> Self { + Self { + slot: <_>::random_for_test(rng), + parent_root: <_>::random_for_test(rng), + state_root: <_>::random_for_test(rng), + randao_reveal: <_>::random_for_test(rng), + candidate_pow_receipt_root: <_>::random_for_test(rng), + signature: <_>::random_for_test(rng), + body: <_>::random_for_test(rng), + } } } #[cfg(test)] mod tests { + use super::super::ssz::ssz_encode; use super::*; + use crate::test_utils::TestRandom; + use rand::{prng::XorShiftRng, SeedableRng}; #[test] - fn test_block_zero() { - let b = BeaconBlock::zero(); - assert_eq!(b.slot, 0); - assert!(b.randao_reveal.is_zero()); - assert!(b.pow_chain_reference.is_zero()); - assert_eq!(b.ancestor_hashes, vec![]); - assert!(b.active_state_root.is_zero()); - assert!(b.crystallized_state_root.is_zero()); - assert_eq!(b.attestations.len(), 0); - assert_eq!(b.specials.len(), 0); - } + pub fn test_ssz_round_trip() { + let mut rng = XorShiftRng::from_seed([42; 16]); + let original = BeaconBlock::random_for_test(&mut rng); - #[test] - pub fn test_block_ssz_encode_decode() { - let mut b = BeaconBlock::zero(); - b.ancestor_hashes = vec![Hash256::zero(); 32]; + let bytes = ssz_encode(&original); + let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap(); - let mut ssz_stream = SszStream::new(); - ssz_stream.append(&b); - let ssz = ssz_stream.drain(); - - let (b_decoded, _) = BeaconBlock::ssz_decode(&ssz, 0).unwrap(); - - assert_eq!(b, b_decoded); - } - - #[test] - pub fn test_block_min_ssz_length() { - let b = BeaconBlock::zero(); - - let mut ssz_stream = SszStream::new(); - ssz_stream.append(&b); - let ssz = ssz_stream.drain(); - - assert_eq!(ssz.len(), MIN_SSZ_BLOCK_LENGTH); - } - - #[test] - pub fn test_block_parent_hash() { - let mut b = BeaconBlock::zero(); - b.ancestor_hashes = vec![ - Hash256::from("cats".as_bytes()), - Hash256::from("dogs".as_bytes()), - Hash256::from("birds".as_bytes()), - ]; - - assert_eq!(b.parent_hash().unwrap(), &Hash256::from("cats".as_bytes())); + assert_eq!(original, decoded); } } diff --git a/beacon_chain/types/src/beacon_block_body.rs b/beacon_chain/types/src/beacon_block_body.rs index e69de29bb2..f5ac223432 100644 --- a/beacon_chain/types/src/beacon_block_body.rs +++ b/beacon_chain/types/src/beacon_block_body.rs @@ -0,0 +1,75 @@ +use super::ssz::{Decodable, DecodeError, Encodable, SszStream}; +use super::{Attestation, CasperSlashing, Deposit, Exit, ProposerSlashing}; +use crate::test_utils::TestRandom; +use rand::RngCore; + +#[derive(Debug, PartialEq, Clone, Default)] +pub struct BeaconBlockBody { + pub proposer_slashings: Vec, + pub casper_slashings: Vec, + pub attestations: Vec, + pub deposits: Vec, + pub exits: Vec, +} + +impl Encodable for BeaconBlockBody { + fn ssz_append(&self, s: &mut SszStream) { + s.append_vec(&self.proposer_slashings); + s.append_vec(&self.casper_slashings); + s.append_vec(&self.attestations); + s.append_vec(&self.deposits); + s.append_vec(&self.exits); + } +} + +impl Decodable for BeaconBlockBody { + fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { + let (proposer_slashings, i) = <_>::ssz_decode(bytes, i)?; + let (casper_slashings, i) = <_>::ssz_decode(bytes, i)?; + let (attestations, i) = <_>::ssz_decode(bytes, i)?; + let (deposits, i) = <_>::ssz_decode(bytes, i)?; + let (exits, i) = <_>::ssz_decode(bytes, i)?; + + Ok(( + Self { + proposer_slashings, + casper_slashings, + attestations, + deposits, + exits, + }, + i, + )) + } +} + +impl TestRandom for BeaconBlockBody { + fn random_for_test(rng: &mut T) -> Self { + Self { + proposer_slashings: <_>::random_for_test(rng), + casper_slashings: <_>::random_for_test(rng), + attestations: <_>::random_for_test(rng), + deposits: <_>::random_for_test(rng), + exits: <_>::random_for_test(rng), + } + } +} + +#[cfg(test)] +mod tests { + use super::super::ssz::ssz_encode; + use super::*; + use crate::test_utils::TestRandom; + use rand::{prng::XorShiftRng, SeedableRng}; + + #[test] + pub fn test_ssz_round_trip() { + let mut rng = XorShiftRng::from_seed([42; 16]); + let original = BeaconBlockBody::random_for_test(&mut rng); + + let bytes = ssz_encode(&original); + let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap(); + + assert_eq!(original, decoded); + } +} diff --git a/beacon_chain/types/src/casper_slashing.rs b/beacon_chain/types/src/casper_slashing.rs new file mode 100644 index 0000000000..f3c1b5d180 --- /dev/null +++ b/beacon_chain/types/src/casper_slashing.rs @@ -0,0 +1,60 @@ +use super::ssz::{Decodable, DecodeError, Encodable, SszStream}; +use super::SlashableVoteData; +use crate::test_utils::TestRandom; +use rand::RngCore; + +#[derive(Debug, PartialEq, Clone)] +pub struct CasperSlashing { + pub slashable_vote_data_1: SlashableVoteData, + pub slashable_vote_data_2: SlashableVoteData, +} + +impl Encodable for CasperSlashing { + fn ssz_append(&self, s: &mut SszStream) { + s.append(&self.slashable_vote_data_1); + s.append(&self.slashable_vote_data_2); + } +} + +impl Decodable for CasperSlashing { + fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { + let (slashable_vote_data_1, i) = <_>::ssz_decode(bytes, i)?; + let (slashable_vote_data_2, i) = <_>::ssz_decode(bytes, i)?; + + Ok(( + CasperSlashing { + slashable_vote_data_1, + slashable_vote_data_2, + }, + i, + )) + } +} + +impl TestRandom for CasperSlashing { + fn random_for_test(rng: &mut T) -> Self { + Self { + slashable_vote_data_1: <_>::random_for_test(rng), + slashable_vote_data_2: <_>::random_for_test(rng), + } + } +} + +#[cfg(test)] +mod tests { + use super::super::ssz::ssz_encode; + use super::*; + use crate::test_utils::TestRandom; + use rand::{prng::XorShiftRng, SeedableRng}; + + #[test] + pub fn test_ssz_round_trip() { + let mut rng = XorShiftRng::from_seed([42; 16]); + let original = CasperSlashing::random_for_test(&mut rng); + + let bytes = ssz_encode(&original); + let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap(); + + assert_eq!(original, decoded); + } +} diff --git a/beacon_chain/types/src/deposit.rs b/beacon_chain/types/src/deposit.rs new file mode 100644 index 0000000000..9d84bc2780 --- /dev/null +++ b/beacon_chain/types/src/deposit.rs @@ -0,0 +1,64 @@ +use super::ssz::{Decodable, DecodeError, Encodable, SszStream}; +use super::{DepositData, Hash256}; +use crate::test_utils::TestRandom; +use rand::RngCore; + +#[derive(Debug, PartialEq, Clone)] +pub struct Deposit { + pub merkle_branch: Vec, + pub merkle_tree_index: u64, + pub deposit_data: DepositData, +} + +impl Encodable for Deposit { + fn ssz_append(&self, s: &mut SszStream) { + s.append_vec(&self.merkle_branch); + s.append(&self.merkle_tree_index); + s.append(&self.deposit_data); + } +} + +impl Decodable for Deposit { + fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { + let (merkle_branch, i) = <_>::ssz_decode(bytes, i)?; + let (merkle_tree_index, i) = <_>::ssz_decode(bytes, i)?; + let (deposit_data, i) = <_>::ssz_decode(bytes, i)?; + + Ok(( + Self { + merkle_branch, + merkle_tree_index, + deposit_data, + }, + i, + )) + } +} + +impl TestRandom for Deposit { + fn random_for_test(rng: &mut T) -> Self { + Self { + merkle_branch: <_>::random_for_test(rng), + merkle_tree_index: <_>::random_for_test(rng), + deposit_data: <_>::random_for_test(rng), + } + } +} + +#[cfg(test)] +mod tests { + use super::super::ssz::ssz_encode; + use super::*; + use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng}; + + #[test] + pub fn test_ssz_round_trip() { + let mut rng = XorShiftRng::from_seed([42; 16]); + let original = Deposit::random_for_test(&mut rng); + + let bytes = ssz_encode(&original); + let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap(); + + assert_eq!(original, decoded); + } +} diff --git a/beacon_chain/types/src/deposit_data.rs b/beacon_chain/types/src/deposit_data.rs new file mode 100644 index 0000000000..b236709d20 --- /dev/null +++ b/beacon_chain/types/src/deposit_data.rs @@ -0,0 +1,65 @@ +use super::ssz::{Decodable, DecodeError, Encodable, SszStream}; +use super::DepositInput; +use crate::test_utils::TestRandom; +use rand::RngCore; + +#[derive(Debug, PartialEq, Clone)] +pub struct DepositData { + pub deposit_input: DepositInput, + pub value: u64, + pub timestamp: u64, +} + +impl Encodable for DepositData { + fn ssz_append(&self, s: &mut SszStream) { + s.append(&self.deposit_input); + s.append(&self.value); + s.append(&self.timestamp); + } +} + +impl Decodable for DepositData { + fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { + let (deposit_input, i) = <_>::ssz_decode(bytes, i)?; + let (value, i) = <_>::ssz_decode(bytes, i)?; + let (timestamp, i) = <_>::ssz_decode(bytes, i)?; + + Ok(( + Self { + deposit_input, + value, + timestamp, + }, + i, + )) + } +} + +impl TestRandom for DepositData { + fn random_for_test(rng: &mut T) -> Self { + Self { + deposit_input: <_>::random_for_test(rng), + value: <_>::random_for_test(rng), + timestamp: <_>::random_for_test(rng), + } + } +} + +#[cfg(test)] +mod tests { + use super::super::ssz::ssz_encode; + use super::*; + use crate::test_utils::TestRandom; + use rand::{prng::XorShiftRng, SeedableRng}; + + #[test] + pub fn test_ssz_round_trip() { + let mut rng = XorShiftRng::from_seed([42; 16]); + let original = DepositData::random_for_test(&mut rng); + + let bytes = ssz_encode(&original); + let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap(); + + assert_eq!(original, decoded); + } +} diff --git a/beacon_chain/types/src/deposit_input.rs b/beacon_chain/types/src/deposit_input.rs new file mode 100644 index 0000000000..41b579cdab --- /dev/null +++ b/beacon_chain/types/src/deposit_input.rs @@ -0,0 +1,72 @@ +use super::ssz::{decode_ssz_list, Decodable, DecodeError, Encodable, SszStream}; +use super::Hash256; +use crate::test_utils::TestRandom; +use bls::{PublicKey, Signature}; +use rand::RngCore; + +#[derive(Debug, PartialEq, Clone)] +pub struct DepositInput { + pub pubkey: PublicKey, + pub withdrawal_credentials: Hash256, + pub randao_commitment: Hash256, + pub proof_of_possession: Signature, +} + +impl Encodable for DepositInput { + fn ssz_append(&self, s: &mut SszStream) { + s.append_vec(&self.pubkey.as_bytes()); + s.append(&self.withdrawal_credentials); + s.append(&self.randao_commitment); + s.append(&self.proof_of_possession); + } +} + +impl Decodable for DepositInput { + fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { + let (pubkey_bytes, i) = decode_ssz_list(bytes, i)?; + let pubkey = PublicKey::from_bytes(&pubkey_bytes).map_err(|_| DecodeError::TooShort)?; + let (withdrawal_credentials, i) = <_>::ssz_decode(bytes, i)?; + let (randao_commitment, i) = <_>::ssz_decode(bytes, i)?; + let (proof_of_possession, i) = <_>::ssz_decode(bytes, i)?; + + Ok(( + Self { + pubkey, + withdrawal_credentials, + randao_commitment, + proof_of_possession, + }, + i, + )) + } +} + +impl TestRandom for DepositInput { + fn random_for_test(rng: &mut T) -> Self { + Self { + pubkey: <_>::random_for_test(rng), + withdrawal_credentials: <_>::random_for_test(rng), + randao_commitment: <_>::random_for_test(rng), + proof_of_possession: <_>::random_for_test(rng), + } + } +} + +#[cfg(test)] +mod tests { + use super::super::ssz::ssz_encode; + use super::*; + use crate::test_utils::TestRandom; + use rand::{prng::XorShiftRng, SeedableRng}; + + #[test] + pub fn test_ssz_round_trip() { + let mut rng = XorShiftRng::from_seed([42; 16]); + let original = DepositInput::random_for_test(&mut rng); + + let bytes = ssz_encode(&original); + let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap(); + + assert_eq!(original, decoded); + } +} diff --git a/beacon_chain/types/src/exit.rs b/beacon_chain/types/src/exit.rs new file mode 100644 index 0000000000..6a6d573051 --- /dev/null +++ b/beacon_chain/types/src/exit.rs @@ -0,0 +1,65 @@ +use super::ssz::{Decodable, DecodeError, Encodable, SszStream}; +use crate::test_utils::TestRandom; +use bls::Signature; +use rand::RngCore; + +#[derive(Debug, PartialEq, Clone)] +pub struct Exit { + pub slot: u64, + pub validator_index: u32, + pub signature: Signature, +} + +impl Encodable for Exit { + fn ssz_append(&self, s: &mut SszStream) { + s.append(&self.slot); + s.append(&self.validator_index); + s.append(&self.signature); + } +} + +impl Decodable for Exit { + fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { + let (slot, i) = <_>::ssz_decode(bytes, i)?; + let (validator_index, i) = <_>::ssz_decode(bytes, i)?; + let (signature, i) = <_>::ssz_decode(bytes, i)?; + + Ok(( + Self { + slot, + validator_index, + signature, + }, + i, + )) + } +} + +impl TestRandom for Exit { + fn random_for_test(rng: &mut T) -> Self { + Self { + slot: <_>::random_for_test(rng), + validator_index: <_>::random_for_test(rng), + signature: <_>::random_for_test(rng), + } + } +} + +#[cfg(test)] +mod tests { + use super::super::ssz::ssz_encode; + use super::*; + use crate::test_utils::TestRandom; + use rand::{prng::XorShiftRng, SeedableRng}; + + #[test] + pub fn test_ssz_round_trip() { + let mut rng = XorShiftRng::from_seed([42; 16]); + let original = Exit::random_for_test(&mut rng); + + let bytes = ssz_encode(&original); + let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap(); + + assert_eq!(original, decoded); + } +} diff --git a/beacon_chain/types/src/lib.rs b/beacon_chain/types/src/lib.rs index 663f23ee12..f91325db70 100644 --- a/beacon_chain/types/src/lib.rs +++ b/beacon_chain/types/src/lib.rs @@ -3,23 +3,34 @@ extern crate boolean_bitfield; extern crate ethereum_types; extern crate ssz; +pub mod test_utils; + pub mod active_state; pub mod attestation_data; pub mod attestation; pub mod beacon_block; +pub mod beacon_block_body; pub mod beacon_state; pub mod candidate_pow_receipt_root_record; +pub mod casper_slashing; pub mod chain_config; pub mod crosslink_record; pub mod crystallized_state; pub mod deposit; pub mod deposit_data; pub mod deposit_input; +<<<<<<< HEAD +======= +pub mod exit; +>>>>>>> master pub mod fork_data; pub mod pending_attestation_record; +pub mod proposal_signed_data; +pub mod proposer_slashing; pub mod shard_and_committee; pub mod shard_reassignment_record; pub mod special_record; +pub mod slashable_vote_data; pub mod validator_record; use self::ethereum_types::{H160, H256, U256}; @@ -29,15 +40,24 @@ pub use crate::active_state::ActiveState; pub use crate::attestation_data::AttestationData; pub use crate::attestation::Attestation; pub use crate::beacon_block::BeaconBlock; +pub use crate::beacon_block_body::BeaconBlockBody; pub use crate::beacon_state::BeaconState; +pub use crate::casper_slashing::CasperSlashing; pub use crate::chain_config::ChainConfig; pub use crate::crosslink_record::CrosslinkRecord; pub use crate::crystallized_state::CrystallizedState; pub use crate::deposit::Deposit; pub use crate::deposit_data::DepositData; pub use crate::deposit_input::DepositInput; +<<<<<<< HEAD +======= +pub use crate::exit::Exit; +>>>>>>> master pub use crate::fork_data::ForkData; pub use crate::pending_attestation_record::PendingAttestationRecord; +pub use crate::proposal_signed_data::ProposalSignedData; +pub use crate::proposer_slashing::ProposerSlashing; +pub use crate::slashable_vote_data::SlashableVoteData; pub use crate::shard_and_committee::ShardAndCommittee; pub use crate::special_record::{SpecialRecord, SpecialRecordKind}; pub use crate::validator_record::{ValidatorRecord, ValidatorStatus}; diff --git a/beacon_chain/types/src/proposal_signed_data.rs b/beacon_chain/types/src/proposal_signed_data.rs new file mode 100644 index 0000000000..7a01fc10a5 --- /dev/null +++ b/beacon_chain/types/src/proposal_signed_data.rs @@ -0,0 +1,65 @@ +use super::ssz::{Decodable, DecodeError, Encodable, SszStream}; +use super::Hash256; +use crate::test_utils::TestRandom; +use rand::RngCore; + +#[derive(Debug, PartialEq, Clone, Default)] +pub struct ProposalSignedData { + pub slot: u64, + pub shard: u64, + pub block_root: Hash256, +} + +impl Encodable for ProposalSignedData { + fn ssz_append(&self, s: &mut SszStream) { + s.append(&self.slot); + s.append(&self.shard); + s.append(&self.block_root); + } +} + +impl Decodable for ProposalSignedData { + fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { + let (slot, i) = <_>::ssz_decode(bytes, i)?; + let (shard, i) = <_>::ssz_decode(bytes, i)?; + let (block_root, i) = <_>::ssz_decode(bytes, i)?; + + Ok(( + ProposalSignedData { + slot, + shard, + block_root, + }, + i, + )) + } +} + +impl TestRandom for ProposalSignedData { + fn random_for_test(rng: &mut T) -> Self { + Self { + slot: <_>::random_for_test(rng), + shard: <_>::random_for_test(rng), + block_root: <_>::random_for_test(rng), + } + } +} + +#[cfg(test)] +mod tests { + use super::super::ssz::ssz_encode; + use super::*; + use crate::test_utils::TestRandom; + use rand::{prng::XorShiftRng, SeedableRng}; + + #[test] + pub fn test_ssz_round_trip() { + let mut rng = XorShiftRng::from_seed([42; 16]); + let original = ProposalSignedData::random_for_test(&mut rng); + + let bytes = ssz_encode(&original); + let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap(); + + assert_eq!(original, decoded); + } +} diff --git a/beacon_chain/types/src/proposer_slashing.rs b/beacon_chain/types/src/proposer_slashing.rs new file mode 100644 index 0000000000..0ae1c6e663 --- /dev/null +++ b/beacon_chain/types/src/proposer_slashing.rs @@ -0,0 +1,76 @@ +use super::ssz::{Decodable, DecodeError, Encodable, SszStream}; +use super::ProposalSignedData; +use crate::test_utils::TestRandom; +use bls::Signature; +use rand::RngCore; + +#[derive(Debug, PartialEq, Clone)] +pub struct ProposerSlashing { + pub proposer_index: u32, + pub proposal_data_1: ProposalSignedData, + pub proposal_signature_1: Signature, + pub proposal_data_2: ProposalSignedData, + pub proposal_signature_2: Signature, +} + +impl Encodable for ProposerSlashing { + fn ssz_append(&self, s: &mut SszStream) { + s.append(&self.proposer_index); + s.append(&self.proposal_data_1); + s.append(&self.proposal_signature_1); + s.append(&self.proposal_data_2); + s.append(&self.proposal_signature_2); + } +} + +impl Decodable for ProposerSlashing { + fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { + let (proposer_index, i) = <_>::ssz_decode(bytes, i)?; + let (proposal_data_1, i) = <_>::ssz_decode(bytes, i)?; + let (proposal_signature_1, i) = <_>::ssz_decode(bytes, i)?; + let (proposal_data_2, i) = <_>::ssz_decode(bytes, i)?; + let (proposal_signature_2, i) = <_>::ssz_decode(bytes, i)?; + + Ok(( + ProposerSlashing { + proposer_index, + proposal_data_1, + proposal_signature_1, + proposal_data_2, + proposal_signature_2, + }, + i, + )) + } +} + +impl TestRandom for ProposerSlashing { + fn random_for_test(rng: &mut T) -> Self { + Self { + proposer_index: <_>::random_for_test(rng), + proposal_data_1: <_>::random_for_test(rng), + proposal_signature_1: <_>::random_for_test(rng), + proposal_data_2: <_>::random_for_test(rng), + proposal_signature_2: <_>::random_for_test(rng), + } + } +} + +#[cfg(test)] +mod tests { + use super::super::ssz::ssz_encode; + use super::*; + use crate::test_utils::TestRandom; + use rand::{prng::XorShiftRng, SeedableRng}; + + #[test] + pub fn test_ssz_round_trip() { + let mut rng = XorShiftRng::from_seed([42; 16]); + let original = ProposerSlashing::random_for_test(&mut rng); + + let bytes = ssz_encode(&original); + let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap(); + + assert_eq!(original, decoded); + } +} diff --git a/beacon_chain/types/src/slashable_vote_data.rs b/beacon_chain/types/src/slashable_vote_data.rs new file mode 100644 index 0000000000..4d8e2eab30 --- /dev/null +++ b/beacon_chain/types/src/slashable_vote_data.rs @@ -0,0 +1,71 @@ +use super::ssz::{Decodable, DecodeError, Encodable, SszStream}; +use super::AttestationData; +use crate::test_utils::TestRandom; +use bls::AggregateSignature; +use rand::RngCore; + +#[derive(Debug, PartialEq, Clone)] +pub struct SlashableVoteData { + pub aggregate_signature_poc_0_indices: Vec, + pub aggregate_signature_poc_1_indices: Vec, + pub data: AttestationData, + pub aggregate_signature: AggregateSignature, +} + +impl Encodable for SlashableVoteData { + fn ssz_append(&self, s: &mut SszStream) { + s.append_vec(&self.aggregate_signature_poc_0_indices); + s.append_vec(&self.aggregate_signature_poc_1_indices); + s.append(&self.data); + s.append(&self.aggregate_signature); + } +} + +impl Decodable for SlashableVoteData { + fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { + let (aggregate_signature_poc_0_indices, i) = <_>::ssz_decode(bytes, i)?; + let (aggregate_signature_poc_1_indices, i) = <_>::ssz_decode(bytes, i)?; + let (data, i) = <_>::ssz_decode(bytes, i)?; + let (aggregate_signature, i) = <_>::ssz_decode(bytes, i)?; + + Ok(( + SlashableVoteData { + aggregate_signature_poc_0_indices, + aggregate_signature_poc_1_indices, + data, + aggregate_signature, + }, + i, + )) + } +} + +impl TestRandom for SlashableVoteData { + fn random_for_test(rng: &mut T) -> Self { + Self { + aggregate_signature_poc_0_indices: <_>::random_for_test(rng), + aggregate_signature_poc_1_indices: <_>::random_for_test(rng), + data: <_>::random_for_test(rng), + aggregate_signature: <_>::random_for_test(rng), + } + } +} + +#[cfg(test)] +mod tests { + use super::super::ssz::ssz_encode; + use super::*; + use crate::test_utils::TestRandom; + use rand::{prng::XorShiftRng, SeedableRng}; + + #[test] + pub fn test_ssz_round_trip() { + let mut rng = XorShiftRng::from_seed([42; 16]); + let original = SlashableVoteData::random_for_test(&mut rng); + + let bytes = ssz_encode(&original); + let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap(); + + assert_eq!(original, decoded); + } +} diff --git a/beacon_chain/types/src/test_utils/aggregate_signature.rs b/beacon_chain/types/src/test_utils/aggregate_signature.rs new file mode 100644 index 0000000000..6a15f73669 --- /dev/null +++ b/beacon_chain/types/src/test_utils/aggregate_signature.rs @@ -0,0 +1,12 @@ +use super::TestRandom; +use bls::{AggregateSignature, Signature}; +use rand::RngCore; + +impl TestRandom for AggregateSignature { + fn random_for_test(rng: &mut T) -> Self { + let signature = Signature::random_for_test(rng); + let mut aggregate_signature = AggregateSignature::new(); + aggregate_signature.add(&signature); + aggregate_signature + } +} diff --git a/beacon_chain/types/src/test_utils/bitfield.rs b/beacon_chain/types/src/test_utils/bitfield.rs new file mode 100644 index 0000000000..15011edd9c --- /dev/null +++ b/beacon_chain/types/src/test_utils/bitfield.rs @@ -0,0 +1,11 @@ +use super::super::Bitfield; +use super::TestRandom; +use rand::RngCore; + +impl TestRandom for Bitfield { + fn random_for_test(rng: &mut T) -> Self { + let mut raw_bytes = vec![0; 32]; + rng.fill_bytes(&mut raw_bytes); + Bitfield::from_bytes(&raw_bytes) + } +} diff --git a/beacon_chain/types/src/test_utils/hash256.rs b/beacon_chain/types/src/test_utils/hash256.rs new file mode 100644 index 0000000000..98f5e78996 --- /dev/null +++ b/beacon_chain/types/src/test_utils/hash256.rs @@ -0,0 +1,11 @@ +use super::TestRandom; +use crate::Hash256; +use rand::RngCore; + +impl TestRandom for Hash256 { + fn random_for_test(rng: &mut T) -> Self { + let mut key_bytes = vec![0; 32]; + rng.fill_bytes(&mut key_bytes); + Hash256::from(&key_bytes[..]) + } +} diff --git a/beacon_chain/types/src/test_utils/mod.rs b/beacon_chain/types/src/test_utils/mod.rs new file mode 100644 index 0000000000..131ff9aacf --- /dev/null +++ b/beacon_chain/types/src/test_utils/mod.rs @@ -0,0 +1,40 @@ +use rand::RngCore; + +pub use rand::{prng::XorShiftRng, SeedableRng}; + +pub mod aggregate_signature; +pub mod bitfield; +pub mod hash256; +pub mod signature; +pub mod secret_key; +pub mod public_key; + +pub trait TestRandom +where T: RngCore +{ + fn random_for_test(rng: &mut T) -> Self; +} + +impl TestRandom for u64 { + fn random_for_test(rng: &mut T) -> Self { + rng.next_u64() + } +} + +impl TestRandom for u32 { + fn random_for_test(rng: &mut T) -> Self { + rng.next_u32() + } +} + +impl TestRandom for Vec +where U: TestRandom +{ + fn random_for_test(rng: &mut T) -> Self { + vec![ + ::random_for_test(rng), + ::random_for_test(rng), + ::random_for_test(rng), + ] + } +} diff --git a/beacon_chain/types/src/test_utils/public_key.rs b/beacon_chain/types/src/test_utils/public_key.rs new file mode 100644 index 0000000000..bfccd3e532 --- /dev/null +++ b/beacon_chain/types/src/test_utils/public_key.rs @@ -0,0 +1,10 @@ +use super::TestRandom; +use bls::{PublicKey, SecretKey}; +use rand::RngCore; + +impl TestRandom for PublicKey { + fn random_for_test(rng: &mut T) -> Self { + let secret_key = SecretKey::random_for_test(rng); + PublicKey::from_secret_key(&secret_key) + } +} diff --git a/beacon_chain/types/src/test_utils/secret_key.rs b/beacon_chain/types/src/test_utils/secret_key.rs new file mode 100644 index 0000000000..17481c3de9 --- /dev/null +++ b/beacon_chain/types/src/test_utils/secret_key.rs @@ -0,0 +1,19 @@ +use super::TestRandom; +use bls::SecretKey; +use rand::RngCore; + +impl TestRandom for SecretKey { + fn random_for_test(rng: &mut T) -> Self { + let mut key_bytes = vec![0; 48]; + rng.fill_bytes(&mut key_bytes); + /* + * An `unreachable!` is used here as there's no reason why you cannot constuct a key from a + * fixed-length byte slice. Also, this should only be used during testing so a panic is + * acceptable. + */ + match SecretKey::from_bytes(&key_bytes) { + Ok(key) => key, + Err(_) => unreachable!(), + } + } +} diff --git a/beacon_chain/types/src/test_utils/signature.rs b/beacon_chain/types/src/test_utils/signature.rs new file mode 100644 index 0000000000..9ec7aec60a --- /dev/null +++ b/beacon_chain/types/src/test_utils/signature.rs @@ -0,0 +1,13 @@ +use super::TestRandom; +use bls::{SecretKey, Signature}; +use rand::RngCore; + +impl TestRandom for Signature { + fn random_for_test(rng: &mut T) -> Self { + let secret_key = SecretKey::random_for_test(rng); + let mut message = vec![0; 32]; + rng.fill_bytes(&mut message); + + Signature::new(&message, &secret_key) + } +} diff --git a/beacon_chain/utils/bls/Cargo.toml b/beacon_chain/utils/bls/Cargo.toml index d38b5c6048..9e782b0597 100644 --- a/beacon_chain/utils/bls/Cargo.toml +++ b/beacon_chain/utils/bls/Cargo.toml @@ -7,3 +7,4 @@ edition = "2018" [dependencies] bls-aggregates = { git = "https://github.com/sigp/signature-schemes" } hashing = { path = "../hashing" } +ssz = { path = "../ssz" } diff --git a/beacon_chain/utils/bls/src/aggregate_signature.rs b/beacon_chain/utils/bls/src/aggregate_signature.rs new file mode 100644 index 0000000000..2e36302688 --- /dev/null +++ b/beacon_chain/utils/bls/src/aggregate_signature.rs @@ -0,0 +1,65 @@ +use super::ssz::{decode_ssz_list, Decodable, DecodeError, Encodable, SszStream}; +use super::{AggregatePublicKey, Signature}; +use bls_aggregates::AggregateSignature as RawAggregateSignature; + +/// A BLS aggregate signature. +/// +/// This struct is a wrapper upon a base type and provides helper functions (e.g., SSZ +/// serialization). +#[derive(Debug, PartialEq, Clone)] +pub struct AggregateSignature(RawAggregateSignature); + +impl AggregateSignature { + /// Instantiate a new AggregateSignature. + pub fn new() -> Self { + AggregateSignature(RawAggregateSignature::new()) + } + + /// Add (aggregate) a signature to the `AggregateSignature`. + pub fn add(&mut self, signature: &Signature) { + self.0.add(signature.as_raw()) + } + + /// Verify the `AggregateSignature` against an `AggregatePublicKey`. + /// + /// Only returns `true` if the set of keys in the `AggregatePublicKey` match the set of keys + /// that signed the `AggregateSignature`. + pub fn verify(&self, msg: &[u8], aggregate_public_key: &AggregatePublicKey) -> bool { + self.0.verify(msg, aggregate_public_key) + } +} + +impl Encodable for AggregateSignature { + fn ssz_append(&self, s: &mut SszStream) { + s.append_vec(&self.0.as_bytes()); + } +} + +impl Decodable for AggregateSignature { + fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { + let (sig_bytes, i) = decode_ssz_list(bytes, i)?; + let raw_sig = + RawAggregateSignature::from_bytes(&sig_bytes).map_err(|_| DecodeError::TooShort)?; + Ok((AggregateSignature(raw_sig), i)) + } +} + +#[cfg(test)] +mod tests { + use super::super::ssz::ssz_encode; + use super::super::{Keypair, Signature}; + use super::*; + + #[test] + pub fn test_ssz_round_trip() { + let keypair = Keypair::random(); + + let mut original = AggregateSignature::new(); + original.add(&Signature::new(&[42, 42], &keypair.sk)); + + let bytes = ssz_encode(&original); + let (decoded, _) = AggregateSignature::ssz_decode(&bytes, 0).unwrap(); + + assert_eq!(original, decoded); + } +} diff --git a/beacon_chain/utils/bls/src/lib.rs b/beacon_chain/utils/bls/src/lib.rs index fc665b206b..d3a01fb10c 100644 --- a/beacon_chain/utils/bls/src/lib.rs +++ b/beacon_chain/utils/bls/src/lib.rs @@ -1,12 +1,17 @@ extern crate bls_aggregates; extern crate hashing; +extern crate ssz; + +mod aggregate_signature; +mod signature; + +pub use crate::aggregate_signature::AggregateSignature; +pub use crate::signature::Signature; pub use self::bls_aggregates::AggregatePublicKey; -pub use self::bls_aggregates::AggregateSignature; pub use self::bls_aggregates::Keypair; pub use self::bls_aggregates::PublicKey; pub use self::bls_aggregates::SecretKey; -pub use self::bls_aggregates::Signature; pub const BLS_AGG_SIG_BYTE_SIZE: usize = 97; diff --git a/beacon_chain/utils/bls/src/signature.rs b/beacon_chain/utils/bls/src/signature.rs new file mode 100644 index 0000000000..ebdb5b8176 --- /dev/null +++ b/beacon_chain/utils/bls/src/signature.rs @@ -0,0 +1,70 @@ +use super::ssz::{decode_ssz_list, Decodable, DecodeError, Encodable, SszStream}; +use bls_aggregates::{PublicKey, SecretKey, Signature as RawSignature}; + +/// A single BLS signature. +/// +/// This struct is a wrapper upon a base type and provides helper functions (e.g., SSZ +/// serialization). +#[derive(Debug, PartialEq, Clone)] +pub struct Signature(RawSignature); + +impl Signature { + /// Instantiate a new Signature from a message and a SecretKey. + pub fn new(msg: &[u8], sk: &SecretKey) -> Self { + Signature(RawSignature::new(msg, sk)) + } + + /// Instantiate a new Signature from a message and a SecretKey, where the message has already + /// been hashed. + pub fn new_hashed(msg_hashed: &[u8], sk: &SecretKey) -> Self { + Signature(RawSignature::new_hashed(msg_hashed, sk)) + } + + /// Verify the Signature against a PublicKey. + pub fn verify(&self, msg: &[u8], pk: &PublicKey) -> bool { + self.0.verify(msg, pk) + } + + /// Verify the Signature against a PublicKey, where the message has already been hashed. + pub fn verify_hashed(&self, msg_hash: &[u8], pk: &PublicKey) -> bool { + self.0.verify_hashed(msg_hash, pk) + } + + /// Returns the underlying signature. + pub fn as_raw(&self) -> &RawSignature { + &self.0 + } +} + +impl Encodable for Signature { + fn ssz_append(&self, s: &mut SszStream) { + s.append_vec(&self.0.as_bytes()); + } +} + +impl Decodable for Signature { + fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { + let (sig_bytes, i) = decode_ssz_list(bytes, i)?; + let raw_sig = RawSignature::from_bytes(&sig_bytes).map_err(|_| DecodeError::TooShort)?; + Ok((Signature(raw_sig), i)) + } +} + +#[cfg(test)] +mod tests { + use super::super::ssz::ssz_encode; + use super::super::Keypair; + use super::*; + + #[test] + pub fn test_ssz_round_trip() { + let keypair = Keypair::random(); + + let original = Signature::new(&[42, 42], &keypair.sk); + + let bytes = ssz_encode(&original); + let (decoded, _) = Signature::ssz_decode(&bytes, 0).unwrap(); + + assert_eq!(original, decoded); + } +} diff --git a/beacon_chain/utils/ssz/src/impl_encode.rs b/beacon_chain/utils/ssz/src/impl_encode.rs index 3f366bdf3a..f316a21ead 100644 --- a/beacon_chain/utils/ssz/src/impl_encode.rs +++ b/beacon_chain/utils/ssz/src/impl_encode.rs @@ -12,7 +12,7 @@ use super::{Encodable, SszStream}; macro_rules! impl_encodable_for_uint { ($type: ident, $bit_size: expr) => { impl Encodable for $type { - #[allow(cast_lossless)] + #[allow(clippy::cast_lossless)] fn ssz_append(&self, s: &mut SszStream) { // Ensure bit size is valid assert!( diff --git a/beacon_chain/utils/ssz_helpers/Cargo.toml b/beacon_chain/utils/ssz_helpers/Cargo.toml deleted file mode 100644 index 3634ea39c8..0000000000 --- a/beacon_chain/utils/ssz_helpers/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "ssz_helpers" -version = "0.1.0" -authors = ["Paul Hauner "] -edition = "2018" - -[dependencies] -bls = { path = "../bls" } -hashing = { path = "../hashing" } -types = { path = "../../types" } -ssz = { path = "../ssz" } diff --git a/beacon_chain/utils/ssz_helpers/src/attestation_ssz_splitter.rs b/beacon_chain/utils/ssz_helpers/src/attestation_ssz_splitter.rs deleted file mode 100644 index a216e0ec26..0000000000 --- a/beacon_chain/utils/ssz_helpers/src/attestation_ssz_splitter.rs +++ /dev/null @@ -1,162 +0,0 @@ -use super::bls::BLS_AGG_SIG_BYTE_SIZE; -use super::ssz::decode::decode_length; -use super::ssz::LENGTH_BYTES; -use super::types::attestation::MIN_SSZ_ATTESTION_RECORD_LENGTH; -use super::types::attestation_data::SSZ_ATTESTION_DATA_LENGTH; - -#[derive(Debug, PartialEq)] -pub enum AttestationSplitError { - TooShort, -} - -/// Given some ssz slice, find the bounds of each serialized Attestation and return a vec of -/// slices point to each. -pub fn split_all_attestations<'a>( - full_ssz: &'a [u8], - index: usize, -) -> Result, AttestationSplitError> { - let mut v = vec![]; - let mut index = index; - while index < full_ssz.len() - 1 { - let (slice, i) = split_one_attestation(full_ssz, index)?; - v.push(slice); - index = i; - } - Ok(v) -} - -/// Given some ssz slice, find the bounds of one serialized Attestation -/// and return a slice pointing to that. -pub fn split_one_attestation( - full_ssz: &[u8], - index: usize, -) -> Result<(&[u8], usize), AttestationSplitError> { - let length = determine_ssz_attestation_len(full_ssz, index)?; - let end = index + length; - - // The check to ensure that the slice exists _should_ be redundant as it is already checked in - // `determine_ssz_attestation_len`, however it is checked here again for additional safety - // against panics. - match full_ssz.get(index..end) { - None => Err(AttestationSplitError::TooShort), - Some(slice) => Ok((slice, end)), - } -} - -/// Given some SSZ, assume that a serialized `Attestation` begins at the `index` position and -/// attempt to find the length (in bytes) of that serialized `Attestation`. -/// -/// This function does not perform validation on the `Attestation`. It is very likely that -/// given some sufficiently long non-`Attestation` bytes it will not raise an error. -fn determine_ssz_attestation_len( - full_ssz: &[u8], - index: usize, -) -> Result { - if full_ssz.len() < MIN_SSZ_ATTESTION_RECORD_LENGTH { - return Err(AttestationSplitError::TooShort); - } - - let data_struct_end = index + SSZ_ATTESTION_DATA_LENGTH; - - // Determine the end of the first bitfield. - let participation_bitfield_len = decode_length(full_ssz, data_struct_end, LENGTH_BYTES) - .map_err(|_| AttestationSplitError::TooShort)?; - let participation_bitfield_end = data_struct_end + LENGTH_BYTES + participation_bitfield_len; - - // Determine the end of the second bitfield. - let custody_bitfield_len = decode_length(full_ssz, participation_bitfield_end, LENGTH_BYTES) - .map_err(|_| AttestationSplitError::TooShort)?; - let custody_bitfield_end = participation_bitfield_end + LENGTH_BYTES + custody_bitfield_len; - - // Determine the very end of the Attestation. - let agg_sig_end = custody_bitfield_end + LENGTH_BYTES + BLS_AGG_SIG_BYTE_SIZE; - - if agg_sig_end > full_ssz.len() { - Err(AttestationSplitError::TooShort) - } else { - Ok(agg_sig_end - index) - } -} - -#[cfg(test)] -mod tests { - use super::super::bls::AggregateSignature; - use super::super::ssz::{Decodable, SszStream}; - use super::super::types::{Attestation, AttestationData, Bitfield, Hash256}; - use super::*; - - fn get_two_records() -> Vec { - let a = Attestation { - data: AttestationData { - slot: 7, - shard: 9, - beacon_block_hash: Hash256::from("a_beacon".as_bytes()), - epoch_boundary_hash: Hash256::from("a_epoch".as_bytes()), - shard_block_hash: Hash256::from("a_shard".as_bytes()), - latest_crosslink_hash: Hash256::from("a_xlink".as_bytes()), - justified_slot: 19, - justified_block_hash: Hash256::from("a_justified".as_bytes()), - }, - participation_bitfield: Bitfield::from_bytes(&vec![17; 42][..]), - custody_bitfield: Bitfield::from_bytes(&vec![255; 12][..]), - aggregate_sig: AggregateSignature::new(), - }; - let b = Attestation { - data: AttestationData { - slot: 9, - shard: 7, - beacon_block_hash: Hash256::from("b_beacon".as_bytes()), - epoch_boundary_hash: Hash256::from("b_epoch".as_bytes()), - shard_block_hash: Hash256::from("b_shard".as_bytes()), - latest_crosslink_hash: Hash256::from("b_xlink".as_bytes()), - justified_slot: 15, - justified_block_hash: Hash256::from("b_justified".as_bytes()), - }, - participation_bitfield: Bitfield::from_bytes(&vec![1; 42][..]), - custody_bitfield: Bitfield::from_bytes(&vec![11; 3][..]), - aggregate_sig: AggregateSignature::new(), - }; - vec![a, b] - } - - #[test] - fn test_attestation_ssz_split() { - let ars = get_two_records(); - let a = ars[0].clone(); - let b = ars[1].clone(); - - /* - * Test split one - */ - let mut ssz_stream = SszStream::new(); - ssz_stream.append(&a); - let ssz = ssz_stream.drain(); - let (a_ssz, i) = split_one_attestation(&ssz, 0).unwrap(); - assert_eq!(i, ssz.len()); - let (decoded_a, _) = Attestation::ssz_decode(a_ssz, 0).unwrap(); - assert_eq!(a, decoded_a); - - /* - * Test split two - */ - let mut ssz_stream = SszStream::new(); - ssz_stream.append(&a); - ssz_stream.append(&b); - let ssz = ssz_stream.drain(); - let ssz_vec = split_all_attestations(&ssz, 0).unwrap(); - let (decoded_a, _) = Attestation::ssz_decode(ssz_vec[0], 0).unwrap(); - let (decoded_b, _) = Attestation::ssz_decode(ssz_vec[1], 0).unwrap(); - assert_eq!(a, decoded_a); - assert_eq!(b, decoded_b); - - /* - * Test split two with shortened ssz - */ - let mut ssz_stream = SszStream::new(); - ssz_stream.append(&a); - ssz_stream.append(&b); - let ssz = ssz_stream.drain(); - let ssz = &ssz[0..ssz.len() - 1]; - assert!(split_all_attestations(&ssz, 0).is_err()); - } -} diff --git a/beacon_chain/utils/ssz_helpers/src/lib.rs b/beacon_chain/utils/ssz_helpers/src/lib.rs deleted file mode 100644 index 6ac4372e9d..0000000000 --- a/beacon_chain/utils/ssz_helpers/src/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -extern crate bls; -extern crate hashing; -extern crate ssz; -extern crate types; - -pub mod attestation_ssz_splitter; -pub mod ssz_beacon_block; diff --git a/beacon_chain/utils/ssz_helpers/src/ssz_beacon_block.rs b/beacon_chain/utils/ssz_helpers/src/ssz_beacon_block.rs deleted file mode 100644 index fd02df78dd..0000000000 --- a/beacon_chain/utils/ssz_helpers/src/ssz_beacon_block.rs +++ /dev/null @@ -1,483 +0,0 @@ -use super::hashing::canonical_hash; -use super::ssz::decode::{decode_length, Decodable}; -use super::types::beacon_block::{MAX_SSZ_BLOCK_LENGTH, MIN_SSZ_BLOCK_LENGTH}; - -#[derive(Debug, PartialEq)] -pub enum SszBeaconBlockError { - TooShort, - TooLong, -} - -/* - * Constants used for navigating the SSZ bytes. - */ -const LENGTH_PREFIX_BYTES: usize = 4; -const SLOT_BYTES: usize = 8; -const HASH_SIZE: usize = 32; -const RANDAO_REVEAL_BYTES: usize = HASH_SIZE; -const POW_CHAIN_REF_BYTES: usize = HASH_SIZE; -const ACTIVE_STATE_BYTES: usize = HASH_SIZE; -const CRYSTALLIZED_STATE_BYTES: usize = HASH_SIZE; - -/// Allows for reading of block values directly from serialized ssz bytes. -/// -/// The purpose of this struct is to provide the functionality to read block fields directly from -/// some serialized SSZ slice allowing us to read the block without fully -/// de-serializing it. -/// -/// This struct should be as "zero-copy" as possible. The `ssz` field is a reference to some slice -/// and each function reads from that slice. -/// -/// Use this to perform intial checks before we fully de-serialize a block. It should only really -/// be used to verify blocks that come in from the network, for internal operations we should use a -/// full `BeaconBlock`. -#[derive(Debug, PartialEq)] -pub struct SszBeaconBlock<'a> { - ssz: &'a [u8], - block_ssz_len: usize, - // Ancestors - ancestors_position: usize, - ancestors_len: usize, - // Attestations - attestations_position: usize, - attestations_len: usize, - // Specials - specials_position: usize, - specials_len: usize, -} - -impl<'a> SszBeaconBlock<'a> { - /// Create a new instance from a slice reference. - /// - /// This function will validate the length of the ssz string, however it will not validate the - /// contents. - /// - /// The returned `SszBeaconBlock` instance will contain a `len` field which can be used to determine - /// how many bytes were read from the slice. In the case of multiple, sequentually serialized - /// blocks `len` can be used to assume the location of the next serialized block. - pub fn from_slice(vec: &'a [u8]) -> Result { - let untrimmed_ssz = &vec[..]; - - /* - * Ensure the SSZ is long enough to be a block - */ - if vec.len() < MIN_SSZ_BLOCK_LENGTH { - return Err(SszBeaconBlockError::TooShort); - } - - /* - * Ensure the SSZ slice isn't longer than is possible for a block. - */ - if vec.len() > MAX_SSZ_BLOCK_LENGTH { - return Err(SszBeaconBlockError::TooLong); - } - - /* - * Determine how many bytes are used to store ancestor hashes. - */ - let ancestors_position = SLOT_BYTES + RANDAO_REVEAL_BYTES + POW_CHAIN_REF_BYTES; - let ancestors_len = decode_length(untrimmed_ssz, ancestors_position, LENGTH_PREFIX_BYTES) - .map_err(|_| SszBeaconBlockError::TooShort)?; - - /* - * Determine how many bytes are used to store attestation records. - */ - let attestations_position = ancestors_position + LENGTH_PREFIX_BYTES + ancestors_len + // end of ancestor bytes - ACTIVE_STATE_BYTES + - CRYSTALLIZED_STATE_BYTES; - let attestations_len = - decode_length(untrimmed_ssz, attestations_position, LENGTH_PREFIX_BYTES) - .map_err(|_| SszBeaconBlockError::TooShort)?; - - /* - * Determine how many bytes are used to store specials. - */ - let specials_position = attestations_position + LENGTH_PREFIX_BYTES + attestations_len; - let specials_len = decode_length(untrimmed_ssz, specials_position, LENGTH_PREFIX_BYTES) - .map_err(|_| SszBeaconBlockError::TooShort)?; - - /* - * Now that all variable field lengths are known (ancestors, attestations, specials) we can - * know the exact length of the block and reject it if the slice is too short. - */ - let block_ssz_len = MIN_SSZ_BLOCK_LENGTH + ancestors_len + attestations_len + specials_len; - if vec.len() < block_ssz_len { - return Err(SszBeaconBlockError::TooShort); - } - - Ok(Self { - ssz: &untrimmed_ssz[0..block_ssz_len], - block_ssz_len, - ancestors_position, - ancestors_len, - attestations_position, - attestations_len, - specials_position, - specials_len, - }) - } - - pub fn len(&self) -> usize { - self.ssz.len() - } - pub fn is_empty(&self) -> bool { - self.ssz.is_empty() - } - - /// Returns this block as ssz. - /// - /// Does not include any excess ssz bytes that were supplied to this struct. - pub fn block_ssz(&self) -> &'a [u8] { - &self.ssz[0..self.block_ssz_len] - } - - /// Return the canonical hash for this block. - pub fn block_hash(&self) -> Vec { - canonical_hash(&self.ssz) - } - - /// Return the bytes representing `ancestor_hashes[0]`. - /// - /// The first hash in `ancestor_hashes` is the parent of the block. - pub fn parent_hash(&self) -> Option<&[u8]> { - let ancestor_ssz = self.ancestor_hashes(); - let start = LENGTH_PREFIX_BYTES; - ancestor_ssz.get(start..start + HASH_SIZE) - } - - /// Return the `slot` field. - pub fn slot(&self) -> u64 { - /* - * An error should be unreachable from this decode - * because we checked the length of the array at - * the initalization of this struct. - * - * If you can make this function panic, please report - * it to paul@sigmaprime.io - */ - if let Ok((n, _)) = u64::ssz_decode(&self.ssz, 0) { - n - } else { - unreachable!(); - } - } - - /// Return the `randao_reveal` field. - pub fn randao_reveal(&self) -> &[u8] { - let start = SLOT_BYTES; - &self.ssz[start..start + RANDAO_REVEAL_BYTES] - } - - /// Return the `pow_chain_reference` field. - pub fn pow_chain_reference(&self) -> &[u8] { - let start = SLOT_BYTES + RANDAO_REVEAL_BYTES; - &self.ssz[start..start + POW_CHAIN_REF_BYTES] - } - - /// Return the serialized `ancestor_hashes` bytes, including length prefix. - pub fn ancestor_hashes(&self) -> &[u8] { - let start = self.ancestors_position; - &self.ssz[start..(start + self.ancestors_len + LENGTH_PREFIX_BYTES)] - } - - /// Return the `active_state_root` field. - pub fn act_state_root(&self) -> &[u8] { - let start = self.ancestors_position + LENGTH_PREFIX_BYTES + self.ancestors_len; - &self.ssz[start..(start + 32)] - } - - /// Return the `active_state_root` field. - pub fn cry_state_root(&self) -> &[u8] { - let start = - self.ancestors_position + LENGTH_PREFIX_BYTES + self.ancestors_len + ACTIVE_STATE_BYTES; - &self.ssz[start..(start + 32)] - } - - /// Return the serialized `attestations` bytes, including length prefix. - pub fn attestations(&self) -> &[u8] { - let start = self.attestations_position; - &self.ssz[start..(start + self.attestations_len + LENGTH_PREFIX_BYTES)] - } - - /// Return the serialized `attestations` bytes _without_ the length prefix. - pub fn attestations_without_length(&self) -> &[u8] { - let start = self.attestations_position + LENGTH_PREFIX_BYTES; - &self.ssz[start..start + self.attestations_len] - } - - /// Return the serialized `specials` bytes, including length prefix. - pub fn specials(&self) -> &[u8] { - let start = self.specials_position; - &self.ssz[start..(start + self.specials_len + LENGTH_PREFIX_BYTES)] - } -} - -#[cfg(test)] -mod tests { - use super::super::ssz::encode::encode_length; - use super::super::ssz::SszStream; - use super::super::types::Hash256; - use super::super::types::{Attestation, BeaconBlock, SpecialRecord}; - use super::*; - - use super::canonical_hash; - - fn get_block_ssz(b: &BeaconBlock) -> Vec { - let mut ssz_stream = SszStream::new(); - ssz_stream.append(b); - ssz_stream.drain() - } - - fn get_special_record_ssz(sr: &SpecialRecord) -> Vec { - let mut ssz_stream = SszStream::new(); - ssz_stream.append(sr); - ssz_stream.drain() - } - - fn get_attestation_record_ssz(ar: &Attestation) -> Vec { - let mut ssz_stream = SszStream::new(); - ssz_stream.append(ar); - ssz_stream.drain() - } - - #[test] - fn test_ssz_block_zero_attestation_records() { - let mut b = BeaconBlock::zero(); - b.attestations = vec![]; - let ssz = get_block_ssz(&b); - - assert!(SszBeaconBlock::from_slice(&ssz[..]).is_ok()); - } - - #[test] - fn test_ssz_block_single_attestation_record_one_byte_short() { - let mut b = BeaconBlock::zero(); - b.attestations = vec![Attestation::zero()]; - let ssz = get_block_ssz(&b); - - assert_eq!( - SszBeaconBlock::from_slice(&ssz[0..(ssz.len() - 1)]), - Err(SszBeaconBlockError::TooShort) - ); - } - - #[test] - fn test_ssz_block_single_attestation_record_one_byte_long() { - let mut b = BeaconBlock::zero(); - b.attestations = vec![Attestation::zero()]; - let mut ssz = get_block_ssz(&b); - let original_len = ssz.len(); - ssz.push(42); - - let ssz_block = SszBeaconBlock::from_slice(&ssz[..]).unwrap(); - - assert_eq!(ssz_block.len(), original_len); - } - - #[test] - fn test_ssz_block_single_attestation_record() { - let mut b = BeaconBlock::zero(); - b.attestations = vec![Attestation::zero()]; - let ssz = get_block_ssz(&b); - - assert!(SszBeaconBlock::from_slice(&ssz[..]).is_ok()); - } - - #[test] - fn test_ssz_block_block_hash() { - let mut block = BeaconBlock::zero(); - block.attestations.push(Attestation::zero()); - let serialized = get_block_ssz(&block); - let ssz_block = SszBeaconBlock::from_slice(&serialized).unwrap(); - let hash = ssz_block.block_hash(); - // Note: this hash was not generated by some external program, - // it was simply printed then copied into the code. This test - // will tell us if the hash changes, not that it matches some - // canonical reference. - // TODO: make sure this test conforms to canonical test vectors; it is not clear that it currently does so - let expected_hash = [ - 132, 43, 230, 49, 234, 240, 253, 146, 85, 121, 104, 79, 35, 0, 126, 162, 132, 99, 145, - 13, 30, 57, 118, 5, 175, 136, 174, 7, 52, 161, 87, 196, - ]; - assert_eq!(hash, expected_hash); - - /* - * Test if you give the SszBeaconBlock too many ssz bytes - */ - let mut too_long = serialized.clone(); - too_long.push(42); - let ssz_block = SszBeaconBlock::from_slice(&too_long).unwrap(); - let hash = ssz_block.block_hash(); - assert_eq!(hash, expected_hash); - } - - #[test] - fn test_ssz_block_slot() { - let mut block = BeaconBlock::zero(); - block.attestations.push(Attestation::zero()); - block.slot = 42; - - let serialized = get_block_ssz(&block); - let ssz_block = SszBeaconBlock::from_slice(&serialized).unwrap(); - - assert_eq!(ssz_block.slot(), 42); - } - - #[test] - fn test_ssz_block_randao_reveal() { - let mut block = BeaconBlock::zero(); - block.attestations.push(Attestation::zero()); - let reference_hash = Hash256::from([42_u8; 32]); - block.randao_reveal = reference_hash.clone(); - - let serialized = get_block_ssz(&block); - let ssz_block = SszBeaconBlock::from_slice(&serialized).unwrap(); - - assert_eq!(ssz_block.randao_reveal(), &reference_hash.to_vec()[..]); - } - - #[test] - fn test_ssz_block_ancestor_hashes() { - let mut block = BeaconBlock::zero(); - let h = Hash256::from(&vec![42_u8; 32][..]); - block.ancestor_hashes.push(h); - - let serialized = get_block_ssz(&block); - let ssz_block = SszBeaconBlock::from_slice(&serialized).unwrap(); - - let mut expected = encode_length(32, LENGTH_PREFIX_BYTES); - expected.append(&mut h.to_vec()); - - assert_eq!(ssz_block.ancestor_hashes(), &expected[..]); - } - - #[test] - fn test_ssz_block_parent_hash() { - let mut block = BeaconBlock::zero(); - block.ancestor_hashes = vec![ - Hash256::from("cats".as_bytes()), - Hash256::from("dogs".as_bytes()), - Hash256::from("birds".as_bytes()), - ]; - - let serialized = get_block_ssz(&block); - let ssz_block = SszBeaconBlock::from_slice(&serialized).unwrap(); - - assert_eq!( - ssz_block.parent_hash().unwrap(), - &Hash256::from("cats".as_bytes()).to_vec()[..] - ); - } - - #[test] - fn test_ssz_block_specials() { - /* - * Without data - */ - let mut block = BeaconBlock::zero(); - let s = SpecialRecord::logout(&[]); - block.specials.push(s.clone()); - - let serialized = get_block_ssz(&block); - let ssz_block = SszBeaconBlock::from_slice(&serialized).unwrap(); - let sr_ssz = get_special_record_ssz(&s); - - let mut expected = encode_length(sr_ssz.len(), LENGTH_PREFIX_BYTES); - expected.append(&mut sr_ssz.to_vec()); - - assert_eq!(ssz_block.specials(), &expected[..]); - - /* - * With data - */ - let mut block = BeaconBlock::zero(); - let s = SpecialRecord::randao_change(&[16, 17, 18]); - block.specials.push(s.clone()); - - let serialized = get_block_ssz(&block); - let ssz_block = SszBeaconBlock::from_slice(&serialized).unwrap(); - let sr_ssz = get_special_record_ssz(&s); - - let mut expected = encode_length(sr_ssz.len(), LENGTH_PREFIX_BYTES); - expected.append(&mut sr_ssz.to_vec()); - - assert_eq!(ssz_block.specials(), &expected[..]); - } - - #[test] - fn test_ssz_block_attestations() { - /* - * Single Attestation - */ - let mut block = BeaconBlock::zero(); - block.attestations.push(Attestation::zero()); - - let serialized = get_block_ssz(&block); - let ssz_block = SszBeaconBlock::from_slice(&serialized).unwrap(); - let ssz_ar = get_attestation_record_ssz(&Attestation::zero()); - - let mut expected = encode_length(ssz_ar.len(), LENGTH_PREFIX_BYTES); - expected.append(&mut ssz_ar.to_vec()); - - assert_eq!(ssz_block.attestations(), &expected[..]); - - /* - * Multiple Attestations - */ - let mut block = BeaconBlock::zero(); - block.attestations.push(Attestation::zero()); - block.attestations.push(Attestation::zero()); - - let serialized = get_block_ssz(&block); - let ssz_block = SszBeaconBlock::from_slice(&serialized).unwrap(); - let mut ssz_ar = get_attestation_record_ssz(&Attestation::zero()); - ssz_ar.append(&mut get_attestation_record_ssz(&Attestation::zero())); - - let mut expected = encode_length(ssz_ar.len(), LENGTH_PREFIX_BYTES); - expected.append(&mut ssz_ar.to_vec()); - - assert_eq!(ssz_block.attestations(), &expected[..]); - } - - #[test] - fn test_ssz_block_pow_chain_reference() { - let mut block = BeaconBlock::zero(); - block.attestations.push(Attestation::zero()); - let reference_hash = Hash256::from([42_u8; 32]); - block.pow_chain_reference = reference_hash.clone(); - - let serialized = get_block_ssz(&block); - let ssz_block = SszBeaconBlock::from_slice(&serialized).unwrap(); - - assert_eq!( - ssz_block.pow_chain_reference(), - &reference_hash.to_vec()[..] - ); - } - - #[test] - fn test_ssz_block_act_state_root() { - let mut block = BeaconBlock::zero(); - block.attestations.push(Attestation::zero()); - let reference_hash = Hash256::from([42_u8; 32]); - block.active_state_root = reference_hash.clone(); - - let serialized = get_block_ssz(&block); - let ssz_block = SszBeaconBlock::from_slice(&serialized).unwrap(); - - assert_eq!(ssz_block.act_state_root(), &reference_hash.to_vec()[..]); - } - - #[test] - fn test_ssz_block_cry_state_root() { - let mut block = BeaconBlock::zero(); - block.attestations.push(Attestation::zero()); - let reference_hash = Hash256::from([42_u8; 32]); - block.crystallized_state_root = reference_hash.clone(); - - let serialized = get_block_ssz(&block); - let ssz_block = SszBeaconBlock::from_slice(&serialized).unwrap(); - - assert_eq!(ssz_block.cry_state_root(), &reference_hash.to_vec()[..]); - } -} diff --git a/lighthouse/db/Cargo.toml b/lighthouse/db/Cargo.toml index a8cfb88d48..cdad7a9d8a 100644 --- a/lighthouse/db/Cargo.toml +++ b/lighthouse/db/Cargo.toml @@ -10,5 +10,4 @@ bls = { path = "../../beacon_chain/utils/bls" } bytes = "0.4.10" rocksdb = "0.10.1" ssz = { path = "../../beacon_chain/utils/ssz" } -ssz_helpers = { path = "../../beacon_chain/utils/ssz_helpers" } types = { path = "../../beacon_chain/types" } diff --git a/lighthouse/db/src/stores/beacon_block_store.rs b/lighthouse/db/src/stores/beacon_block_store.rs index cad353eb26..5f777ea0ae 100644 --- a/lighthouse/db/src/stores/beacon_block_store.rs +++ b/lighthouse/db/src/stores/beacon_block_store.rs @@ -1,9 +1,8 @@ -extern crate ssz_helpers; - -use self::ssz_helpers::ssz_beacon_block::SszBeaconBlock; use super::BLOCKS_DB_COLUMN as DB_COLUMN; use super::{ClientDB, DBError}; +use ssz::{Decodable, DecodeError}; use std::sync::Arc; +use types::Hash256; type BeaconBlockHash = Vec; type BeaconBlockSsz = Vec; @@ -60,21 +59,29 @@ impl BeaconBlockStore { match self.get_serialized_block(head_hash)? { None => Err(BeaconBlockAtSlotError::UnknownBeaconBlock), Some(ssz) => { - let block = SszBeaconBlock::from_slice(&ssz) + let (retrieved_slot, parent_hash) = slot_and_parent_from_block_ssz(&ssz, 0) .map_err(|_| BeaconBlockAtSlotError::InvalidBeaconBlock)?; - match block.slot() { + match retrieved_slot { s if s == slot => Ok(Some((head_hash.to_vec(), ssz.to_vec()))), s if s < slot => Ok(None), - _ => match block.parent_hash() { - Some(parent_hash) => self.block_at_slot(parent_hash, slot), - None => Err(BeaconBlockAtSlotError::UnknownBeaconBlock), - }, + _ => self.block_at_slot(&parent_hash, slot), } } } } } +/// Read `block.slot` and `block.parent_root` from a SSZ-encoded block bytes. +/// +/// Assumes the block starts at byte `i`. +fn slot_and_parent_from_block_ssz(ssz: &[u8], i: usize) -> Result<(u64, Hash256), DecodeError> { + // Assuming the slot is the first field on a block. + let (slot, i) = u64::ssz_decode(&ssz, i)?; + // Assuming the parent has is the second field on a block. + let (parent_root, _) = Hash256::ssz_decode(&ssz, i)?; + Ok((slot, parent_root)) +} + impl From for BeaconBlockAtSlotError { fn from(e: DBError) -> Self { BeaconBlockAtSlotError::DBError(e.message) @@ -83,19 +90,17 @@ impl From for BeaconBlockAtSlotError { #[cfg(test)] mod tests { - extern crate ssz; - extern crate types; - - use self::ssz::SszStream; - use self::types::attestation::Attestation; - use self::types::beacon_block::BeaconBlock; - use self::types::Hash256; - use super::super::super::MemoryDB; use super::*; + use std::sync::Arc; use std::thread; + use ssz::ssz_encode; + use types::test_utils::{SeedableRng, TestRandom, XorShiftRng}; + use types::BeaconBlock; + use types::Hash256; + #[test] fn test_put_serialized_block() { let db = Arc::new(MemoryDB::open()); @@ -247,60 +252,58 @@ mod tests { fn test_block_at_slot() { let db = Arc::new(MemoryDB::open()); let bs = Arc::new(BeaconBlockStore::new(db.clone())); + let mut rng = XorShiftRng::from_seed([42; 16]); - let blocks = (0..5).into_iter().map(|_| { - let mut block = BeaconBlock::zero(); - let ar = Attestation::zero(); - block.attestations.push(ar); - block - }); - + // Specify test block parameters. let hashes = [ - Hash256::from("zero".as_bytes()), - Hash256::from("one".as_bytes()), - Hash256::from("two".as_bytes()), - Hash256::from("three".as_bytes()), - Hash256::from("four".as_bytes()), + Hash256::from(&[0; 32][..]), + Hash256::from(&[1; 32][..]), + Hash256::from(&[2; 32][..]), + Hash256::from(&[3; 32][..]), + Hash256::from(&[4; 32][..]), ]; - let parent_hashes = [ - Hash256::from("genesis".as_bytes()), - Hash256::from("zero".as_bytes()), - Hash256::from("one".as_bytes()), - Hash256::from("two".as_bytes()), - Hash256::from("three".as_bytes()), + Hash256::from(&[255; 32][..]), // Genesis block. + Hash256::from(&[0; 32][..]), + Hash256::from(&[1; 32][..]), + Hash256::from(&[2; 32][..]), + Hash256::from(&[3; 32][..]), ]; - let slots = [0, 1, 3, 4, 5]; - for (i, mut block) in blocks.enumerate() { - block.ancestor_hashes.push(parent_hashes[i]); + // Generate a vec of random blocks and store them in the DB. + let block_count = 5; + let mut blocks: Vec = Vec::with_capacity(5); + for i in 0..block_count { + let mut block = BeaconBlock::random_for_test(&mut rng); + + block.parent_root = parent_hashes[i]; block.slot = slots[i]; - let mut s = SszStream::new(); - s.append(&block); - let ssz = s.drain(); + + let ssz = ssz_encode(&block); db.put(DB_COLUMN, &hashes[i].to_vec(), &ssz).unwrap(); + + // Ensure the slot and parent_root decoding fn works correctly. + let (decoded_slot, decoded_parent_root) = + slot_and_parent_from_block_ssz(&ssz, 0).unwrap(); + assert_eq!(decoded_slot, block.slot); + assert_eq!(decoded_parent_root, block.parent_root); + + blocks.push(block); } - let tuple = bs.block_at_slot(&hashes[4], 5).unwrap().unwrap(); - let block = SszBeaconBlock::from_slice(&tuple.1).unwrap(); - assert_eq!(block.slot(), 5); - assert_eq!(tuple.0, hashes[4].to_vec()); - - let tuple = bs.block_at_slot(&hashes[4], 4).unwrap().unwrap(); - let block = SszBeaconBlock::from_slice(&tuple.1).unwrap(); - assert_eq!(block.slot(), 4); - assert_eq!(tuple.0, hashes[3].to_vec()); - - let tuple = bs.block_at_slot(&hashes[4], 3).unwrap().unwrap(); - let block = SszBeaconBlock::from_slice(&tuple.1).unwrap(); - assert_eq!(block.slot(), 3); - assert_eq!(tuple.0, hashes[2].to_vec()); - - let tuple = bs.block_at_slot(&hashes[4], 0).unwrap().unwrap(); - let block = SszBeaconBlock::from_slice(&tuple.1).unwrap(); - assert_eq!(block.slot(), 0); - assert_eq!(tuple.0, hashes[0].to_vec()); + // Test that certain slots can be reached from certain hashes. + let test_cases = vec![(4, 4), (4, 3), (4, 2), (4, 1), (4, 0)]; + for (hashes_index, slot_index) in test_cases { + let (matched_block_hash, matched_block_ssz) = bs + .block_at_slot(&hashes[hashes_index], slots[slot_index]) + .unwrap() + .unwrap(); + let (retrieved_slot, _) = + slot_and_parent_from_block_ssz(&matched_block_ssz, 0).unwrap(); + assert_eq!(retrieved_slot, slots[slot_index]); + assert_eq!(matched_block_hash, hashes[slot_index].to_vec()); + } let ssz = bs.block_at_slot(&hashes[4], 2).unwrap(); assert_eq!(ssz, None);