diff --git a/beacon_node/beacon_chain/src/block_production.rs b/beacon_node/beacon_chain/src/block_production.rs index c0a057ad2..d633b3490 100644 --- a/beacon_node/beacon_chain/src/block_production.rs +++ b/beacon_node/beacon_chain/src/block_production.rs @@ -68,7 +68,8 @@ where }, }; - let state = self.state_transition(parent_state, &block)?; + let state = + self.state_transition_without_verifying_block_signature(parent_state, &block)?; let state_root = state.canonical_root(); diff --git a/beacon_node/beacon_chain/src/canonical_head.rs b/beacon_node/beacon_chain/src/canonical_head.rs index acccb5de7..f6ab646a8 100644 --- a/beacon_node/beacon_chain/src/canonical_head.rs +++ b/beacon_node/beacon_chain/src/canonical_head.rs @@ -1,6 +1,11 @@ use crate::{BeaconChain, CheckPoint, ClientDB, SlotClock}; use std::sync::RwLockReadGuard; -use types::{BeaconBlock, BeaconState, Hash256}; +use types::{beacon_state::SlotProcessingError, BeaconBlock, BeaconState, Hash256}; + +pub enum Error { + PastSlot, + UnableToDetermineProducer, +} impl BeaconChain where @@ -31,4 +36,37 @@ where .read() .expect("CRITICAL: CanonicalHead poisioned.") } + + pub fn state(&self, slot: u64) -> Result { + let mut state = self + .canonical_head + .read() + .expect("CRITICAL: CanonicalHead poisioned.") + .beacon_state + .clone(); + let previous_block_root = self + .canonical_head + .read() + .expect("CRITICAL: CanonicalHead poisioned.") + .beacon_block_root + .clone(); + + match slot.checked_sub(state.slot) { + None => Err(Error::PastSlot), + Some(distance) => { + for _ in 0..distance { + state.per_slot_processing(previous_block_root.clone(), &self.spec)? + } + Ok(state) + } + } + } +} + +impl From for Error { + fn from(e: SlotProcessingError) -> Error { + match e { + SlotProcessingError::UnableToDetermineProducer => Error::UnableToDetermineProducer, + } + } } diff --git a/beacon_node/beacon_chain/src/info.rs b/beacon_node/beacon_chain/src/info.rs index 2025ba27a..eddb2fdb9 100644 --- a/beacon_node/beacon_chain/src/info.rs +++ b/beacon_node/beacon_chain/src/info.rs @@ -22,12 +22,10 @@ where } pub fn proposer_slots(&self, validator_index: usize) -> Option { - if let Some(validator) = self - .canonical_head() - .beacon_state - .validator_registry - .get(validator_index) - { + let slot = self.present_slot()?; + let state = self.state(slot).ok()?; + + if let Some(validator) = state.validator_registry.get(validator_index) { Some(validator.proposer_slots) } else { None @@ -42,8 +40,8 @@ where } pub fn block_proposer(&self, slot: u64) -> Option { - //TODO: this is a stub; fix. - let validator_count = self.canonical_head().beacon_state.validator_registry.len(); - Some((slot as usize) % validator_count) + let present_slot = self.present_slot()?; + let state = self.state(present_slot).ok()?; + state.get_beacon_proposer_index(slot, &self.spec) } } diff --git a/beacon_node/beacon_chain/src/state_transition.rs b/beacon_node/beacon_chain/src/state_transition.rs index 95fd9a7ad..fe2b0e7b1 100644 --- a/beacon_node/beacon_chain/src/state_transition.rs +++ b/beacon_node/beacon_chain/src/state_transition.rs @@ -4,8 +4,9 @@ use boolean_bitfield::BooleanBitfield; use slot_clock::{SystemTimeSlotClockError, TestingSlotClockError}; use ssz::ssz_encode; use types::{ - readers::BeaconBlockReader, AttestationData, AttestationDataAndCustodyBit, BeaconBlock, - BeaconState, Exit, Fork, Hash256, PendingAttestation, ProposalSignedData, + beacon_state::SlotProcessingError, readers::BeaconBlockReader, AttestationData, + AttestationDataAndCustodyBit, BeaconBlock, BeaconState, Exit, Fork, Hash256, + PendingAttestation, }; // TODO: define elsehwere. @@ -58,14 +59,31 @@ where U: SlotClock, { pub fn state_transition( + &self, + state: BeaconState, + block: &BeaconBlock, + ) -> Result { + self.internal_state_transition(state, block, true) + } + + pub fn state_transition_without_verifying_block_signature( + &self, + state: BeaconState, + block: &BeaconBlock, + ) -> Result { + self.internal_state_transition(state, block, false) + } + + fn internal_state_transition( &self, mut state: BeaconState, block: &BeaconBlock, + verify_block_signature: bool, ) -> Result { ensure!(state.slot < block.slot, Error::StateAlreadyTransitioned); for _ in state.slot..block.slot { - self.per_slot_processing(&mut state, &block.parent_root)?; + state.per_slot_processing(block.parent_root.clone(), &self.spec)?; } /* @@ -78,44 +96,35 @@ where * Proposer Signature */ - let block_without_signature_root = { - let mut block_without_signature = block.clone(); - block_without_signature.signature = self.spec.empty_signature.clone(); - block_without_signature.canonical_root() - }; - - let proposal_root = { - let proposal = ProposalSignedData { - slot: state.slot, - shard: self.spec.beacon_chain_shard_number, - block_root: block_without_signature_root, - }; - hash_tree_root(&proposal) - }; - - let block_proposer_index = - get_beacon_proposer_index(&state, block.slot, self.spec.epoch_length) - .ok_or(Error::NoBlockProducer)?; + let block_proposer_index = state + .get_beacon_proposer_index(block.slot, &self.spec) + .ok_or(Error::NoBlockProducer)?; let block_proposer = &state.validator_registry[block_proposer_index]; - ensure!( - bls_verify( - &block_proposer.pubkey, - &proposal_root, - &block.signature, - get_domain(&state.fork_data, state.slot, DOMAIN_PROPOSAL) - ), - Error::BadBlockSignature - ); + if verify_block_signature { + ensure!( + bls_verify( + &block_proposer.pubkey, + &block.proposal_root(&self.spec)[..], + &block.signature, + get_domain(&state.fork_data, state.slot, DOMAIN_PROPOSAL) + ), + Error::BadBlockSignature + ); + } /* * RANDAO */ + println!("proposer pubkey: {:?}", &block_proposer.pubkey); ensure!( bls_verify( &block_proposer.pubkey, - &ssz_encode(&block_proposer.proposer_slots), + // TODO: https://github.com/ethereum/eth2.0-specs/pull/496 + // + // &ssz_encode(&block_proposer.proposer_slots), + &ssz_encode(&block.slot), &block.randao_reveal, get_domain(&state.fork_data, state.slot, DOMAIN_RANDAO) ), @@ -358,35 +367,6 @@ where Ok(state) } - - fn per_slot_processing( - &self, - state: &mut BeaconState, - previous_block_root: &Hash256, - ) -> Result<(), Error> { - let epoch_length = self.spec.epoch_length; - let latest_randao_mixes_length = self.spec.latest_randao_mixes_length; - let latest_block_roots_length = self.spec.latest_block_roots_length; - - // Misc counters. - state.slot += 1; - let block_proposer = get_beacon_proposer_index(&state, state.slot, epoch_length) - .ok_or(Error::NoBlockProducer)?; - state.validator_registry[block_proposer].proposer_slots += 1; - state.latest_randao_mixes[(state.slot % latest_randao_mixes_length) as usize] = - state.latest_randao_mixes[((state.slot - 1) % latest_randao_mixes_length) as usize]; - - // Block roots. - state.latest_block_roots - [((state.slot - 1) % self.spec.latest_block_roots_length) as usize] = - *previous_block_root; - - if state.slot % latest_block_roots_length == 0 { - let root = merkle_root(&state.latest_block_roots[..]); - state.batched_block_roots.push(root); - } - Ok(()) - } } fn initiate_validator_exit(_state: &BeaconState, _index: u32) { @@ -450,20 +430,6 @@ fn hash_tree_root(_input: &T) -> Hash256 { Hash256::zero() } -fn merkle_root(_input: &[Hash256]) -> Hash256 { - // TODO: stubbed out. - Hash256::zero() -} - -fn get_beacon_proposer_index( - _state: &BeaconState, - _slot: u64, - _epoch_length: u64, -) -> Option { - // TODO: stubbed out. - Some(0) -} - impl From for Error { fn from(e: DBError) -> Error { Error::DBError(e.message) @@ -481,3 +447,11 @@ impl From for Error { Error::SlotClockError(e) } } + +impl From for Error { + fn from(e: SlotProcessingError) -> Error { + match e { + SlotProcessingError::UnableToDetermineProducer => Error::NoBlockProducer, + } + } +} diff --git a/beacon_node/beacon_chain/src/transition.rs b/beacon_node/beacon_chain/src/transition.rs deleted file mode 100644 index df434cc0c..000000000 --- a/beacon_node/beacon_chain/src/transition.rs +++ /dev/null @@ -1,29 +0,0 @@ -use super::BeaconChain; -use db::ClientDB; -use state_transition::{extend_active_state, StateTransitionError}; -use types::{ActiveState, BeaconBlock, CrystallizedState, Hash256}; - -impl BeaconChain -where - T: ClientDB + Sized, -{ - pub(crate) fn transition_states( - &self, - act_state: &ActiveState, - cry_state: &CrystallizedState, - block: &BeaconBlock, - block_hash: &Hash256, - ) -> Result<(ActiveState, Option), StateTransitionError> { - let state_recalc_distance = block - .slot - .checked_sub(cry_state.last_state_recalculation_slot) - .ok_or(StateTransitionError::BlockSlotBeforeRecalcSlot)?; - - if state_recalc_distance >= u64::from(self.spec.epoch_length) { - panic!("Not implemented!") - } else { - let new_act_state = extend_active_state(act_state, block, block_hash)?; - Ok((new_act_state, None)) - } - } -} diff --git a/beacon_node/beacon_chain/tests/chain.rs b/beacon_node/beacon_chain/tests/chain.rs index 9bf8959c5..af1310cd6 100644 --- a/beacon_node/beacon_chain/tests/chain.rs +++ b/beacon_node/beacon_chain/tests/chain.rs @@ -1,42 +1,15 @@ use self::utils::TestRig; -use beacon_chain::BeaconChain; -#[cfg(test)] -use block_producer::{test_utils::TestSigner, BlockProducer}; -use db::{ - stores::{BeaconBlockStore, BeaconStateStore}, - MemoryDB, -}; -use slot_clock::TestingSlotClock; -use std::sync::{Arc, RwLock}; -use types::{ChainSpec, Keypair, Validator}; +use types::ChainSpec; mod utils; #[test] -fn rig_can_generate_validators() { - /* - let (_db, mut chain) = in_memory_test_chain(ChainSpec::foundation()); - let validators = generate_validators(2, &chain); - chain.spec = inject_validators_into_spec(chain.spec.clone(), &validators[..]); - */ +fn it_can_produce_blocks() { let validator_count = 2; + let blocks = 3; + let mut rig = TestRig::new(ChainSpec::foundation(), validator_count); - rig.produce_next_slot(); + for _ in 0..blocks { + rig.produce_next_slot(); + } } - -/* -#[test] -fn it_produces() { - let (_db, mut chain) = in_memory_test_chain(ChainSpec::foundation()); - let (_block, _state) = chain.produce_block().unwrap(); -} - -#[test] -fn it_processes_a_block_it_produces() { - let (_db, mut chain) = in_memory_test_chain(ChainSpec::foundation()); - let (block, _state) = chain.produce_block().unwrap(); - let (outcome, new_block_hash) = chain.process_block(block).unwrap(); - assert_eq!(outcome, BlockProcessingOutcome::Processed); - assert_eq!(chain.canonical_leaf_block, new_block_hash); -} -*/ diff --git a/eth2/block_producer/src/lib.rs b/eth2/block_producer/src/lib.rs index 8ce6da859..d1b1f4e7c 100644 --- a/eth2/block_producer/src/lib.rs +++ b/eth2/block_producer/src/lib.rs @@ -134,14 +134,23 @@ impl BlockProducer Result { let randao_reveal = { + /* + * TODO: + * https://github.com/ethereum/eth2.0-specs/pull/496 + * let producer_nonce = self.beacon_node.proposer_nonce(&self.pubkey)?; // TODO: add domain, etc to this message. let message = ssz_encode(&producer_nonce); + */ + // TODO: add domain, etc to this message. + let message = ssz_encode(&slot); + println!("validator randao: {:?}", &message); match self.signer.bls_sign(&message) { None => return Ok(PollOutcome::SignerRejection(slot)), Some(signature) => signature, } }; + println!("validator pubkey: {:?}", &self.pubkey); if let Some(block) = self .beacon_node @@ -169,21 +178,7 @@ impl BlockProducer Option { self.store_produce(&block); - let proposal_root = { - let block_without_signature_root = { - let mut block_without_signature = block.clone(); - block_without_signature.signature = self.spec.empty_signature.clone(); - block_without_signature.canonical_root() - }; - let proposal = ProposalSignedData { - slot: block.slot, - shard: self.spec.beacon_chain_shard_number, - block_root: block_without_signature_root, - }; - hash_tree_root(&proposal) - }; - - match self.signer.bls_sign(&proposal_root[..]) { + match self.signer.bls_sign(&block.proposal_root(&self.spec)[..]) { None => None, Some(signature) => { block.signature = signature; @@ -214,11 +209,6 @@ impl BlockProducer(_input: &T) -> Hash256 { - // TODO: stubbed out. - Hash256::zero() -} - impl From for Error { fn from(e: BeaconNodeError) -> Error { Error::BeaconNodeError(e) diff --git a/eth2/types/src/beacon_block/signing.rs b/eth2/types/src/beacon_block/signing.rs new file mode 100644 index 000000000..c3470217f --- /dev/null +++ b/eth2/types/src/beacon_block/signing.rs @@ -0,0 +1,19 @@ +use crate::{BeaconBlock, ChainSpec, Hash256, ProposalSignedData}; +use hashing::hash_tree_root; + +impl BeaconBlock { + pub fn proposal_root(&self, spec: &ChainSpec) -> Hash256 { + let block_without_signature_root = { + let mut block_without_signature = self.clone(); + block_without_signature.signature = spec.empty_signature.clone(); + block_without_signature.canonical_root() + }; + + let proposal = ProposalSignedData { + slot: self.slot, + shard: spec.beacon_chain_shard_number, + block_root: block_without_signature_root, + }; + Hash256::from_slice(&hash_tree_root(&proposal)[..]) + } +} diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state/mod.rs similarity index 98% rename from eth2/types/src/beacon_state.rs rename to eth2/types/src/beacon_state/mod.rs index 921d49360..57fff2c53 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state/mod.rs @@ -10,6 +10,10 @@ use hashing::canonical_hash; use rand::RngCore; use ssz::{ssz_encode, Decodable, DecodeError, Encodable, SszStream}; +mod slot_advance; + +pub use self::slot_advance::Error as SlotProcessingError; + // Custody will not be added to the specs until Phase 1 (Sharding Phase) so dummy class used. type CustodyChallenge = usize; @@ -203,9 +207,9 @@ impl TestRandom for BeaconState { #[cfg(test)] mod tests { - use super::ssz::ssz_encode; use super::*; use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng}; + use ssz::ssz_encode; #[test] pub fn test_ssz_round_trip() { diff --git a/eth2/types/src/beacon_state/slot_advance.rs b/eth2/types/src/beacon_state/slot_advance.rs new file mode 100644 index 000000000..e3be9ccee --- /dev/null +++ b/eth2/types/src/beacon_state/slot_advance.rs @@ -0,0 +1,45 @@ +use crate::{BeaconState, ChainSpec, Hash256}; + +pub enum Error { + UnableToDetermineProducer, +} + +impl BeaconState { + pub fn per_slot_processing( + &mut self, + previous_block_root: Hash256, + spec: &ChainSpec, + ) -> Result<(), Error> { + self.slot += 1; + + let block_proposer = self + .get_beacon_proposer_index(self.slot, spec) + .ok_or_else(|| Error::UnableToDetermineProducer)?; + + self.validator_registry[block_proposer].proposer_slots += 1; + self.latest_randao_mixes[(self.slot % spec.latest_randao_mixes_length) as usize] = + self.latest_randao_mixes[((self.slot - 1) % spec.latest_randao_mixes_length) as usize]; + + // Block roots. + self.latest_block_roots[((self.slot - 1) % spec.latest_block_roots_length) as usize] = + previous_block_root; + + if self.slot % spec.latest_block_roots_length == 0 { + let root = merkle_root(&self.latest_block_roots[..]); + self.batched_block_roots.push(root); + } + Ok(()) + } + + pub fn get_beacon_proposer_index(&self, slot: u64, spec: &ChainSpec) -> Option { + // TODO: this is a stub; implement it properly. + // + // https://github.com/sigp/lighthouse/pull/148/files + let validator_count = self.validator_registry.len(); + Some((slot as usize) % validator_count) + } +} + +fn merkle_root(_input: &[Hash256]) -> Hash256 { + Hash256::zero() +} diff --git a/eth2/utils/hashing/Cargo.toml b/eth2/utils/hashing/Cargo.toml index 1527bceba..6abd9cb0c 100644 --- a/eth2/utils/hashing/Cargo.toml +++ b/eth2/utils/hashing/Cargo.toml @@ -6,3 +6,4 @@ edition = "2018" [dependencies] tiny-keccak = "1.4.2" +ssz = { path = "../ssz" } diff --git a/eth2/utils/hashing/src/lib.rs b/eth2/utils/hashing/src/lib.rs index 02203dc16..3d388fd92 100644 --- a/eth2/utils/hashing/src/lib.rs +++ b/eth2/utils/hashing/src/lib.rs @@ -1,5 +1,4 @@ -extern crate tiny_keccak; - +use ssz::{ssz_encode, Encodable as SszEncodable}; use tiny_keccak::Keccak; pub fn canonical_hash(input: &[u8]) -> Vec { @@ -10,6 +9,10 @@ pub fn canonical_hash(input: &[u8]) -> Vec { result } +pub fn hash_tree_root(input: &T) -> Vec { + canonical_hash(&ssz_encode(input)) +} + #[cfg(test)] mod tests { use super::*;