diff --git a/beacon_node/beacon_chain/Cargo.toml b/beacon_node/beacon_chain/Cargo.toml index 7e6dc209d..5c930403c 100644 --- a/beacon_node/beacon_chain/Cargo.toml +++ b/beacon_node/beacon_chain/Cargo.toml @@ -13,6 +13,7 @@ failure = "0.1" failure_derive = "0.1" genesis = { path = "../../eth2/genesis" } hashing = { path = "../../eth2/utils/hashing" } +parking_lot = "0.7" log = "0.4" env_logger = "0.6" serde = "1.0" diff --git a/beacon_node/beacon_chain/src/attestation_processing.rs b/beacon_node/beacon_chain/src/attestation_processing.rs index ecbe355df..1b7e8ace4 100644 --- a/beacon_node/beacon_chain/src/attestation_processing.rs +++ b/beacon_node/beacon_chain/src/attestation_processing.rs @@ -19,15 +19,9 @@ where &self, free_attestation: FreeAttestation, ) -> Result { - let present_slot = self - .present_slot() - .ok_or_else(|| Error::PresentSlotUnknown)?; - let state = self.state(present_slot)?; - self.attestation_aggregator .write() - .expect("Aggregator unlock failed.") - .process_free_attestation(&state, &free_attestation, &self.spec) + .process_free_attestation(&self.state.read(), &free_attestation, &self.spec) .map_err(|e| e.into()) } } diff --git a/beacon_node/beacon_chain/src/attestation_production.rs b/beacon_node/beacon_chain/src/attestation_production.rs index 9755f48af..febd2ff9a 100644 --- a/beacon_node/beacon_chain/src/attestation_production.rs +++ b/beacon_node/beacon_chain/src/attestation_production.rs @@ -13,30 +13,27 @@ where T: ClientDB, U: SlotClock, { - pub fn produce_attestation_data( - &self, - slot: u64, - shard: u64, - ) -> Result { - let present_slot = self - .present_slot() - .ok_or_else(|| Error::PresentSlotUnknown)?; - let state = self.state(present_slot).map_err(|_| Error::StateError)?; - + pub fn produce_attestation_data(&self, shard: u64) -> Result { let justified_slot = self.justified_slot(); - - let justified_block_root = *state + let justified_block_root = self + .state + .read() .get_block_root(justified_slot, &self.spec) - .ok_or_else(|| Error::SlotTooOld)?; + .ok_or_else(|| Error::SlotTooOld)? + .clone(); - let head_slot = self.head().beacon_block.slot; - let previous_epoch_start_slot = head_slot - (head_slot % self.spec.epoch_length); - let epoch_boundary_root = *state - .get_block_root(previous_epoch_start_slot, &self.spec) - .ok_or_else(|| Error::SlotTooOld)?; + let epoch_boundary_root = self + .state + .read() + .get_block_root( + self.state.read().current_epoch_start_slot(&self.spec), + &self.spec, + ) + .ok_or_else(|| Error::SlotTooOld)? + .clone(); Ok(AttestationData { - slot, + slot: self.state.read().slot, shard, beacon_block_root: self.head().beacon_block_root.clone(), epoch_boundary_root, diff --git a/beacon_node/beacon_chain/src/attestation_targets.rs b/beacon_node/beacon_chain/src/attestation_targets.rs index d968abcf2..62f0bbdfa 100644 --- a/beacon_node/beacon_chain/src/attestation_targets.rs +++ b/beacon_node/beacon_chain/src/attestation_targets.rs @@ -28,18 +28,12 @@ where U: SlotClock, { pub fn insert_latest_attestation_target(&self, validator_index: u64, block_root: Hash256) { - let mut targets = self - .latest_attestation_targets - .write() - .expect("CRITICAL: CanonicalHead poisioned."); + let mut targets = self.latest_attestation_targets.write(); targets.insert(validator_index, block_root); } pub fn get_latest_attestation_target(&self, validator_index: u64) -> Option { - let targets = self - .latest_attestation_targets - .read() - .expect("CRITICAL: CanonicalHead poisioned."); + let targets = self.latest_attestation_targets.read(); match targets.get(validator_index) { Some(hash) => Some(hash.clone()), diff --git a/beacon_node/beacon_chain/src/block_processing.rs b/beacon_node/beacon_chain/src/block_processing.rs index b4126ed9e..2c637031e 100644 --- a/beacon_node/beacon_chain/src/block_processing.rs +++ b/beacon_node/beacon_chain/src/block_processing.rs @@ -1,9 +1,9 @@ -use super::state_transition::Error as TransitionError; use super::{BeaconChain, ClientDB, DBError, SlotClock}; use log::debug; use slot_clock::{SystemTimeSlotClockError, TestingSlotClockError}; use ssz::{ssz_encode, Encodable}; use types::{ + beacon_state::{BlockProcessingError, SlotProcessingError}, readers::{BeaconBlockReader, BeaconStateReader}, Hash256, }; @@ -16,7 +16,6 @@ pub enum ValidBlock { #[derive(Debug, PartialEq)] pub enum InvalidBlock { FutureSlot, - StateTransitionFailed(TransitionError), StateRootMismatch, } @@ -29,31 +28,16 @@ pub enum Outcome { #[derive(Debug, PartialEq)] pub enum Error { DBError(String), - SlotClockError(SystemTimeSlotClockError), - - NotImplemented, - PresentSlotIsNone, UnableToDecodeBlock, + PresentSlotIsNone, + SlotClockError(SystemTimeSlotClockError), MissingParentState(Hash256), InvalidParentState(Hash256), MissingBeaconBlock(Hash256), InvalidBeaconBlock(Hash256), MissingParentBlock(Hash256), - NoBlockProducer, - StateSlotMismatch, - BadBlockSignature, - BadRandaoSignature, - MaxProposerSlashingsExceeded, - BadProposerSlashing, - MaxAttestationsExceeded, - BadAttestation, - NoBlockRoot, - MaxDepositsExceeded, - MaxExitsExceeded, - BadExit, - BadCustodyReseeds, - BadCustodyChallenges, - BadCustodyResponses, + SlotProcessingError(SlotProcessingError), + PerBlockProcessingError(BlockProcessingError), } impl BeaconChain @@ -99,14 +83,13 @@ where .into_beacon_state() .ok_or(Error::InvalidParentState(parent_state_root))?; - let state = match self.state_transition(parent_state, &block) { - Ok(state) => state, - Err(error) => { - return Ok(Outcome::InvalidBlock(InvalidBlock::StateTransitionFailed( - error, - ))); - } - }; + let mut state = parent_state; + + for _ in state.slot..present_slot { + state.per_slot_processing(parent_block_root.clone(), &self.spec)?; + } + + state.per_block_processing(&block, &self.spec)?; let state_root = state.canonical_root(); @@ -131,6 +114,7 @@ where state.clone(), state_root.clone(), ); + *self.state.write() = state.clone(); } // The block was sucessfully processed. @@ -144,6 +128,18 @@ impl From for Error { } } +impl From for Error { + fn from(e: SlotProcessingError) -> Error { + Error::SlotProcessingError(e) + } +} + +impl From for Error { + fn from(e: BlockProcessingError) -> Error { + Error::PerBlockProcessingError(e) + } +} + impl From for Error { fn from(_: TestingSlotClockError) -> Error { unreachable!(); // Testing clock never throws an error. diff --git a/beacon_node/beacon_chain/src/block_production.rs b/beacon_node/beacon_chain/src/block_production.rs index 2e8665a77..6c1fa10c4 100644 --- a/beacon_node/beacon_chain/src/block_production.rs +++ b/beacon_node/beacon_chain/src/block_production.rs @@ -1,10 +1,9 @@ -use super::state_transition::Error as TransitionError; use super::{BeaconChain, ClientDB, DBError, SlotClock}; use bls::Signature; use log::debug; -use slot_clock::TestingSlotClockError; +use slot_clock::{SystemTimeSlotClockError, TestingSlotClockError}; use types::{ - beacon_state::SlotProcessingError, + beacon_state::{BlockProcessingError, SlotProcessingError}, readers::{BeaconBlockReader, BeaconStateReader}, BeaconBlock, BeaconBlockBody, BeaconState, Eth1Data, Hash256, }; @@ -12,9 +11,10 @@ use types::{ #[derive(Debug, PartialEq)] pub enum Error { DBError(String), - StateTransitionError(TransitionError), PresentSlotIsNone, SlotProcessingError(SlotProcessingError), + PerBlockProcessingError(BlockProcessingError), + SlotClockError(SystemTimeSlotClockError), } impl BeaconChain @@ -29,43 +29,31 @@ where where Error: From<::Error>, { - // TODO: allow producing a block from a previous (or future?) slot. - let present_slot = self - .slot_clock - .present_slot() - .map_err(|e| e.into())? - .ok_or(Error::PresentSlotIsNone)?; + debug!("Starting block production..."); - debug!("Producing block for slot {}...", present_slot); + let mut state = self.state.read().clone(); - let parent_root = self.head().beacon_block_root; - let parent_block_reader = self - .block_store - .get_reader(&parent_root)? - .ok_or_else(|| Error::DBError("Block not found.".to_string()))?; - let parent_state = self - .state_store - .get_reader(&parent_block_reader.state_root())? - .ok_or_else(|| Error::DBError("State not found.".to_string()))? - .into_beacon_state() - .ok_or_else(|| Error::DBError("State invalid.".to_string()))?; + debug!("Finding attesatations for new block..."); - debug!("Finding attesatations for block..."); + let attestations = self + .attestation_aggregator + .read() + .get_attestations_for_state(&state, &self.spec); - let attestations = { - let mut next_state = parent_state.clone(); - next_state.per_slot_processing(Hash256::zero(), &self.spec)?; - self.attestation_aggregator - .read() - .unwrap() - .get_attestations_for_state(&next_state, &self.spec) - }; + debug!( + "Inserting {} attestation(s) into new block.", + attestations.len() + ); - debug!("Found {} attestation(s).", attestations.len()); + let parent_root = state + .get_block_root(state.slot.saturating_sub(1), &self.spec) + // TODO: fix unwrap + .unwrap() + .clone(); let mut block = BeaconBlock { - slot: present_slot, - parent_root: parent_root.clone(), + slot: state.slot, + parent_root, state_root: Hash256::zero(), // Updated after the state is calculated. randao_reveal: randao_reveal, eth1_data: Eth1Data { @@ -86,8 +74,8 @@ where }, }; - let state = - self.state_transition_without_verifying_block_signature(parent_state, &block)?; + state.per_block_processing_without_verifying_block_signature(&block, &self.spec)?; + let state_root = state.canonical_root(); block.state_root = state_root; @@ -104,9 +92,15 @@ impl From for Error { } } -impl From for Error { - fn from(e: TransitionError) -> Error { - Error::StateTransitionError(e) +impl From for Error { + fn from(e: SlotProcessingError) -> Error { + Error::SlotProcessingError(e) + } +} + +impl From for Error { + fn from(e: BlockProcessingError) -> Error { + Error::PerBlockProcessingError(e) } } @@ -116,8 +110,8 @@ impl From for Error { } } -impl From for Error { - fn from(e: SlotProcessingError) -> Error { - Error::SlotProcessingError(e) +impl From for Error { + fn from(e: SystemTimeSlotClockError) -> Error { + Error::SlotClockError(e) } } diff --git a/beacon_node/beacon_chain/src/canonical_head.rs b/beacon_node/beacon_chain/src/canonical_head.rs index a92cff327..14f1adf81 100644 --- a/beacon_node/beacon_chain/src/canonical_head.rs +++ b/beacon_node/beacon_chain/src/canonical_head.rs @@ -1,5 +1,5 @@ use crate::{BeaconChain, CheckPoint, ClientDB, SlotClock}; -use std::sync::RwLockReadGuard; +use parking_lot::RwLockReadGuard; use types::{beacon_state::SlotProcessingError, BeaconBlock, BeaconState, Hash256}; #[derive(Debug, PartialEq)] @@ -20,10 +20,7 @@ where new_beacon_state: BeaconState, new_beacon_state_root: Hash256, ) { - let mut head = self - .canonical_head - .write() - .expect("CRITICAL: CanonicalHead poisioned."); + let mut head = self.canonical_head.write(); head.update( new_beacon_block, new_beacon_block_root, @@ -33,34 +30,18 @@ where } pub fn head(&self) -> RwLockReadGuard { - self.canonical_head - .read() - .expect("CRITICAL: CanonicalHead poisioned.") + self.canonical_head.read() } - 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) - } + pub fn advance_state(&self, slot: u64) -> Result<(), SlotProcessingError> { + let state_slot = self.state.read().slot; + let head_block_root = self.head().beacon_block_root; + for _ in state_slot..slot { + self.state + .write() + .per_slot_processing(head_block_root.clone(), &self.spec)?; } + Ok(()) } } diff --git a/beacon_node/beacon_chain/src/finalized_head.rs b/beacon_node/beacon_chain/src/finalized_head.rs index 28c883b4c..76a20a2c2 100644 --- a/beacon_node/beacon_chain/src/finalized_head.rs +++ b/beacon_node/beacon_chain/src/finalized_head.rs @@ -1,5 +1,5 @@ use crate::{BeaconChain, CheckPoint, ClientDB, SlotClock}; -use std::sync::RwLockReadGuard; +use parking_lot::RwLockReadGuard; use types::{BeaconBlock, BeaconState, Hash256}; impl BeaconChain @@ -14,10 +14,7 @@ where new_beacon_state: BeaconState, new_beacon_state_root: Hash256, ) { - let mut finalized_head = self - .finalized_head - .write() - .expect("CRITICAL: finalized_head poisioned."); + let mut finalized_head = self.finalized_head.write(); finalized_head.update( new_beacon_block, new_beacon_block_root, @@ -27,8 +24,6 @@ where } pub fn finalized_head(&self) -> RwLockReadGuard { - self.finalized_head - .read() - .expect("CRITICAL: finalized_head poisioned.") + self.finalized_head.read() } } diff --git a/beacon_node/beacon_chain/src/info.rs b/beacon_node/beacon_chain/src/info.rs index e23aaef78..34ac64c8e 100644 --- a/beacon_node/beacon_chain/src/info.rs +++ b/beacon_node/beacon_chain/src/info.rs @@ -28,10 +28,7 @@ where } pub fn proposer_slots(&self, validator_index: usize) -> Option { - let slot = self.present_slot()?; - let state = self.state(slot).ok()?; - - if let Some(validator) = state.validator_registry.get(validator_index) { + if let Some(validator) = self.state.read().validator_registry.get(validator_index) { Some(validator.proposer_slots) } else { None @@ -46,35 +43,22 @@ where } pub fn block_proposer(&self, slot: u64) -> Result { - // TODO: fix unwrap - let present_slot = self.present_slot().unwrap(); - // TODO: fix unwrap - let state = self.state(present_slot).unwrap(); - let index = state.get_beacon_proposer_index(slot, &self.spec)?; + let index = self + .state + .read() + .get_beacon_proposer_index(slot, &self.spec)?; Ok(index) } pub fn justified_slot(&self) -> u64 { - // TODO: fix unwrap - let present_slot = self.present_slot().unwrap(); - // TODO: fix unwrap - let state = self.state(present_slot).unwrap(); - state.justified_slot - /* - self.justified_head - .read() - .expect("Justified head poisoned") - .beacon_block - .slot - */ + self.state.read().justified_slot } pub fn validator_attestion_slot_and_shard(&self, validator_index: usize) -> Option<(u64, u64)> { - let present_slot = self.present_slot()?; - let state = self.state(present_slot).ok()?; - - let (slot, shard, _committee) = state + let (slot, shard, _committee) = self + .state + .read() .attestation_slot_and_shard_for_validator(validator_index, &self.spec) .ok()?; Some((slot, shard)) diff --git a/beacon_node/beacon_chain/src/lib.rs b/beacon_node/beacon_chain/src/lib.rs index 5fdadd34f..5c719dddd 100644 --- a/beacon_node/beacon_chain/src/lib.rs +++ b/beacon_node/beacon_chain/src/lib.rs @@ -10,7 +10,7 @@ pub mod dump; mod finalized_head; mod info; mod lmd_ghost; -mod state_transition; +// mod state_transition; use self::attestation_targets::AttestationTargets; use self::block_graph::BlockGraph; @@ -20,9 +20,10 @@ use db::{ ClientDB, DBError, }; use genesis::{genesis_beacon_block, genesis_beacon_state, GenesisError}; +use parking_lot::RwLock; use slot_clock::SlotClock; use ssz::ssz_encode; -use std::sync::{Arc, RwLock}; +use std::sync::Arc; use types::{BeaconBlock, BeaconState, ChainSpec, Hash256}; pub use self::block_processing::Outcome as BlockProcessingOutcome; @@ -79,6 +80,7 @@ pub struct BeaconChain { canonical_head: RwLock, finalized_head: RwLock, justified_head: RwLock, + pub state: RwLock, pub latest_attestation_targets: RwLock, pub spec: ChainSpec, } @@ -137,6 +139,7 @@ where slot_clock, block_graph, attestation_aggregator, + state: RwLock::new(genesis_state.clone()), justified_head, finalized_head, canonical_head, diff --git a/beacon_node/beacon_chain/test_harness/benches/state_transition.rs b/beacon_node/beacon_chain/test_harness/benches/state_transition.rs index d78a17720..15184e10c 100644 --- a/beacon_node/beacon_chain/test_harness/benches/state_transition.rs +++ b/beacon_node/beacon_chain/test_harness/benches/state_transition.rs @@ -1,64 +1,80 @@ use criterion::Criterion; -use criterion::{criterion_group, criterion_main}; +use criterion::{black_box, criterion_group, criterion_main, Benchmark}; +use env_logger::{Builder, Env}; use test_harness::BeaconChainHarness; -use types::ChainSpec; +use types::{ChainSpec, Hash256}; fn mid_epoch_state_transition(c: &mut Criterion) { - let validator_count = 2; + Builder::from_env(Env::default().default_filter_or("debug")).init(); + + let validator_count = 1000; let mut rig = BeaconChainHarness::new(ChainSpec::foundation(), validator_count); - let two_and_half_epochs = (rig.spec.epoch_length * 2) + (rig.spec.epoch_length / 2); + let epoch_depth = (rig.spec.epoch_length * 2) + (rig.spec.epoch_length / 2); - for _ in 0..two_and_half_epochs { + for _ in 0..epoch_depth { rig.advance_chain_with_block(); } - let block = rig.advance_chain_without_block(); - let state = rig.beacon_chain.canonical_head().beacon_state.clone(); + let state = rig.beacon_chain.state.read().clone(); + + assert!((state.slot + 1) % rig.spec.epoch_length != 0); c.bench_function("mid-epoch state transition 10k validators", move |b| { - let block = block.clone(); let state = state.clone(); b.iter(|| { - rig.beacon_chain - .state_transition(state.clone(), &block.clone()) + let mut state = state.clone(); + black_box(state.per_slot_processing(Hash256::zero(), &rig.spec)) }) }); } fn epoch_boundary_state_transition(c: &mut Criterion) { - let validator_count = 10_000; + // Builder::from_env(Env::default().default_filter_or("debug")).init(); + + let validator_count = 10000; let mut rig = BeaconChainHarness::new(ChainSpec::foundation(), validator_count); - let three_epochs = rig.spec.epoch_length * 3; + let epoch_depth = rig.spec.epoch_length * 2; - for _ in 0..(three_epochs - 1) { + for _ in 0..(epoch_depth - 1) { rig.advance_chain_with_block(); } - let state = rig.beacon_chain.canonical_head().beacon_state.clone(); - assert_eq!( - state.slot % rig.spec.epoch_length, - rig.spec.epoch_length - 1, - ); - let block = rig.advance_chain_without_block(); + let state = rig.beacon_chain.state.read().clone(); - c.bench_function("epoch boundary state transition 10k validators", move |b| { - let block = block.clone(); + assert_eq!((state.slot + 1) % rig.spec.epoch_length, 0); + + c.bench( + "routines", + Benchmark::new("routine_1", move |b| { + let state = state.clone(); + b.iter(|| { + let mut state = state.clone(); + black_box(black_box( + state.per_slot_processing(Hash256::zero(), &rig.spec), + )) + }) + }) + .sample_size(5), + ); + + /* + c.bench_function("mid-epoch state transition 10k validators", move |b| { let state = state.clone(); b.iter(|| { - let state = rig - .beacon_chain - .state_transition(state.clone(), &block.clone()) - .unwrap(); - assert_eq!(state.slot % rig.spec.epoch_length, 0); + let mut state = state.clone(); + black_box(black_box( + state.per_slot_processing(Hash256::zero(), &rig.spec), + )) }) }); + */ } criterion_group!( benches, - mid_epoch_state_transition, + // mid_epoch_state_transition, epoch_boundary_state_transition ); criterion_main!(benches); diff --git a/beacon_node/beacon_chain/test_harness/src/harness.rs b/beacon_node/beacon_chain/test_harness/src/harness.rs index 3cdfc8024..7d05c7b89 100644 --- a/beacon_node/beacon_chain/test_harness/src/harness.rs +++ b/beacon_node/beacon_chain/test_harness/src/harness.rs @@ -113,6 +113,7 @@ impl BeaconChainHarness { debug!("Incrementing BeaconChain slot to {}.", slot); self.beacon_chain.slot_clock.set_slot(slot); + self.beacon_chain.advance_state(slot).unwrap(); slot } @@ -125,8 +126,8 @@ impl BeaconChainHarness { let attesting_validators = self .beacon_chain - .state(present_slot) - .unwrap() + .state + .read() .get_crosslink_committees_at_slot(present_slot, &self.spec) .unwrap() .iter() @@ -195,13 +196,27 @@ impl BeaconChainHarness { self.beacon_chain.process_block(block).unwrap(); debug!("...block processed by BeaconChain."); + debug!("Producing free attestations..."); + // Produce new attestations. let free_attestations = self.gather_free_attesations(); + + debug!("Processing free attestations..."); + + free_attestations.par_iter().for_each(|free_attestation| { + self.beacon_chain + .process_free_attestation(free_attestation.clone()) + .unwrap(); + }); + + debug!("Free attestations processed."); + /* for free_attestation in free_attestations { self.beacon_chain .process_free_attestation(free_attestation) .unwrap(); } + */ } pub fn chain_dump(&self) -> Result, DumpError> { diff --git a/beacon_node/beacon_chain/test_harness/src/validator/beacon_node/attester.rs b/beacon_node/beacon_chain/test_harness/src/validator/beacon_node/attester.rs index 9c6ce7456..c3e3191f7 100644 --- a/beacon_node/beacon_chain/test_harness/src/validator/beacon_node/attester.rs +++ b/beacon_node/beacon_chain/test_harness/src/validator/beacon_node/attester.rs @@ -13,10 +13,10 @@ where { fn produce_attestation_data( &self, - slot: u64, + _slot: u64, shard: u64, ) -> Result, NodeError> { - match self.beacon_chain.produce_attestation_data(slot, shard) { + match self.beacon_chain.produce_attestation_data(shard) { Ok(attestation_data) => Ok(Some(attestation_data)), Err(e) => Err(NodeError::RemoteFailure(format!("{:?}", e))), } diff --git a/beacon_node/beacon_chain/test_harness/src/validator/mod.rs b/beacon_node/beacon_chain/test_harness/src/validator/mod.rs index 018ffbc86..adb2cdc45 100644 --- a/beacon_node/beacon_chain/test_harness/src/validator/mod.rs +++ b/beacon_node/beacon_chain/test_harness/src/validator/mod.rs @@ -2,6 +2,7 @@ use attester::{Attester, Error as AttestationPollError}; use beacon_chain::BeaconChain; use block_producer::{BlockProducer, Error as BlockPollError}; use db::MemoryDB; +use log::trace; use signer::TestSigner; use slot_clock::TestingSlotClock; use std::sync::Arc; diff --git a/beacon_node/beacon_chain/test_harness/tests/chain.rs b/beacon_node/beacon_chain/test_harness/tests/chain.rs index f84157533..ece9bd340 100644 --- a/beacon_node/beacon_chain/test_harness/tests/chain.rs +++ b/beacon_node/beacon_chain/test_harness/tests/chain.rs @@ -24,7 +24,7 @@ fn it_can_build_on_genesis_block() { fn it_can_produce_past_first_epoch_boundary() { Builder::from_env(Env::default().default_filter_or("debug")).init(); - let validator_count = 100; + let validator_count = 128 * 1024; debug!("Starting harness build..."); diff --git a/eth2/types/Cargo.toml b/eth2/types/Cargo.toml index a779c7e7e..77cfb6040 100644 --- a/eth2/types/Cargo.toml +++ b/eth2/types/Cargo.toml @@ -12,6 +12,7 @@ hashing = { path = "../utils/hashing" } honey-badger-split = { path = "../utils/honey-badger-split" } integer-sqrt = "0.1" log = "0.4" +rayon = "1.0" rand = "0.5.5" serde = "1.0" serde_derive = "1.0" diff --git a/eth2/types/src/beacon_state/attestation_validation.rs b/eth2/types/src/beacon_state/attestation_validation.rs index c4a1f0d8a..6e6ade34f 100644 --- a/eth2/types/src/beacon_state/attestation_validation.rs +++ b/eth2/types/src/beacon_state/attestation_validation.rs @@ -61,7 +61,7 @@ impl BeaconState { attestation.data.slot + spec.epoch_length >= self.slot, Error::IncludedTooLate ); - if self.justified_slot >= self.slot - (self.slot % spec.epoch_length) { + if attestation.data.slot >= self.current_epoch_start_slot(spec) { ensure!( attestation.data.justified_slot == self.justified_slot, Error::WrongJustifiedSlot diff --git a/beacon_node/beacon_chain/src/state_transition.rs b/eth2/types/src/beacon_state/block_processing.rs similarity index 66% rename from beacon_node/beacon_chain/src/state_transition.rs rename to eth2/types/src/beacon_state/block_processing.rs index 405da055c..df232d6d1 100644 --- a/beacon_node/beacon_chain/src/state_transition.rs +++ b/eth2/types/src/beacon_state/block_processing.rs @@ -1,19 +1,12 @@ -use super::{BeaconChain, ClientDB, DBError, SlotClock}; +use crate::{ + beacon_state::{AttestationValidationError, CommitteesError, SlotProcessingError}, + readers::BeaconBlockReader, + BeaconBlock, BeaconState, ChainSpec, Exit, Fork, Hash256, PendingAttestation, +}; use bls::{PublicKey, Signature}; use hashing::hash; use log::debug; -use slot_clock::{SystemTimeSlotClockError, TestingSlotClockError}; use ssz::{ssz_encode, TreeHash}; -use types::{ - beacon_state::{AttestationValidationError, CommitteesError, SlotProcessingError}, - readers::BeaconBlockReader, - BeaconBlock, BeaconState, Exit, Fork, Hash256, PendingAttestation, -}; - -// TODO: define elsehwere. -const DOMAIN_PROPOSAL: u64 = 2; -const DOMAIN_EXIT: u64 = 3; -const DOMAIN_RANDAO: u64 = 4; macro_rules! ensure { ($condition: expr, $result: expr) => { @@ -23,6 +16,11 @@ macro_rules! ensure { }; } +// TODO: define elsehwere. +const DOMAIN_PROPOSAL: u64 = 2; +const DOMAIN_EXIT: u64 = 3; +const DOMAIN_RANDAO: u64 = 4; + #[derive(Debug, PartialEq)] pub enum Error { DBError(String), @@ -50,71 +48,50 @@ pub enum Error { BadCustodyReseeds, BadCustodyChallenges, BadCustodyResponses, - SlotClockError(SystemTimeSlotClockError), CommitteesError(CommitteesError), SlotProcessingError(SlotProcessingError), } -impl BeaconChain -where - T: ClientDB, - U: SlotClock, -{ - pub fn state_transition( - &self, - state: BeaconState, +impl BeaconState { + pub fn per_block_processing( + &mut self, block: &BeaconBlock, - ) -> Result { - self.internal_state_transition(state, block, true) + spec: &ChainSpec, + ) -> Result<(), Error> { + self.per_block_processing_signature_optional(block, true, spec) } - pub fn state_transition_without_verifying_block_signature( - &self, - state: BeaconState, + pub fn per_block_processing_without_verifying_block_signature( + &mut self, block: &BeaconBlock, - ) -> Result { - self.internal_state_transition(state, block, false) + spec: &ChainSpec, + ) -> Result<(), Error> { + self.per_block_processing_signature_optional(block, false, spec) } - fn internal_state_transition( - &self, - mut state: BeaconState, + fn per_block_processing_signature_optional( + &mut self, block: &BeaconBlock, verify_block_signature: bool, - ) -> Result { - ensure!(state.slot < block.slot, Error::StateAlreadyTransitioned); - - debug!( - "Starting state transition from slot {} to {}...", - state.slot, block.slot - ); - - for _ in state.slot..block.slot { - state.per_slot_processing(block.parent_root.clone(), &self.spec)?; - } - - /* - * Slot - */ - - ensure!(block.slot() == state.slot, Error::StateSlotMismatch); + spec: &ChainSpec, + ) -> Result<(), Error> { + ensure!(block.slot() == self.slot, Error::StateSlotMismatch); /* * Proposer Signature */ - - let block_proposer_index = state - .get_beacon_proposer_index(block.slot, &self.spec) + let block_proposer_index = self + .get_beacon_proposer_index(block.slot, spec) .map_err(|_| Error::NoBlockProducer)?; - let block_proposer = &state.validator_registry[block_proposer_index]; + let block_proposer = &self.validator_registry[block_proposer_index]; if verify_block_signature { ensure!( bls_verify( &block_proposer.pubkey, - &block.proposal_root(&self.spec)[..], + &block.proposal_root(spec)[..], &block.signature, - get_domain(&state.fork_data, state.slot, DOMAIN_PROPOSAL) + get_domain(&self.fork_data, self.slot, DOMAIN_PROPOSAL) ), Error::BadBlockSignature ); @@ -123,49 +100,42 @@ where /* * RANDAO */ - ensure!( bls_verify( &block_proposer.pubkey, &ssz_encode(&block_proposer.proposer_slots), &block.randao_reveal, - get_domain(&state.fork_data, state.slot, DOMAIN_RANDAO) + get_domain(&self.fork_data, self.slot, DOMAIN_RANDAO) ), Error::BadRandaoSignature ); // TODO: check this is correct. let new_mix = { - let mut mix = state.latest_randao_mixes - [(state.slot % self.spec.latest_randao_mixes_length) as usize] + let mut mix = self.latest_randao_mixes + [(self.slot % spec.latest_randao_mixes_length) as usize] .to_vec(); mix.append(&mut ssz_encode(&block.randao_reveal)); Hash256::from(&hash(&mix)[..]) }; - state.latest_randao_mixes[(state.slot % self.spec.latest_randao_mixes_length) as usize] = - new_mix; + self.latest_randao_mixes[(self.slot % spec.latest_randao_mixes_length) as usize] = new_mix; /* * Eth1 data */ - // TODO: Eth1 data stuff. - - /* - * OPERATIONS - */ + // TODO: Eth1 data processing. /* * Proposer slashings */ - ensure!( - block.body.proposer_slashings.len() as u64 <= self.spec.max_proposer_slashings, + block.body.proposer_slashings.len() as u64 <= spec.max_proposer_slashings, Error::MaxProposerSlashingsExceeded ); for proposer_slashing in &block.body.proposer_slashings { - let proposer = state + let proposer = self .validator_registry .get(proposer_slashing.proposer_index as usize) .ok_or(Error::BadProposerSlashing)?; @@ -183,7 +153,7 @@ where Error::BadProposerSlashing ); ensure!( - proposer.penalized_slot > state.slot, + proposer.penalized_slot > self.slot, Error::BadProposerSlashing ); ensure!( @@ -192,7 +162,7 @@ where &proposer_slashing.proposal_data_1.hash_tree_root(), &proposer_slashing.proposal_signature_1, get_domain( - &state.fork_data, + &self.fork_data, proposer_slashing.proposal_data_1.slot, DOMAIN_PROPOSAL ) @@ -205,34 +175,34 @@ where &proposer_slashing.proposal_data_2.hash_tree_root(), &proposer_slashing.proposal_signature_2, get_domain( - &state.fork_data, + &self.fork_data, proposer_slashing.proposal_data_2.slot, DOMAIN_PROPOSAL ) ), Error::BadProposerSlashing ); - penalize_validator(&state, proposer_slashing.proposer_index as usize); + penalize_validator(&self, proposer_slashing.proposer_index as usize); } /* * Attestations */ ensure!( - block.body.attestations.len() as u64 <= self.spec.max_attestations, + block.body.attestations.len() as u64 <= spec.max_attestations, Error::MaxAttestationsExceeded ); for attestation in &block.body.attestations { - state.validate_attestation(attestation, &self.spec)?; + self.validate_attestation(attestation, spec)?; let pending_attestation = PendingAttestation { data: attestation.data.clone(), aggregation_bitfield: attestation.aggregation_bitfield.clone(), custody_bitfield: attestation.custody_bitfield.clone(), - slot_included: state.slot, + slot_included: self.slot, }; - state.latest_attestations.push(pending_attestation); + self.latest_attestations.push(pending_attestation); } debug!( @@ -244,7 +214,7 @@ where * Deposits */ ensure!( - block.body.deposits.len() as u64 <= self.spec.max_deposits, + block.body.deposits.len() as u64 <= spec.max_deposits, Error::MaxDepositsExceeded ); @@ -255,25 +225,25 @@ where */ ensure!( - block.body.exits.len() as u64 <= self.spec.max_exits, + block.body.exits.len() as u64 <= spec.max_exits, Error::MaxExitsExceeded ); for exit in &block.body.exits { - let validator = state + let validator = self .validator_registry .get(exit.validator_index as usize) .ok_or(Error::BadExit)?; ensure!( - validator.exit_slot > state.slot + self.spec.entry_exit_delay, + validator.exit_slot > self.slot + spec.entry_exit_delay, Error::BadExit ); - ensure!(state.slot >= exit.slot, Error::BadExit); + ensure!(self.slot >= exit.slot, Error::BadExit); let exit_message = { let exit_struct = Exit { slot: exit.slot, validator_index: exit.validator_index, - signature: self.spec.empty_signature.clone(), + signature: spec.empty_signature.clone(), }; exit_struct.hash_tree_root() }; @@ -282,11 +252,11 @@ where &validator.pubkey, &exit_message, &exit.signature, - get_domain(&state.fork_data, exit.slot, DOMAIN_EXIT) + get_domain(&self.fork_data, exit.slot, DOMAIN_EXIT) ), Error::BadProposerSlashing ); - initiate_validator_exit(&state, exit.validator_index); + initiate_validator_exit(&self, exit.validator_index); } /* @@ -307,7 +277,7 @@ where debug!("State transition complete."); - Ok(state) + Ok(()) } } @@ -329,24 +299,6 @@ fn bls_verify(pubkey: &PublicKey, message: &[u8], signature: &Signature, _domain signature.verify(message, pubkey) } -impl From for Error { - fn from(e: DBError) -> Error { - Error::DBError(e.message) - } -} - -impl From for Error { - fn from(_: TestingSlotClockError) -> Error { - unreachable!(); // Testing clock never throws an error. - } -} - -impl From for Error { - fn from(e: SystemTimeSlotClockError) -> Error { - Error::SlotClockError(e) - } -} - impl From for Error { fn from(e: AttestationValidationError) -> Error { Error::InvalidAttestation(e) diff --git a/eth2/types/src/beacon_state/epoch_processing.rs b/eth2/types/src/beacon_state/epoch_processing.rs index 296c2e848..d93cd4fe5 100644 --- a/eth2/types/src/beacon_state/epoch_processing.rs +++ b/eth2/types/src/beacon_state/epoch_processing.rs @@ -7,6 +7,7 @@ use crate::{ }; use integer_sqrt::IntegerSquareRoot; use log::debug; +use rayon::prelude::*; use std::collections::{HashMap, HashSet}; use std::iter::FromIterator; @@ -59,7 +60,7 @@ impl BeaconState { let current_epoch_attestations: Vec<&PendingAttestation> = self .latest_attestations - .iter() + .par_iter() .filter(|a| a.data.slot / spec.epoch_length == self.current_epoch(spec)) .collect(); @@ -77,7 +78,7 @@ impl BeaconState { let current_epoch_boundary_attestations: Vec<&PendingAttestation> = current_epoch_attestations - .iter() + .par_iter() .filter(|a| { match self.get_block_root(self.current_epoch_start_slot(spec), spec) { Some(block_root) => { @@ -112,7 +113,7 @@ impl BeaconState { */ let previous_epoch_attestations: Vec<&PendingAttestation> = self .latest_attestations - .iter() + .par_iter() .filter(|a| { //TODO: ensure these saturating subs are correct. a.data.slot / spec.epoch_length == self.previous_epoch(spec) diff --git a/eth2/types/src/beacon_state/mod.rs b/eth2/types/src/beacon_state/mod.rs index 3972eaa82..f0548914e 100644 --- a/eth2/types/src/beacon_state/mod.rs +++ b/eth2/types/src/beacon_state/mod.rs @@ -12,6 +12,7 @@ use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash}; mod attestation_participants; mod attestation_validation; +mod block_processing; mod committees; mod epoch_processing; mod shuffling; @@ -20,6 +21,7 @@ mod winning_root; pub use self::attestation_participants::Error as AttestationParticipantsError; pub use self::attestation_validation::Error as AttestationValidationError; +pub use self::block_processing::Error as BlockProcessingError; pub use self::committees::Error as CommitteesError; pub use self::epoch_processing::Error as EpochProcessingError; pub use self::slot_processing::Error as SlotProcessingError;