Fix issues with building on genesis block

This commit is contained in:
Paul Hauner 2019-03-18 16:53:59 +11:00
parent 191761f356
commit edeace9e75
No known key found for this signature in database
GPG Key ID: D362883A9218FCC6
5 changed files with 202 additions and 74 deletions

View File

@ -82,20 +82,18 @@ where
let state_root = genesis_state.canonical_root(); let state_root = genesis_state.canonical_root();
state_store.put(&state_root, &ssz_encode(&genesis_state)[..])?; 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)[..])?; block_store.put(&block_root, &ssz_encode(&genesis_block)[..])?;
let finalized_head = RwLock::new(CheckPoint::new( let finalized_head = RwLock::new(CheckPoint::new(
genesis_block.clone(), genesis_block.clone(),
block_root, block_root,
// TODO: this is a memory waste; remove full clone.
genesis_state.clone(), genesis_state.clone(),
state_root, state_root,
)); ));
let canonical_head = RwLock::new(CheckPoint::new( let canonical_head = RwLock::new(CheckPoint::new(
genesis_block.clone(), genesis_block.clone(),
block_root, block_root,
// TODO: this is a memory waste; remove full clone.
genesis_state.clone(), genesis_state.clone(),
state_root, state_root,
)); ));
@ -190,10 +188,13 @@ where
/// processing applied to it. /// processing applied to it.
pub fn advance_state(&self, slot: Slot) -> Result<(), SlotProcessingError> { pub fn advance_state(&self, slot: Slot) -> Result<(), SlotProcessingError> {
let state_slot = self.state.read().slot; 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() { 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(()) 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<Vec<CheckPoint>, 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. /// 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. /// Will accept blocks from prior slots, however it will reject any block from a future slot.
pub fn process_block(&self, block: BeaconBlock) -> Result<BlockProcessingOutcome, Error> { pub fn process_block(&self, block: BeaconBlock) -> Result<BlockProcessingOutcome, Error> {
debug!("Processing block with slot {}...", block.slot); 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(); let present_slot = self.present_slot();
@ -648,8 +596,10 @@ where
// Transition the parent state to the present slot. // Transition the parent state to the present slot.
let mut state = parent_state; 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() { 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( return Ok(BlockProcessingOutcome::InvalidBlock(
InvalidBlock::SlotProcessingError(e), InvalidBlock::SlotProcessingError(e),
)); ));
@ -664,6 +614,8 @@ where
)); ));
} }
println!("process state: {:?}", state.latest_block_header);
let state_root = state.canonical_root(); let state_root = state.canonical_root();
if block.state_root != state_root { if block.state_root != state_root {
@ -726,7 +678,7 @@ where
); );
let previous_block_root = *state 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)?; .map_err(|_| BlockProductionError::UnableToGetBlockRootFromState)?;
let mut block = BeaconBlock { let mut block = BeaconBlock {
@ -754,6 +706,8 @@ where
per_block_processing_without_verifying_block_signature(&mut state, &block, &self.spec)?; 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(); let state_root = state.canonical_root();
block.state_root = state_root; block.state_root = state_root;
@ -788,6 +742,59 @@ where
Ok(()) 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<Vec<CheckPoint>, 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<DBError> for Error { impl From<DBError> for Error {

View File

@ -1,5 +1,6 @@
use crate::*; use crate::*;
use types::{BeaconState, BeaconStateError, ChainSpec, Hash256}; use ssz::TreeHash;
use types::*;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Error { pub enum Error {
@ -12,9 +13,11 @@ pub enum Error {
/// Spec v0.5.0 /// Spec v0.5.0
pub fn per_slot_processing( pub fn per_slot_processing(
state: &mut BeaconState, state: &mut BeaconState,
previous_block_root: Hash256, latest_block_header: &BeaconBlockHeader,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<(), Error> { ) -> Result<(), Error> {
cache_state(state, latest_block_header, spec)?;
if (state.slot + 1) % spec.slots_per_epoch == 0 { if (state.slot + 1) % spec.slots_per_epoch == 0 {
per_epoch_processing(state, spec)?; per_epoch_processing(state, spec)?;
state.advance_caches(); state.advance_caches();
@ -22,6 +25,37 @@ pub fn per_slot_processing(
state.slot += 1; 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(()) Ok(())
} }

View File

@ -63,16 +63,32 @@ impl BeaconBlock {
Hash256::from_slice(&self.hash_tree_root()[..]) 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`. /// Returns a "temporary" header, where the `state_root` is `spec.zero_hash`.
/// ///
/// Spec v0.5.0 /// Spec v0.5.0
pub fn into_temporary_header(&self, spec: &ChainSpec) -> BeaconBlockHeader { pub fn into_temporary_header(&self, spec: &ChainSpec) -> BeaconBlockHeader {
BeaconBlockHeader { BeaconBlockHeader {
slot: self.slot,
previous_block_root: self.previous_block_root,
state_root: spec.zero_hash, state_root: spec.zero_hash,
block_body_root: Hash256::from_slice(&self.hash_tree_root()), signature: spec.empty_signature.clone(),
signature: self.signature.clone(), ..self.into_header()
} }
} }
} }

View File

@ -30,6 +30,15 @@ pub struct BeaconBlockHeader {
pub signature: Signature, 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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -35,6 +35,7 @@ pub enum Error {
InsufficientAttestations, InsufficientAttestations,
InsufficientCommittees, InsufficientCommittees,
InsufficientSlashedBalances, InsufficientSlashedBalances,
InsufficientStateRoots,
NoCommitteeForShard, NoCommitteeForShard,
EpochCacheUninitialized(RelativeEpoch), EpochCacheUninitialized(RelativeEpoch),
PubkeyCacheInconsistent, PubkeyCacheInconsistent,
@ -425,6 +426,22 @@ impl BeaconState {
.ok_or_else(|| Error::NoCommitteeForShard)?) .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<usize, Error> {
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`. /// Return the block root at a recent `slot`.
/// ///
/// Spec v0.5.0 /// Spec v0.5.0
@ -433,13 +450,21 @@ impl BeaconState {
slot: Slot, slot: Slot,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<&Hash256, BeaconStateError> { ) -> Result<&Hash256, BeaconStateError> {
if (self.slot <= slot + spec.slots_per_historical_root as u64) && (slot < self.slot) { let i = self.get_latest_block_roots_index(slot, spec)?;
self.latest_block_roots Ok(&self.latest_block_roots[i])
.get(slot.as_usize() % spec.slots_per_historical_root) }
.ok_or_else(|| Error::InsufficientBlockRoots)
} else { /// Sets the block root for some given slot.
Err(Error::EpochOutOfBounds) ///
} /// 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`. /// 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<usize, Error> {
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`. /// Generate a seed for the given `epoch`.
/// ///
/// Spec v0.4.0 /// Spec v0.4.0