Fix issues with building on genesis block
This commit is contained in:
parent
191761f356
commit
edeace9e75
@ -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<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.
|
||||
///
|
||||
/// 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> {
|
||||
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<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 {
|
||||
|
@ -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(())
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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::*;
|
||||
|
@ -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<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`.
|
||||
///
|
||||
/// 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<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`.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
|
Loading…
Reference in New Issue
Block a user