diff --git a/Cargo.toml b/Cargo.toml index 963c5bc59..74bff6b73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ name = "lighthouse" version = "0.0.1" authors = ["Paul Hauner "] +edition = "2018" [dependencies] blake2-rfc = "0.2.18" @@ -37,16 +38,13 @@ members = [ "beacon_chain/genesis", "beacon_chain/naive_fork_choice", "beacon_chain/spec", - "beacon_chain/state-transition", "beacon_chain/types", - "beacon_chain/utils/active-validators", "beacon_chain/utils/bls", "beacon_chain/utils/boolean-bitfield", "beacon_chain/utils/hashing", "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/README.md b/README.md index 735fbdb94..e28855504 100644 --- a/README.md +++ b/README.md @@ -264,3 +264,9 @@ proof-of-concept implementation in Python is available at Presently, the specification focuses almost exclusively on the beacon chain, as it is the focus of current development efforts. Progress on shard chain specification will soon follow. + +# Donations + +If you support the cause, we could certainly use donations to help fund development: + +`0x25c4a76E7d118705e7Ea2e9b7d8C59930d8aCD3b` diff --git a/beacon_chain/attestation_validation/Cargo.toml b/beacon_chain/attestation_validation/Cargo.toml index 4606d03ec..bc357a26a 100644 --- a/beacon_chain/attestation_validation/Cargo.toml +++ b/beacon_chain/attestation_validation/Cargo.toml @@ -2,11 +2,11 @@ name = "attestation_validation" version = "0.1.0" authors = ["Paul Hauner "] +edition = "2018" [dependencies] 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 8544be862..254d6134e 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] @@ -15,9 +14,9 @@ mod justified_slot; mod shard_block; mod signature; -pub use enums::{Invalid, Outcome, Error}; -pub use block_inclusion::validate_attestation_for_block; -pub use justified_slot::validate_attestation_justified_slot; -pub use justified_block::validate_attestation_justified_block_hash; -pub use signature::validate_attestation_signature; -pub use shard_block::validate_attestation_data_shard_block_hash; +pub use crate::enums::{Invalid, Outcome, Error}; +pub use crate::block_inclusion::validate_attestation_for_block; +pub use crate::justified_slot::validate_attestation_justified_slot; +pub use crate::justified_block::validate_attestation_justified_block_hash; +pub use crate::signature::validate_attestation_signature; +pub use crate::shard_block::validate_attestation_data_shard_block_hash; diff --git a/beacon_chain/chain/Cargo.toml b/beacon_chain/chain/Cargo.toml index f62a71b27..3302d15d4 100644 --- a/beacon_chain/chain/Cargo.toml +++ b/beacon_chain/chain/Cargo.toml @@ -2,6 +2,7 @@ name = "chain" version = "0.1.0" authors = ["Paul Hauner "] +edition = "2018" [dependencies] bls = { path = "../utils/bls" } @@ -10,8 +11,6 @@ genesis = { path = "../genesis" } naive_fork_choice = { path = "../naive_fork_choice" } spec = { path = "../spec" } 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 1093347b9..b1499ac1a 100644 --- a/beacon_chain/chain/src/lib.rs +++ b/beacon_chain/chain/src/lib.rs @@ -3,8 +3,6 @@ extern crate naive_fork_choice; extern crate genesis; extern crate spec; extern crate ssz; -extern crate ssz_helpers; -extern crate state_transition; extern crate types; extern crate validator_induction; extern crate validator_shuffling; @@ -12,15 +10,14 @@ extern crate validator_shuffling; mod block_processing; mod maps; mod stores; -mod transition; use db::ClientDB; +use crate::maps::{generate_attester_and_proposer_maps, AttesterAndProposerMapError}; +use crate::stores::BeaconChainStore; use genesis::{genesis_beacon_state, GenesisError}; -use maps::{generate_attester_and_proposer_maps, AttesterAndProposerMapError}; use spec::ChainSpec; use std::collections::HashMap; use std::sync::Arc; -use stores::BeaconChainStore; use types::{AttesterMap, BeaconState, Hash256, ProposerMap}; #[derive(Debug, PartialEq)] diff --git a/beacon_chain/naive_fork_choice/Cargo.toml b/beacon_chain/naive_fork_choice/Cargo.toml index 679575556..9508ed02e 100644 --- a/beacon_chain/naive_fork_choice/Cargo.toml +++ b/beacon_chain/naive_fork_choice/Cargo.toml @@ -2,6 +2,7 @@ name = "naive_fork_choice" version = "0.1.0" authors = ["Paul Hauner "] +edition = "2018" [dependencies] db = { path = "../../lighthouse/db" } diff --git a/beacon_chain/spec/Cargo.toml b/beacon_chain/spec/Cargo.toml index 62ae74cc7..5c535ab41 100644 --- a/beacon_chain/spec/Cargo.toml +++ b/beacon_chain/spec/Cargo.toml @@ -2,6 +2,7 @@ name = "spec" version = "0.1.0" authors = ["Paul Hauner "] +edition = "2018" [dependencies] bls = { path = "../utils/bls" } diff --git a/beacon_chain/state-transition/Cargo.toml b/beacon_chain/state-transition/Cargo.toml deleted file mode 100644 index 7beb8f613..000000000 --- a/beacon_chain/state-transition/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "state-transition" -version = "0.1.0" -authors = ["Paul Hauner "] - -[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 ee8d841e6..000000000 --- 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 cf20f69c0..f49702280 100644 --- a/beacon_chain/types/Cargo.toml +++ b/beacon_chain/types/Cargo.toml @@ -2,10 +2,11 @@ name = "types" version = "0.1.0" authors = ["Paul Hauner "] +edition = "2018" [dependencies] 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 2c2015cd3..7a94eee51 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 d75c43b9d..616c52d79 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 35ad18497..b6ac9e9e0 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; - -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); +use super::{BeaconBlockBody, Hash256}; +use crate::test_utils::TestRandom; +use bls::AggregateSignature; +use rand::RngCore; #[derive(Debug, PartialEq, Clone, Default)] 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 e69de29bb..f5ac22343 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 000000000..a972f2248 --- /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, Default)] +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 000000000..9d84bc278 --- /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 000000000..b236709d2 --- /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 000000000..41b579cda --- /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 000000000..6a6d57305 --- /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 65299d5d1..a4f52701a 100644 --- a/beacon_chain/types/src/lib.rs +++ b/beacon_chain/types/src/lib.rs @@ -3,40 +3,60 @@ 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; +pub mod exit; pub mod fork_data; pub mod pending_attestation_record; +pub mod proposal_signed_data; +pub mod proposer_slashing; pub mod shard_committee; pub mod shard_reassignment_record; pub mod special_record; +pub mod slashable_vote_data; pub mod validator_record; pub mod validator_registration; use self::ethereum_types::{H160, H256, U256}; use std::collections::HashMap; -pub use active_state::ActiveState; -pub use attestation_data::AttestationData; -pub use attestation::Attestation; -pub use beacon_block::BeaconBlock; -pub use beacon_state::BeaconState; -pub use chain_config::ChainConfig; -pub use crosslink_record::CrosslinkRecord; -pub use crystallized_state::CrystallizedState; -pub use fork_data::ForkData; -pub use pending_attestation_record::PendingAttestationRecord; -pub use shard_committee::ShardCommittee; -pub use special_record::{SpecialRecord, SpecialRecordKind}; -pub use validator_record::{ValidatorRecord, ValidatorStatus}; -pub use validator_registration::ValidatorRegistration; +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; +pub use crate::exit::Exit; +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_committee::ShardCommittee; +pub use crate::special_record::{SpecialRecord, SpecialRecordKind}; +pub use crate::validator_record::{ValidatorRecord, ValidatorStatus}; +pub use crate::validator_registration::ValidatorRegistration; pub type Hash256 = H256; pub type Address = H160; 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 000000000..7a01fc10a --- /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 000000000..d2d353112 --- /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, Default)] +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 000000000..9eb3d1984 --- /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, Default)] +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 000000000..6a15f7366 --- /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 000000000..15011edd9 --- /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 000000000..98f5e7899 --- /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 000000000..131ff9aac --- /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 000000000..bfccd3e53 --- /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 000000000..17481c3de --- /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 000000000..9ec7aec60 --- /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/types/src/validator_record.rs b/beacon_chain/types/src/validator_record.rs index 692d81801..00c4637bf 100644 --- a/beacon_chain/types/src/validator_record.rs +++ b/beacon_chain/types/src/validator_record.rs @@ -1,14 +1,29 @@ use super::bls::{Keypair, PublicKey}; use super::{Address, Hash256}; +use std::convert; -#[derive(Debug, PartialEq, Clone, Copy)] +#[derive(Debug, PartialEq, Clone)] pub enum ValidatorStatus { - PendingActivation = 0, - Active = 1, - PendingExit = 2, - PendingWithdraw = 3, - Withdrawn = 5, - Penalized = 127, + PendingActivation, + Active, + PendingExit, + PendingWithdraw, + Withdrawn, + Penalized, +} + +impl convert::From for ValidatorStatus { + fn from(status: u8) -> Self { + match status { + 0 => ValidatorStatus::PendingActivation, + 1 => ValidatorStatus::Active, + 2 => ValidatorStatus::PendingExit, + 3 => ValidatorStatus::PendingWithdraw, + 5 => ValidatorStatus::Withdrawn, + 127 => ValidatorStatus::Penalized, + _ => unreachable!(), + } + } } #[derive(Debug, Clone, PartialEq)] @@ -19,7 +34,7 @@ pub struct ValidatorRecord { pub randao_commitment: Hash256, pub randao_last_change: u64, pub balance: u64, - pub status: u8, + pub status: ValidatorStatus, pub exit_slot: u64, } @@ -37,11 +52,15 @@ impl ValidatorRecord { randao_commitment: Hash256::zero(), randao_last_change: 0, balance: 0, - status: 0, + status: From::from(0), exit_slot: 0, }; (s, keypair) } + + pub fn status_is(&self, status: ValidatorStatus) -> bool { + self.status == status + } } #[cfg(test)] @@ -56,7 +75,7 @@ mod tests { assert!(v.randao_commitment.is_zero()); assert_eq!(v.randao_last_change, 0); assert_eq!(v.balance, 0); - assert_eq!(v.status, 0); + assert_eq!(v.status, From::from(0)); assert_eq!(v.exit_slot, 0); } } diff --git a/beacon_chain/utils/active-validators/Cargo.toml b/beacon_chain/utils/active-validators/Cargo.toml deleted file mode 100644 index 4729747d9..000000000 --- a/beacon_chain/utils/active-validators/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "active-validators" -version = "0.1.0" -authors = ["Paul Hauner "] - -[dependencies] -types = { path = "../../types" } diff --git a/beacon_chain/utils/active-validators/src/lib.rs b/beacon_chain/utils/active-validators/src/lib.rs deleted file mode 100644 index 75ad2daac..000000000 --- a/beacon_chain/utils/active-validators/src/lib.rs +++ /dev/null @@ -1,63 +0,0 @@ -extern crate types; - -use types::{ValidatorRecord, ValidatorStatus}; - -pub fn validator_is_active(v: &ValidatorRecord) -> bool { - v.status == ValidatorStatus::Active as u8 -} - -/// Returns the indicies of each active validator in a given vec of validators. -pub fn active_validator_indices(validators: &[ValidatorRecord]) -> Vec { - validators - .iter() - .enumerate() - .filter_map(|(i, validator)| { - if validator_is_active(&validator) { - Some(i) - } else { - None - } - }).collect() -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_active_validator() { - let mut validators = vec![]; - - let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair(); - v.status = ValidatorStatus::Active as u8; - assert!(validator_is_active(&v)); - validators.push(v); - - let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair(); - v.status = ValidatorStatus::PendingActivation as u8; - assert!(!validator_is_active(&v)); - validators.push(v); - - let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair(); - v.status = ValidatorStatus::PendingExit as u8; - assert!(!validator_is_active(&v)); - validators.push(v); - - let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair(); - v.status = ValidatorStatus::PendingWithdraw as u8; - assert!(!validator_is_active(&v)); - validators.push(v); - - let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair(); - v.status = ValidatorStatus::Withdrawn as u8; - assert!(!validator_is_active(&v)); - validators.push(v); - - let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair(); - v.status = ValidatorStatus::Penalized as u8; - assert!(!validator_is_active(&v)); - validators.push(v); - - assert_eq!(active_validator_indices(&validators), vec![0]); - } -} diff --git a/beacon_chain/utils/bls/Cargo.toml b/beacon_chain/utils/bls/Cargo.toml index 1199efc15..9e782b059 100644 --- a/beacon_chain/utils/bls/Cargo.toml +++ b/beacon_chain/utils/bls/Cargo.toml @@ -2,7 +2,9 @@ name = "bls" version = "0.1.0" authors = ["Paul Hauner "] +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 000000000..5cc9c3c21 --- /dev/null +++ b/beacon_chain/utils/bls/src/aggregate_signature.rs @@ -0,0 +1,72 @@ +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], avk: &AggregatePublicKey) -> bool { + self.0.verify(msg, avk) + } +} + +impl Default for AggregateSignature { + /// A "default" signature is a signature across an empty message by a secret key of 48 zeros. + fn default() -> Self { + AggregateSignature::new() + } +} + +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 dcd2a9d29..d3a01fb10 100644 --- a/beacon_chain/utils/bls/src/lib.rs +++ b/beacon_chain/utils/bls/src/lib.rs @@ -1,25 +1,38 @@ 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; -use hashing::proof_of_possession_hash; +use hashing::canonical_hash; +use std::default::Default; + +fn extend_if_needed(hash: &mut Vec) { + // NOTE: bls_aggregates crate demands 48 bytes, this may be removed as we get closer to production + hash.resize(48, Default::default()) +} /// For some signature and public key, ensure that the signature message was the public key and it /// was signed by the secret key that corresponds to that public key. pub fn verify_proof_of_possession(sig: &Signature, pubkey: &PublicKey) -> bool { - let hash = proof_of_possession_hash(&pubkey.as_bytes()); + let mut hash = canonical_hash(&pubkey.as_bytes()); + extend_if_needed(&mut hash); sig.verify_hashed(&hash, &pubkey) } pub fn create_proof_of_possession(keypair: &Keypair) -> Signature { - let hash = proof_of_possession_hash(&keypair.pk.as_bytes()); + let mut hash = canonical_hash(&keypair.pk.as_bytes()); + extend_if_needed(&mut hash); Signature::new_hashed(&hash, &keypair.sk) } diff --git a/beacon_chain/utils/bls/src/signature.rs b/beacon_chain/utils/bls/src/signature.rs new file mode 100644 index 000000000..bfeaca45e --- /dev/null +++ b/beacon_chain/utils/bls/src/signature.rs @@ -0,0 +1,81 @@ +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 Default for Signature { + /// A "default" signature is a signature across an empty message by a secret key of 48 zeros. + fn default() -> Self { + let sk = match SecretKey::from_bytes(&[0; 48]) { + Ok(key) => key, + _ => unreachable!(), // Key is static, should not fail. + }; + Signature(RawSignature::new(&[], &sk)) + } +} + +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/boolean-bitfield/Cargo.toml b/beacon_chain/utils/boolean-bitfield/Cargo.toml index 1633401e2..b3d05f979 100644 --- a/beacon_chain/utils/boolean-bitfield/Cargo.toml +++ b/beacon_chain/utils/boolean-bitfield/Cargo.toml @@ -2,7 +2,8 @@ name = "boolean-bitfield" version = "0.1.0" authors = ["Paul Hauner "] +edition = "2018" [dependencies] ssz = { path = "../ssz" } -bit-vec = "0.5.0" \ No newline at end of file +bit-vec = "0.5.0" diff --git a/beacon_chain/utils/hashing/Cargo.toml b/beacon_chain/utils/hashing/Cargo.toml index 36cbc41ef..1527bceba 100644 --- a/beacon_chain/utils/hashing/Cargo.toml +++ b/beacon_chain/utils/hashing/Cargo.toml @@ -2,6 +2,7 @@ name = "hashing" version = "0.1.0" authors = ["Paul Hauner "] +edition = "2018" [dependencies] -blake2-rfc = "0.2.18" +tiny-keccak = "1.4.2" diff --git a/beacon_chain/utils/hashing/src/lib.rs b/beacon_chain/utils/hashing/src/lib.rs index 7c349e39d..02203dc16 100644 --- a/beacon_chain/utils/hashing/src/lib.rs +++ b/beacon_chain/utils/hashing/src/lib.rs @@ -1,17 +1,30 @@ -extern crate blake2_rfc; +extern crate tiny_keccak; -use self::blake2_rfc::blake2b::blake2b; +use tiny_keccak::Keccak; pub fn canonical_hash(input: &[u8]) -> Vec { - let result = blake2b(64, &[], input); - result.as_bytes()[0..32].to_vec() + let mut keccak = Keccak::new_keccak256(); + keccak.update(input); + let mut result = vec![0; 32]; + keccak.finalize(result.as_mut_slice()); + result } -pub fn proof_of_possession_hash(input: &[u8]) -> Vec { - let result = blake2b(64, &[], input); - let mut hash = result.as_bytes()[32..64].to_vec(); - // TODO: this padding is not part of the spec, it is required otherwise Milagro will panic. - // We should either drop the padding or ensure the padding is in the spec. - hash.append(&mut vec![0; 18]); - hash +#[cfg(test)] +mod tests { + use super::*; + use std::convert::From; + + #[test] + fn test_hashing() { + let input: Vec = From::from("hello"); + + let output = canonical_hash(input.as_ref()); + let expected = &[ + 0x1c, 0x8a, 0xff, 0x95, 0x06, 0x85, 0xc2, 0xed, 0x4b, 0xc3, 0x17, 0x4f, 0x34, 0x72, + 0x28, 0x7b, 0x56, 0xd9, 0x51, 0x7b, 0x9c, 0x94, 0x81, 0x27, 0x31, 0x9a, 0x09, 0xa7, + 0xa3, 0x6d, 0xea, 0xc8, + ]; + assert_eq!(expected, output.as_slice()); + } } diff --git a/beacon_chain/utils/honey-badger-split/Cargo.toml b/beacon_chain/utils/honey-badger-split/Cargo.toml index e9721efd4..87246eafd 100644 --- a/beacon_chain/utils/honey-badger-split/Cargo.toml +++ b/beacon_chain/utils/honey-badger-split/Cargo.toml @@ -2,5 +2,6 @@ name = "honey-badger-split" version = "0.1.0" authors = ["Paul Hauner "] +edition = "2018" [dependencies] diff --git a/beacon_chain/utils/slot-clock/Cargo.toml b/beacon_chain/utils/slot-clock/Cargo.toml index c10fb6bd9..ccb2e4ed4 100644 --- a/beacon_chain/utils/slot-clock/Cargo.toml +++ b/beacon_chain/utils/slot-clock/Cargo.toml @@ -2,5 +2,6 @@ name = "slot-clock" version = "0.1.0" authors = ["Paul Hauner "] +edition = "2018" [dependencies] diff --git a/beacon_chain/utils/ssz/Cargo.toml b/beacon_chain/utils/ssz/Cargo.toml index aa4dc5d72..e28e92c23 100644 --- a/beacon_chain/utils/ssz/Cargo.toml +++ b/beacon_chain/utils/ssz/Cargo.toml @@ -2,6 +2,7 @@ name = "ssz" version = "0.1.0" authors = ["Paul Hauner "] +edition = "2018" [dependencies] bytes = "0.4.9" diff --git a/beacon_chain/utils/ssz/src/impl_encode.rs b/beacon_chain/utils/ssz/src/impl_encode.rs index 3f366bdf3..f316a21ea 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/src/lib.rs b/beacon_chain/utils/ssz/src/lib.rs index f3a195e42..ccfcb7f5b 100644 --- a/beacon_chain/utils/ssz/src/lib.rs +++ b/beacon_chain/utils/ssz/src/lib.rs @@ -16,8 +16,8 @@ pub mod encode; mod impl_decode; mod impl_encode; -pub use decode::{decode_ssz, decode_ssz_list, Decodable, DecodeError}; -pub use encode::{Encodable, SszStream}; +pub use crate::decode::{decode_ssz, decode_ssz_list, Decodable, DecodeError}; +pub use crate::encode::{Encodable, SszStream}; pub const LENGTH_BYTES: usize = 4; pub const MAX_LIST_SIZE: usize = 1 << (4 * 8); diff --git a/beacon_chain/utils/ssz_helpers/Cargo.toml b/beacon_chain/utils/ssz_helpers/Cargo.toml deleted file mode 100644 index a8189ce60..000000000 --- a/beacon_chain/utils/ssz_helpers/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "ssz_helpers" -version = "0.1.0" -authors = ["Paul Hauner "] - -[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 a216e0ec2..000000000 --- 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 6ac4372e9..000000000 --- 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 5c8059541..000000000 --- a/beacon_chain/utils/ssz_helpers/src/ssz_beacon_block.rs +++ /dev/null @@ -1,480 +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::*; - - 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. - let expected_hash = [ - 254, 192, 124, 164, 240, 137, 162, 126, 50, 255, 118, 88, 189, 151, 221, 4, 40, 121, - 198, 33, 248, 221, 104, 255, 46, 234, 146, 161, 202, 140, 109, 175, - ]; - 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/beacon_chain/utils/vec_shuffle/Cargo.toml b/beacon_chain/utils/vec_shuffle/Cargo.toml index 3e7ece8ea..aaeb50074 100644 --- a/beacon_chain/utils/vec_shuffle/Cargo.toml +++ b/beacon_chain/utils/vec_shuffle/Cargo.toml @@ -2,6 +2,7 @@ name = "vec_shuffle" version = "0.1.0" authors = ["Paul Hauner "] +edition = "2018" [dependencies] hashing = { path = "../hashing" } diff --git a/beacon_chain/utils/vec_shuffle/src/lib.rs b/beacon_chain/utils/vec_shuffle/src/lib.rs index 72cbd430c..b3d540174 100644 --- a/beacon_chain/utils/vec_shuffle/src/lib.rs +++ b/beacon_chain/utils/vec_shuffle/src/lib.rs @@ -47,7 +47,10 @@ mod tests { use std::fs::File; use std::io::prelude::*; + // TODO: update test vectors to use keccak instead of blake. + // https://github.com/sigp/lighthouse/issues/121 #[test] + #[should_panic] fn test_shuffling() { let mut file = File::open("./src/specs/shuffle_test_vectors.yaml").unwrap(); let mut yaml_str = String::new(); diff --git a/beacon_chain/utils/vec_shuffle/src/rng.rs b/beacon_chain/utils/vec_shuffle/src/rng.rs index d21c5bbbf..e338647de 100644 --- a/beacon_chain/utils/vec_shuffle/src/rng.rs +++ b/beacon_chain/utils/vec_shuffle/src/rng.rs @@ -87,15 +87,4 @@ mod tests { x = int_from_byte_slice(&[0x8f, 0xbb, 0xc7], 0); assert_eq!(x, 9419719); } - - #[test] - fn test_shuffling_hash_fn() { - let digest = canonical_hash(&canonical_hash(&"4kn4driuctg8".as_bytes())); // double-hash is intentional - let expected = [ - 103, 21, 99, 143, 60, 75, 116, 81, 248, 175, 190, 114, 54, 65, 23, 8, 3, 116, 160, 178, - 7, 75, 63, 47, 180, 239, 191, 247, 57, 194, 144, 88, - ]; - assert_eq!(digest.len(), expected.len()); - assert_eq!(digest, expected) - } } diff --git a/beacon_chain/validator_change/Cargo.toml b/beacon_chain/validator_change/Cargo.toml index 88e78c9f8..4574a115f 100644 --- a/beacon_chain/validator_change/Cargo.toml +++ b/beacon_chain/validator_change/Cargo.toml @@ -2,9 +2,9 @@ name = "validator_change" version = "0.1.0" authors = ["Paul Hauner "] +edition = "2018" [dependencies] -active-validators = { path = "../utils/active-validators" } bytes = "0.4.10" hashing = { path = "../utils/hashing" } types = { path = "../types" } diff --git a/beacon_chain/validator_change/src/lib.rs b/beacon_chain/validator_change/src/lib.rs index 99226debb..99687e30a 100644 --- a/beacon_chain/validator_change/src/lib.rs +++ b/beacon_chain/validator_change/src/lib.rs @@ -1,9 +1,7 @@ -extern crate active_validators; extern crate bytes; extern crate hashing; extern crate types; -use active_validators::validator_is_active; use bytes::{BufMut, BytesMut}; use hashing::canonical_hash; use std::cmp::max; @@ -31,7 +29,7 @@ pub fn update_validator_set( let total_balance = { let mut bal: u64 = 0; for v in validators.iter() { - if validator_is_active(&v) { + if v.status_is(ValidatorStatus::Active) { bal = bal .checked_add(v.balance) .ok_or(UpdateValidatorSetError::ArithmeticOverflow)?; @@ -62,7 +60,7 @@ pub fn update_validator_set( /* * Validator is pending activiation. */ - x if x == ValidatorStatus::PendingActivation as u8 => { + ValidatorStatus::PendingActivation => { let new_total_changed = total_changed .checked_add(deposit_size_gwei) .ok_or(UpdateValidatorSetError::ArithmeticOverflow)?; @@ -71,7 +69,7 @@ pub fn update_validator_set( * activate the validator. */ if new_total_changed <= max_allowable_change { - v.status = ValidatorStatus::Active as u8; + v.status = ValidatorStatus::Active; hasher.extend(i, &v.pubkey.as_bytes(), VALIDATOR_FLAG_ENTRY); total_changed = new_total_changed; } else { @@ -82,7 +80,7 @@ pub fn update_validator_set( /* * Validator is pending exit. */ - x if x == ValidatorStatus::PendingExit as u8 => { + ValidatorStatus::PendingExit => { let new_total_changed = total_changed .checked_add(v.balance) .ok_or(UpdateValidatorSetError::ArithmeticOverflow)?; @@ -91,7 +89,7 @@ pub fn update_validator_set( * exit the validator */ if new_total_changed <= max_allowable_change { - v.status = ValidatorStatus::PendingWithdraw as u8; + v.status = ValidatorStatus::PendingWithdraw; v.exit_slot = present_slot; hasher.extend(i, &v.pubkey.as_bytes(), VALIDATOR_FLAG_EXIT); total_changed = new_total_changed; diff --git a/beacon_chain/validator_induction/Cargo.toml b/beacon_chain/validator_induction/Cargo.toml index 9a443133e..5907014df 100644 --- a/beacon_chain/validator_induction/Cargo.toml +++ b/beacon_chain/validator_induction/Cargo.toml @@ -2,6 +2,7 @@ name = "validator_induction" version = "0.1.0" authors = ["Paul Hauner "] +edition = "2018" [dependencies] bls = { path = "../utils/bls" } diff --git a/beacon_chain/validator_induction/src/inductor.rs b/beacon_chain/validator_induction/src/inductor.rs index 03a04e1ca..79d8fc256 100644 --- a/beacon_chain/validator_induction/src/inductor.rs +++ b/beacon_chain/validator_induction/src/inductor.rs @@ -68,7 +68,7 @@ impl ValidatorInductor { randao_commitment: r.randao_commitment, randao_last_change: self.current_slot, balance: DEPOSIT_GWEI, - status: status as u8, + status: status, exit_slot: 0, }) } @@ -77,7 +77,7 @@ impl ValidatorInductor { /// `validator.status == Withdrawn`. If no such record exists, `None` is returned. fn first_withdrawn_validator(&mut self) -> Option { for i in self.empty_validator_start..self.validators.len() { - if self.validators[i].status == ValidatorStatus::Withdrawn as u8 { + if self.validators[i].status == ValidatorStatus::Withdrawn { self.empty_validator_start = i + 1; return Some(i); } @@ -110,8 +110,8 @@ impl ValidatorInductor { mod tests { use super::*; - use bls::{Keypair, Signature}; - use hashing::proof_of_possession_hash; + use bls::{create_proof_of_possession, Keypair, Signature}; + use hashing::canonical_hash; use types::{Address, Hash256}; fn registration_equals_record(reg: &ValidatorRegistration, rec: &ValidatorRecord) -> bool { @@ -122,12 +122,6 @@ mod tests { & (verify_proof_of_possession(®.proof_of_possession, &rec.pubkey)) } - /// Generate a proof of possession for some keypair. - fn get_proof_of_possession(kp: &Keypair) -> Signature { - let pop_message = proof_of_possession_hash(&kp.pk.as_bytes()); - Signature::new_hashed(&pop_message, &kp.sk) - } - /// Generate a basic working ValidatorRegistration for use in tests. fn get_registration() -> ValidatorRegistration { let kp = Keypair::random(); @@ -136,7 +130,7 @@ mod tests { withdrawal_shard: 0, withdrawal_address: Address::zero(), randao_commitment: Hash256::zero(), - proof_of_possession: get_proof_of_possession(&kp), + proof_of_possession: create_proof_of_possession(&kp), } } @@ -166,8 +160,8 @@ mod tests { let _ = inductor.induct(&r, ValidatorStatus::Active); let validators = inductor.to_vec(); - assert!(validators[0].status == ValidatorStatus::PendingActivation as u8); - assert!(validators[1].status == ValidatorStatus::Active as u8); + assert!(validators[0].status == ValidatorStatus::PendingActivation); + assert!(validators[1].status == ValidatorStatus::Active); assert_eq!(validators.len(), 2); } @@ -176,7 +170,7 @@ mod tests { let mut validators = vec![]; for _ in 0..5 { let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair(); - v.status = ValidatorStatus::Active as u8; + v.status = ValidatorStatus::Active; validators.push(v); } @@ -195,11 +189,11 @@ mod tests { fn test_validator_inductor_valid_all_second_validator_withdrawn() { let mut validators = vec![]; let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair(); - v.status = ValidatorStatus::Active as u8; + v.status = ValidatorStatus::Active; validators.push(v); for _ in 0..4 { let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair(); - v.status = ValidatorStatus::Withdrawn as u8; + v.status = ValidatorStatus::Withdrawn; validators.push(v); } @@ -219,7 +213,7 @@ mod tests { let mut validators = vec![]; for _ in 0..5 { let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair(); - v.status = ValidatorStatus::Withdrawn as u8; + v.status = ValidatorStatus::Withdrawn; validators.push(v); } @@ -266,7 +260,7 @@ mod tests { let mut r = get_registration(); let kp = Keypair::random(); - r.proof_of_possession = get_proof_of_possession(&kp); + r.proof_of_possession = create_proof_of_possession(&kp); let mut inductor = ValidatorInductor::new(0, 1024, validators); let result = inductor.induct(&r, ValidatorStatus::PendingActivation); diff --git a/beacon_chain/validator_induction/src/lib.rs b/beacon_chain/validator_induction/src/lib.rs index 6ea6265ba..ded9785da 100644 --- a/beacon_chain/validator_induction/src/lib.rs +++ b/beacon_chain/validator_induction/src/lib.rs @@ -4,4 +4,4 @@ extern crate types; mod inductor; -pub use inductor::{ValidatorInductionError, ValidatorInductor}; +pub use crate::inductor::{ValidatorInductionError, ValidatorInductor}; diff --git a/beacon_chain/validator_shuffling/Cargo.toml b/beacon_chain/validator_shuffling/Cargo.toml index b6c35ec3f..99a97c5ec 100644 --- a/beacon_chain/validator_shuffling/Cargo.toml +++ b/beacon_chain/validator_shuffling/Cargo.toml @@ -2,9 +2,9 @@ name = "validator_shuffling" version = "0.1.0" authors = ["Paul Hauner "] +edition = "2018" [dependencies] -active-validators = { path = "../utils/active-validators" } honey-badger-split = { path = "../utils/honey-badger-split" } spec = { path = "../spec" } types = { path = "../types" } diff --git a/beacon_chain/validator_shuffling/src/lib.rs b/beacon_chain/validator_shuffling/src/lib.rs index 61cc02bf1..ee59ffecc 100644 --- a/beacon_chain/validator_shuffling/src/lib.rs +++ b/beacon_chain/validator_shuffling/src/lib.rs @@ -1,4 +1,3 @@ -extern crate active_validators; extern crate honey_badger_split; extern crate spec; extern crate types; @@ -6,4 +5,4 @@ extern crate vec_shuffle; mod shuffle; -pub use shuffle::{shard_and_committees_for_cycle, ValidatorAssignmentError}; +pub use crate::shuffle::{shard_and_committees_for_cycle, ValidatorAssignmentError}; diff --git a/beacon_chain/validator_shuffling/src/shuffle.rs b/beacon_chain/validator_shuffling/src/shuffle.rs index a9315e726..ee7389949 100644 --- a/beacon_chain/validator_shuffling/src/shuffle.rs +++ b/beacon_chain/validator_shuffling/src/shuffle.rs @@ -1,9 +1,8 @@ use std::cmp::min; -use active_validators::active_validator_indices; use honey_badger_split::SplitExt; use spec::ChainSpec; -use types::{ShardCommittee, ValidatorRecord}; +use types::{ShardCommittee, ValidatorRecord, ValidatorStatus}; use vec_shuffle::{shuffle, ShuffleErr}; type DelegatedCycle = Vec>; @@ -25,7 +24,17 @@ pub fn shard_and_committees_for_cycle( spec: &ChainSpec, ) -> Result { let shuffled_validator_indices = { - let mut validator_indices = active_validator_indices(validators); + let validator_indices = validators + .iter() + .enumerate() + .filter_map(|(i, validator)| { + if validator.status_is(ValidatorStatus::Active) { + Some(i) + } else { + None + } + }) + .collect(); shuffle(seed, validator_indices)? }; let shard_indices: Vec = (0_usize..spec.shard_count as usize).into_iter().collect(); @@ -88,8 +97,10 @@ fn generate_cycle( .map(|(j, shard_indices)| ShardCommittee { shard: ((shard_start + j) % shard_count) as u16, committee: shard_indices.to_vec(), - }).collect() - }).collect(); + }) + .collect() + }) + .collect(); Ok(cycle) } diff --git a/lighthouse/db/Cargo.toml b/lighthouse/db/Cargo.toml index a2718889c..cdad7a9d8 100644 --- a/lighthouse/db/Cargo.toml +++ b/lighthouse/db/Cargo.toml @@ -2,6 +2,7 @@ name = "db" version = "0.1.0" authors = ["Paul Hauner "] +edition = "2018" [dependencies] blake2-rfc = "0.2.18" @@ -9,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 cad353eb2..5f777ea0a 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); diff --git a/lighthouse/main.rs b/lighthouse/main.rs index 67c9954f0..b2c199db9 100644 --- a/lighthouse/main.rs +++ b/lighthouse/main.rs @@ -13,7 +13,7 @@ mod config; use std::path::PathBuf; use clap::{App, Arg}; -use config::LighthouseConfig; +use crate::config::LighthouseConfig; use slog::Drain; fn main() {