Forwards block root iterators (#672)

* Implement forwards block root iterators

* Clean up errors and docs
This commit is contained in:
Michael Sproul
2019-12-06 18:52:11 +11:00
committed by Paul Hauner
parent 779873680b
commit bd1b61a5b1
23 changed files with 573 additions and 187 deletions

View File

@@ -1,6 +1,7 @@
use crate::chunked_vector::{
store_updated_vector, BlockRoots, HistoricalRoots, RandaoMixes, StateRoots,
};
use crate::forwards_iter::HybridForwardsBlockRootsIterator;
use crate::iter::{ParentRootBlockIterator, StateRootsIterator};
use crate::{
leveldb_store::LevelDB, DBColumn, Error, PartialBeaconState, SimpleStoreItem, Store, StoreItem,
@@ -14,6 +15,7 @@ use state_processing::{
SlotProcessingError,
};
use std::convert::TryInto;
use std::marker::PhantomData;
use std::path::Path;
use std::sync::Arc;
use types::*;
@@ -25,7 +27,7 @@ pub const SPLIT_DB_KEY: &str = "FREEZERDBSPLITFREEZERDBSPLITFREE";
///
/// Stores vector fields like the `block_roots` and `state_roots` separately, and only stores
/// intermittent "restore point" states pre-finalization.
pub struct HotColdDB {
pub struct HotColdDB<E: EthSpec> {
/// The slot and state root at the point where the database is split between hot and cold.
///
/// States with slots less than `split.slot` are in the cold DB, while states with slots
@@ -34,15 +36,17 @@ pub struct HotColdDB {
/// Number of slots per restore point state in the freezer database.
slots_per_restore_point: u64,
/// Cold database containing compact historical data.
cold_db: LevelDB,
pub(crate) cold_db: LevelDB<E>,
/// Hot database containing duplicated but quick-to-access recent data.
///
/// The hot database also contains all blocks.
hot_db: LevelDB,
pub(crate) hot_db: LevelDB<E>,
/// Chain spec.
spec: ChainSpec,
/// Logger.
pub(crate) log: Logger,
/// Mere vessel for E.
_phantom: PhantomData<E>,
}
#[derive(Debug, PartialEq)]
@@ -71,7 +75,9 @@ pub enum HotColdDbError {
RestorePointBlockHashError(BeaconStateError),
}
impl Store for HotColdDB {
impl<E: EthSpec> Store<E> for HotColdDB<E> {
type ForwardsBlockRootsIterator = HybridForwardsBlockRootsIterator<E>;
// Defer to the hot database for basic operations (including blocks for now)
fn get_bytes(&self, column: &str, key: &[u8]) -> Result<Option<Vec<u8>>, Error> {
self.hot_db.get_bytes(column, key)
@@ -90,11 +96,7 @@ impl Store for HotColdDB {
}
/// Store a state in the store.
fn put_state<E: EthSpec>(
&self,
state_root: &Hash256,
state: &BeaconState<E>,
) -> Result<(), Error> {
fn put_state(&self, state_root: &Hash256, state: &BeaconState<E>) -> Result<(), Error> {
if state.slot < self.get_split_slot() {
self.store_archive_state(state_root, state)
} else {
@@ -103,7 +105,7 @@ impl Store for HotColdDB {
}
/// Fetch a state from the store.
fn get_state<E: EthSpec>(
fn get_state(
&self,
state_root: &Hash256,
slot: Option<Slot>,
@@ -129,7 +131,7 @@ impl Store for HotColdDB {
}
/// Advance the split point of the store, moving new finalized states to the freezer.
fn freeze_to_state<E: EthSpec>(
fn freeze_to_state(
store: Arc<Self>,
frozen_head_root: Hash256,
frozen_head: &BeaconState<E>,
@@ -157,7 +159,7 @@ impl Store for HotColdDB {
for (state_root, slot) in
state_root_iter.take_while(|&(_, slot)| slot >= current_split_slot)
{
if slot % store.slots_per_restore_point == 0 {
if slot % dbg!(store.slots_per_restore_point) == 0 {
let state: BeaconState<E> = store
.hot_db
.get_state(&state_root, None)?
@@ -195,20 +197,30 @@ impl Store for HotColdDB {
Ok(())
}
fn forwards_block_roots_iterator(
store: Arc<Self>,
start_slot: Slot,
end_state: BeaconState<E>,
end_block_root: Hash256,
spec: &ChainSpec,
) -> Self::ForwardsBlockRootsIterator {
HybridForwardsBlockRootsIterator::new(store, start_slot, end_state, end_block_root, spec)
}
}
impl HotColdDB {
impl<E: EthSpec> HotColdDB<E> {
/// Open a new or existing database, with the given paths to the hot and cold DBs.
///
/// The `slots_per_restore_point` parameter must be a divisor of `SLOTS_PER_HISTORICAL_ROOT`.
pub fn open<E: EthSpec>(
pub fn open(
hot_path: &Path,
cold_path: &Path,
slots_per_restore_point: u64,
spec: ChainSpec,
log: Logger,
) -> Result<Self, Error> {
Self::verify_slots_per_restore_point::<E>(slots_per_restore_point)?;
Self::verify_slots_per_restore_point(slots_per_restore_point)?;
let db = HotColdDB {
split: RwLock::new(Split::default()),
@@ -217,6 +229,7 @@ impl HotColdDB {
hot_db: LevelDB::open(hot_path)?,
spec,
log,
_phantom: PhantomData,
};
// Load the previous split slot from the database (if any). This ensures we can
@@ -230,7 +243,7 @@ impl HotColdDB {
/// Store a pre-finalization state in the freezer database.
///
/// Will return an error if the state does not lie on a restore point boundary.
pub fn store_archive_state<E: EthSpec>(
pub fn store_archive_state(
&self,
state_root: &Hash256,
state: &BeaconState<E>,
@@ -251,6 +264,7 @@ impl HotColdDB {
"slot" => state.slot,
"state_root" => format!("{:?}", state_root)
);
println!("Creating restore point {}", state.slot);
// 1. Convert to PartialBeaconState and store that in the DB.
let partial_state = PartialBeaconState::from_state_forgetful(state);
@@ -273,7 +287,7 @@ impl HotColdDB {
/// Load a pre-finalization state from the freezer database.
///
/// Will reconstruct the state if it lies between restore points.
pub fn load_archive_state<E: EthSpec>(
pub fn load_archive_state(
&self,
state_root: &Hash256,
slot: Slot,
@@ -286,10 +300,7 @@ impl HotColdDB {
}
/// Load a restore point state by its `state_root`.
fn load_restore_point<E: EthSpec>(
&self,
state_root: &Hash256,
) -> Result<BeaconState<E>, Error> {
fn load_restore_point(&self, state_root: &Hash256) -> Result<BeaconState<E>, Error> {
let mut partial_state = PartialBeaconState::db_get(&self.cold_db, state_root)?
.ok_or_else(|| HotColdDbError::MissingRestorePoint(*state_root))?;
@@ -303,7 +314,7 @@ impl HotColdDB {
}
/// Load a restore point state by its `restore_point_index`.
fn load_restore_point_by_index<E: EthSpec>(
fn load_restore_point_by_index(
&self,
restore_point_index: u64,
) -> Result<BeaconState<E>, Error> {
@@ -312,7 +323,7 @@ impl HotColdDB {
}
/// Load a state that lies between restore points.
fn load_intermediate_state<E: EthSpec>(
fn load_intermediate_state(
&self,
state_root: &Hash256,
slot: Slot,
@@ -330,7 +341,7 @@ impl HotColdDB {
let high_restore_point = if high_restore_point_idx * self.slots_per_restore_point
>= split.slot.as_u64()
{
self.get_state::<E>(&split.state_root, Some(split.slot))?
self.get_state(&split.state_root, Some(split.slot))?
.ok_or_else(|| HotColdDbError::MissingSplitState(split.state_root, split.slot))?
} else {
self.load_restore_point_by_index(high_restore_point_idx)?
@@ -365,7 +376,7 @@ impl HotColdDB {
/// Get a suitable block root for backtracking from `high_restore_point` to the state at `slot`.
///
/// Defaults to the block root for `slot`, which *should* be in range.
fn get_high_restore_point_block_root<E: EthSpec>(
fn get_high_restore_point_block_root(
&self,
high_restore_point: &BeaconState<E>,
slot: Slot,
@@ -381,7 +392,7 @@ impl HotColdDB {
///
/// Blocks are returned in slot-ascending order, suitable for replaying on a state with slot
/// equal to `start_slot`, to reach a state with slot equal to `end_slot`.
fn load_blocks_to_replay<E: EthSpec>(
fn load_blocks_to_replay(
&self,
start_slot: Slot,
end_slot: Slot,
@@ -402,7 +413,7 @@ impl HotColdDB {
/// Replay `blocks` on top of `state` until `target_slot` is reached.
///
/// Will skip slots as necessary.
fn replay_blocks<E: EthSpec>(
fn replay_blocks(
&self,
mut state: BeaconState<E>,
blocks: Vec<BeaconBlock<E>>,
@@ -440,6 +451,11 @@ impl HotColdDB {
self.split.read().slot
}
/// Fetch the slot of the most recently stored restore point.
pub fn get_latest_restore_point_slot(&self) -> Slot {
self.get_split_slot() / self.slots_per_restore_point * self.slots_per_restore_point
}
/// Load the split point from disk.
fn load_split(&self) -> Result<Option<Split>, Error> {
let key = Hash256::from_slice(SPLIT_DB_KEY.as_bytes());
@@ -498,9 +514,7 @@ impl HotColdDB {
/// This ensures that we have at least one restore point within range of our state
/// root history when iterating backwards (and allows for more frequent restore points if
/// desired).
fn verify_slots_per_restore_point<E: EthSpec>(
slots_per_restore_point: u64,
) -> Result<(), HotColdDbError> {
fn verify_slots_per_restore_point(slots_per_restore_point: u64) -> Result<(), HotColdDbError> {
let slots_per_historical_root = E::SlotsPerHistoricalRoot::to_u64();
if slots_per_restore_point > 0 && slots_per_historical_root % slots_per_restore_point == 0 {
Ok(())