diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 1082f6cab..bf87adf10 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -82,20 +82,18 @@ where let state_root = genesis_state.canonical_root(); state_store.put(&state_root, &ssz_encode(&genesis_state)[..])?; - let block_root = genesis_block.canonical_root(); + let block_root = genesis_block.into_header().canonical_root(); block_store.put(&block_root, &ssz_encode(&genesis_block)[..])?; let finalized_head = RwLock::new(CheckPoint::new( genesis_block.clone(), block_root, - // TODO: this is a memory waste; remove full clone. genesis_state.clone(), state_root, )); let canonical_head = RwLock::new(CheckPoint::new( genesis_block.clone(), block_root, - // TODO: this is a memory waste; remove full clone. genesis_state.clone(), state_root, )); @@ -190,10 +188,13 @@ where /// processing applied to it. pub fn advance_state(&self, slot: Slot) -> Result<(), SlotProcessingError> { let state_slot = self.state.read().slot; - let head_block_root = self.head().beacon_block_root; + + let latest_block_header = self.head().beacon_block.into_header(); + for _ in state_slot.as_u64()..slot.as_u64() { - per_slot_processing(&mut *self.state.write(), head_block_root, &self.spec)?; + per_slot_processing(&mut *self.state.write(), &latest_block_header, &self.spec)?; } + Ok(()) } @@ -554,66 +555,13 @@ where } } - /// Dumps the entire canonical chain, from the head to genesis to a vector for analysis. - /// - /// This could be a very expensive operation and should only be done in testing/analysis - /// activities. - pub fn chain_dump(&self) -> Result, Error> { - let mut dump = vec![]; - - let mut last_slot = CheckPoint { - beacon_block: self.head().beacon_block.clone(), - beacon_block_root: self.head().beacon_block_root, - beacon_state: self.head().beacon_state.clone(), - beacon_state_root: self.head().beacon_state_root, - }; - - dump.push(last_slot.clone()); - - loop { - let beacon_block_root = last_slot.beacon_block.previous_block_root; - - if beacon_block_root == self.spec.zero_hash { - break; // Genesis has been reached. - } - - let beacon_block = self - .block_store - .get_deserialized(&beacon_block_root)? - .ok_or_else(|| { - Error::DBInconsistent(format!("Missing block {}", beacon_block_root)) - })?; - let beacon_state_root = beacon_block.state_root; - let beacon_state = self - .state_store - .get_deserialized(&beacon_state_root)? - .ok_or_else(|| { - Error::DBInconsistent(format!("Missing state {}", beacon_state_root)) - })?; - - let slot = CheckPoint { - beacon_block, - beacon_block_root, - beacon_state, - beacon_state_root, - }; - - dump.push(slot.clone()); - last_slot = slot; - } - - dump.reverse(); - - Ok(dump) - } - /// Accept some block and attempt to add it to block DAG. /// /// Will accept blocks from prior slots, however it will reject any block from a future slot. pub fn process_block(&self, block: BeaconBlock) -> Result { debug!("Processing block with slot {}...", block.slot); - let block_root = block.canonical_root(); + let block_root = block.into_header().canonical_root(); let present_slot = self.present_slot(); @@ -648,8 +596,10 @@ where // Transition the parent state to the present slot. let mut state = parent_state; + println!("parent process state: {:?}", state.latest_block_header); + let previous_block_header = parent_block.into_header(); for _ in state.slot.as_u64()..present_slot.as_u64() { - if let Err(e) = per_slot_processing(&mut state, parent_block_root, &self.spec) { + if let Err(e) = per_slot_processing(&mut state, &previous_block_header, &self.spec) { return Ok(BlockProcessingOutcome::InvalidBlock( InvalidBlock::SlotProcessingError(e), )); @@ -664,6 +614,8 @@ where )); } + println!("process state: {:?}", state.latest_block_header); + let state_root = state.canonical_root(); if block.state_root != state_root { @@ -726,7 +678,7 @@ where ); let previous_block_root = *state - .get_block_root(state.slot.saturating_sub(1_u64), &self.spec) + .get_block_root(state.slot - 1, &self.spec) .map_err(|_| BlockProductionError::UnableToGetBlockRootFromState)?; let mut block = BeaconBlock { @@ -754,6 +706,8 @@ where per_block_processing_without_verifying_block_signature(&mut state, &block, &self.spec)?; + println!("produce state: {:?}", state.latest_block_header); + let state_root = state.canonical_root(); block.state_root = state_root; @@ -788,6 +742,59 @@ where Ok(()) } + + /// Dumps the entire canonical chain, from the head to genesis to a vector for analysis. + /// + /// This could be a very expensive operation and should only be done in testing/analysis + /// activities. + pub fn chain_dump(&self) -> Result, Error> { + let mut dump = vec![]; + + let mut last_slot = CheckPoint { + beacon_block: self.head().beacon_block.clone(), + beacon_block_root: self.head().beacon_block_root, + beacon_state: self.head().beacon_state.clone(), + beacon_state_root: self.head().beacon_state_root, + }; + + dump.push(last_slot.clone()); + + loop { + let beacon_block_root = last_slot.beacon_block.previous_block_root; + + if beacon_block_root == self.spec.zero_hash { + break; // Genesis has been reached. + } + + let beacon_block = self + .block_store + .get_deserialized(&beacon_block_root)? + .ok_or_else(|| { + Error::DBInconsistent(format!("Missing block {}", beacon_block_root)) + })?; + let beacon_state_root = beacon_block.state_root; + let beacon_state = self + .state_store + .get_deserialized(&beacon_state_root)? + .ok_or_else(|| { + Error::DBInconsistent(format!("Missing state {}", beacon_state_root)) + })?; + + let slot = CheckPoint { + beacon_block, + beacon_block_root, + beacon_state, + beacon_state_root, + }; + + dump.push(slot.clone()); + last_slot = slot; + } + + dump.reverse(); + + Ok(dump) + } } impl From for Error { diff --git a/eth2/state_processing/src/per_slot_processing.rs b/eth2/state_processing/src/per_slot_processing.rs index aafc7166a..a90c5b408 100644 --- a/eth2/state_processing/src/per_slot_processing.rs +++ b/eth2/state_processing/src/per_slot_processing.rs @@ -1,5 +1,6 @@ use crate::*; -use types::{BeaconState, BeaconStateError, ChainSpec, Hash256}; +use ssz::TreeHash; +use types::*; #[derive(Debug, PartialEq)] pub enum Error { @@ -12,9 +13,11 @@ pub enum Error { /// Spec v0.5.0 pub fn per_slot_processing( state: &mut BeaconState, - previous_block_root: Hash256, + latest_block_header: &BeaconBlockHeader, spec: &ChainSpec, ) -> Result<(), Error> { + cache_state(state, latest_block_header, spec)?; + if (state.slot + 1) % spec.slots_per_epoch == 0 { per_epoch_processing(state, spec)?; state.advance_caches(); @@ -22,6 +25,37 @@ pub fn per_slot_processing( state.slot += 1; + let latest_block_root = Hash256::from_slice(&state.latest_block_header.hash_tree_root()[..]); + state.set_block_root(state.slot - 1, latest_block_root, spec)?; + + Ok(()) +} + +fn cache_state( + state: &mut BeaconState, + latest_block_header: &BeaconBlockHeader, + spec: &ChainSpec, +) -> Result<(), Error> { + let previous_slot_state_root = Hash256::from_slice(&state.hash_tree_root()[..]); + + // Note: increment the state slot here to allow use of our `state_root` and `block_root` + // getter/setter functions. + // + // This is a bit hacky, however it gets the job safely without lots of code. + let previous_slot = state.slot; + state.slot += 1; + + // Store the previous slot's post-state transition root. + if state.latest_block_header.state_root == spec.zero_hash { + state.latest_block_header.state_root = previous_slot_state_root + } + + let latest_block_root = Hash256::from_slice(&latest_block_header.hash_tree_root()[..]); + state.set_block_root(previous_slot, latest_block_root, spec)?; + + // Set the state slot back to what it should be. + state.slot -= 1; + Ok(()) } diff --git a/eth2/types/src/beacon_block.rs b/eth2/types/src/beacon_block.rs index 2dcf91d95..b966751ed 100644 --- a/eth2/types/src/beacon_block.rs +++ b/eth2/types/src/beacon_block.rs @@ -63,16 +63,32 @@ impl BeaconBlock { Hash256::from_slice(&self.hash_tree_root()[..]) } + /// Returns a full `BeaconBlockHeader` of this block. + /// + /// Note: This method is used instead of an `Into` impl to avoid a `Clone` of an entire block + /// when you want to have the block _and_ the header. + /// + /// Note: performs a full tree-hash of `self.body`. + /// + /// Spec v0.5.0 + pub fn into_header(&self) -> BeaconBlockHeader { + BeaconBlockHeader { + slot: self.slot, + previous_block_root: self.previous_block_root, + state_root: self.state_root, + block_body_root: Hash256::from_slice(&self.body.hash_tree_root()[..]), + signature: self.signature.clone(), + } + } + /// Returns a "temporary" header, where the `state_root` is `spec.zero_hash`. /// /// Spec v0.5.0 pub fn into_temporary_header(&self, spec: &ChainSpec) -> BeaconBlockHeader { BeaconBlockHeader { - slot: self.slot, - previous_block_root: self.previous_block_root, state_root: spec.zero_hash, - block_body_root: Hash256::from_slice(&self.hash_tree_root()), - signature: self.signature.clone(), + signature: spec.empty_signature.clone(), + ..self.into_header() } } } diff --git a/eth2/types/src/beacon_block_header.rs b/eth2/types/src/beacon_block_header.rs index 029c7e56b..3d8b08cc8 100644 --- a/eth2/types/src/beacon_block_header.rs +++ b/eth2/types/src/beacon_block_header.rs @@ -30,6 +30,15 @@ pub struct BeaconBlockHeader { pub signature: Signature, } +impl BeaconBlockHeader { + /// Returns the `hash_tree_root` of the header. + /// + /// Spec v0.5.0 + pub fn canonical_root(&self) -> Hash256 { + Hash256::from_slice(&self.hash_tree_root()[..]) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index 1a77d3449..1b2424774 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -35,6 +35,7 @@ pub enum Error { InsufficientAttestations, InsufficientCommittees, InsufficientSlashedBalances, + InsufficientStateRoots, NoCommitteeForShard, EpochCacheUninitialized(RelativeEpoch), PubkeyCacheInconsistent, @@ -425,6 +426,22 @@ impl BeaconState { .ok_or_else(|| Error::NoCommitteeForShard)?) } + /// Safely obtains the index for latest block roots, given some `slot`. + /// + /// Spec v0.5.0 + fn get_latest_block_roots_index(&self, slot: Slot, spec: &ChainSpec) -> Result { + if (slot < self.slot) && (self.slot <= slot + spec.slots_per_historical_root as u64) { + let i = slot.as_usize() % spec.slots_per_historical_root; + if i >= self.latest_block_roots.len() { + Err(Error::InsufficientStateRoots) + } else { + Ok(i) + } + } else { + Err(BeaconStateError::SlotOutOfBounds) + } + } + /// Return the block root at a recent `slot`. /// /// Spec v0.5.0 @@ -433,13 +450,21 @@ impl BeaconState { slot: Slot, spec: &ChainSpec, ) -> Result<&Hash256, BeaconStateError> { - if (self.slot <= slot + spec.slots_per_historical_root as u64) && (slot < self.slot) { - self.latest_block_roots - .get(slot.as_usize() % spec.slots_per_historical_root) - .ok_or_else(|| Error::InsufficientBlockRoots) - } else { - Err(Error::EpochOutOfBounds) - } + let i = self.get_latest_block_roots_index(slot, spec)?; + Ok(&self.latest_block_roots[i]) + } + + /// Sets the block root for some given slot. + /// + /// Spec v0.5.0 + pub fn set_block_root( + &mut self, + slot: Slot, + block_root: Hash256, + spec: &ChainSpec, + ) -> Result<(), BeaconStateError> { + let i = self.get_latest_block_roots_index(slot, spec)?; + Ok(self.latest_block_roots[i] = block_root) } /// XOR-assigns the existing `epoch` randao mix with the hash of the `signature`. @@ -506,6 +531,43 @@ impl BeaconState { } } + /// Safely obtains the index for latest state roots, given some `slot`. + /// + /// Spec v0.5.0 + fn get_latest_state_roots_index(&self, slot: Slot, spec: &ChainSpec) -> Result { + if (slot < self.slot) && (self.slot <= slot + spec.slots_per_historical_root as u64) { + let i = slot.as_usize() % spec.slots_per_historical_root; + if i >= self.latest_state_roots.len() { + Err(Error::InsufficientStateRoots) + } else { + Ok(i) + } + } else { + Err(BeaconStateError::SlotOutOfBounds) + } + } + + /// Gets the state root for some slot. + /// + /// Spec v0.5.0 + pub fn get_state_root(&mut self, slot: Slot, spec: &ChainSpec) -> Result<&Hash256, Error> { + let i = self.get_latest_state_roots_index(slot, spec)?; + Ok(&self.latest_state_roots[i]) + } + + /// Sets the latest state root for slot. + /// + /// Spec v0.5.0 + pub fn set_state_root( + &mut self, + slot: Slot, + state_root: Hash256, + spec: &ChainSpec, + ) -> Result<(), Error> { + let i = self.get_latest_state_roots_index(slot, spec)?; + Ok(self.latest_state_roots[i] = state_root) + } + /// Generate a seed for the given `epoch`. /// /// Spec v0.4.0