Implement basic BeaconChain persistence.

This commit is contained in:
Paul Hauner 2019-05-27 16:13:32 +10:00
parent 76602a65fc
commit 9ed8a4d380
No known key found for this signature in database
GPG Key ID: 5E2CFF9B75FA63DF
13 changed files with 137 additions and 74 deletions

View File

@ -21,6 +21,7 @@ serde_derive = "1.0"
serde_json = "1.0"
slot_clock = { path = "../../eth2/utils/slot_clock" }
ssz = { path = "../../eth2/utils/ssz" }
ssz_derive = { path = "../../eth2/utils/ssz_derive" }
state_processing = { path = "../../eth2/state_processing" }
tree_hash = { path = "../../eth2/utils/tree_hash" }
types = { path = "../../eth2/types" }

View File

@ -1,5 +1,6 @@
use crate::checkpoint::CheckPoint;
use crate::errors::{BeaconChainError as Error, BlockProductionError};
use crate::persisted_beacon_chain::{PersistedBeaconChain, BEACON_CHAIN_DB_KEY};
use fork_choice::{ForkChoice, ForkChoiceError};
use log::{debug, trace};
use operation_pool::DepositInsertStatus;
@ -140,6 +141,51 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
})
}
/// Attempt to load an existing instance from the given `store`.
pub fn from_store(store: Arc<T::Store>) -> Result<Option<BeaconChain<T>>, Error> {
let key = Hash256::from_slice(&BEACON_CHAIN_DB_KEY.as_bytes());
let p: PersistedBeaconChain<T> = match store.get(&key) {
Err(e) => return Err(e.into()),
Ok(None) => return Ok(None),
Ok(Some(p)) => p,
};
let spec = T::EthSpec::spec();
let slot_clock = T::SlotClock::new(
spec.genesis_slot,
p.state.genesis_time,
spec.seconds_per_slot,
);
let fork_choice = T::ForkChoice::new(store.clone());
Ok(Some(BeaconChain {
store,
slot_clock,
op_pool: OperationPool::default(),
canonical_head: RwLock::new(p.canonical_head),
finalized_head: RwLock::new(p.finalized_head),
state: RwLock::new(p.state),
spec,
fork_choice: RwLock::new(fork_choice),
}))
}
/// Attempt to save this instance to `self.store`.
pub fn persist(&self) -> Result<(), Error> {
let p: PersistedBeaconChain<T> = PersistedBeaconChain {
canonical_head: self.canonical_head.read().clone(),
finalized_head: self.finalized_head.read().clone(),
state: self.state.read().clone(),
};
let key = Hash256::from_slice(&BEACON_CHAIN_DB_KEY.as_bytes());
self.store.put(&key, &p)?;
Ok(())
}
/// Returns the beacon block body for each beacon block root in `roots`.
///
/// Fails if any root in `roots` does not have a corresponding block.

View File

@ -1,9 +1,10 @@
use serde_derive::Serialize;
use ssz_derive::{Decode, Encode};
use types::{BeaconBlock, BeaconState, EthSpec, Hash256};
/// Represents some block and it's associated state. Generally, this will be used for tracking the
/// head, justified head and finalized head.
#[derive(Clone, Serialize, PartialEq, Debug)]
#[derive(Clone, Serialize, PartialEq, Debug, Encode, Decode)]
pub struct CheckPoint<E: EthSpec> {
pub beacon_block: BeaconBlock,
pub beacon_block_root: Hash256,

View File

@ -1,6 +1,7 @@
mod beacon_chain;
mod checkpoint;
mod errors;
mod persisted_beacon_chain;
pub use self::beacon_chain::{
BeaconChain, BeaconChainTypes, BlockProcessingOutcome, InvalidBlock, ValidBlock,

View File

@ -0,0 +1,30 @@
use crate::{BeaconChainTypes, CheckPoint};
use ssz::{Decode, Encode};
use ssz_derive::{Decode, Encode};
use store::{DBColumn, Error as StoreError, StoreItem};
use types::BeaconState;
/// 32-byte key for accessing the `PersistedBeaconChain`.
pub const BEACON_CHAIN_DB_KEY: &str = "PERSISTEDBEACONCHAINPERSISTEDBEA";
#[derive(Encode, Decode)]
pub struct PersistedBeaconChain<T: BeaconChainTypes> {
pub canonical_head: CheckPoint<T::EthSpec>,
pub finalized_head: CheckPoint<T::EthSpec>,
// TODO: operations pool.
pub state: BeaconState<T::EthSpec>,
}
impl<T: BeaconChainTypes> StoreItem for PersistedBeaconChain<T> {
fn db_column() -> DBColumn {
DBColumn::BeaconChain
}
fn as_store_bytes(&self) -> Vec<u8> {
self.as_ssz_bytes()
}
fn from_store_bytes(bytes: &mut [u8]) -> Result<Self, StoreError> {
Self::from_ssz_bytes(bytes).map_err(Into::into)
}
}

View File

@ -1,4 +1,3 @@
use crate::ClientConfig;
use beacon_chain::{
fork_choice::BitwiseLMDGhost,
slot_clock::SystemTimeSlotClock,
@ -15,7 +14,7 @@ use types::{
/// Provides a new, initialized `BeaconChain`
pub trait InitialiseBeaconChain<T: BeaconChainTypes> {
fn initialise_beacon_chain(config: &ClientConfig) -> BeaconChain<T>;
fn initialise_beacon_chain(store: Arc<T::Store>) -> BeaconChain<T>;
}
/// A testnet-suitable BeaconChainType, using `MemoryStore`.
@ -29,16 +28,9 @@ impl BeaconChainTypes for TestnetMemoryBeaconChainTypes {
type EthSpec = FewValidatorsEthSpec;
}
impl<T> InitialiseBeaconChain<T> for TestnetMemoryBeaconChainTypes
where
T: BeaconChainTypes<
Store = MemoryStore,
SlotClock = SystemTimeSlotClock,
ForkChoice = BitwiseLMDGhost<MemoryStore, FewValidatorsEthSpec>,
>,
{
fn initialise_beacon_chain(_config: &ClientConfig) -> BeaconChain<T> {
initialize_chain::<_, _, FewValidatorsEthSpec>(MemoryStore::open())
impl<T: BeaconChainTypes> InitialiseBeaconChain<T> for TestnetMemoryBeaconChainTypes {
fn initialise_beacon_chain(store: Arc<T::Store>) -> BeaconChain<T> {
maybe_load_from_store_for_testnet::<_, T::Store, T::EthSpec>(store)
}
}
@ -53,55 +45,49 @@ impl BeaconChainTypes for TestnetDiskBeaconChainTypes {
type EthSpec = FewValidatorsEthSpec;
}
impl<T> InitialiseBeaconChain<T> for TestnetDiskBeaconChainTypes
where
T: BeaconChainTypes<
Store = DiskStore,
SlotClock = SystemTimeSlotClock,
ForkChoice = BitwiseLMDGhost<DiskStore, FewValidatorsEthSpec>,
>,
{
fn initialise_beacon_chain(config: &ClientConfig) -> BeaconChain<T> {
let store = DiskStore::open(&config.db_name).expect("Unable to open DB.");
initialize_chain::<_, _, FewValidatorsEthSpec>(store)
impl<T: BeaconChainTypes> InitialiseBeaconChain<T> for TestnetDiskBeaconChainTypes {
fn initialise_beacon_chain(store: Arc<T::Store>) -> BeaconChain<T> {
maybe_load_from_store_for_testnet::<_, T::Store, T::EthSpec>(store)
}
}
/// Produces a `BeaconChain` given some pre-initialized `Store`.
fn initialize_chain<T, U: Store, V: EthSpec>(store: U) -> BeaconChain<T>
/// Loads a `BeaconChain` from `store`, if it exists. Otherwise, create a new chain from genesis.
fn maybe_load_from_store_for_testnet<T, U: Store, V: EthSpec>(store: Arc<U>) -> BeaconChain<T>
where
T: BeaconChainTypes<Store = U>,
T::ForkChoice: ForkChoice<U>,
{
let spec = T::EthSpec::spec();
if let Ok(Some(beacon_chain)) = BeaconChain::from_store(store.clone()) {
beacon_chain
} else {
let spec = T::EthSpec::spec();
let store = Arc::new(store);
let state_builder =
TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(8, &spec);
let (genesis_state, _keypairs) = state_builder.build();
let state_builder = TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(8, &spec);
let (genesis_state, _keypairs) = state_builder.build();
let mut genesis_block = BeaconBlock::empty(&spec);
genesis_block.state_root = Hash256::from_slice(&genesis_state.tree_hash_root());
let mut genesis_block = BeaconBlock::empty(&spec);
genesis_block.state_root = Hash256::from_slice(&genesis_state.tree_hash_root());
// Slot clock
let slot_clock = T::SlotClock::new(
spec.genesis_slot,
genesis_state.genesis_time,
spec.seconds_per_slot,
);
// Choose the fork choice
let fork_choice = T::ForkChoice::new(store.clone());
// Slot clock
let slot_clock = T::SlotClock::new(
spec.genesis_slot,
genesis_state.genesis_time,
spec.seconds_per_slot,
);
// Choose the fork choice
let fork_choice = T::ForkChoice::new(store.clone());
// Genesis chain
//TODO: Handle error correctly
BeaconChain::from_genesis(
store,
slot_clock,
genesis_state,
genesis_block,
spec.clone(),
fork_choice,
)
.expect("Terminate if beacon chain generation fails")
// Genesis chain
//TODO: Handle error correctly
BeaconChain::from_genesis(
store,
slot_clock,
genesis_state,
genesis_block,
spec.clone(),
fork_choice,
)
.expect("Terminate if beacon chain generation fails")
}
}

View File

@ -1,10 +1,6 @@
// generates error types
use network;
use error_chain::{
error_chain, error_chain_processing, impl_error_chain_kind, impl_error_chain_processed,
impl_extract_backtrace,
};
use error_chain::error_chain;
error_chain! {
links {

View File

@ -50,11 +50,14 @@ where
/// Generate an instance of the client. Spawn and link all internal sub-processes.
pub fn new(
config: ClientConfig,
store: T::Store,
log: slog::Logger,
executor: &TaskExecutor,
) -> error::Result<Self> {
// generate a beacon chain
let beacon_chain = Arc::new(T::initialise_beacon_chain(&config));
let store = Arc::new(store);
// Load a `BeaconChain` from the store, or create a new one if it does not exist.
let beacon_chain = Arc::new(T::initialise_beacon_chain(store));
if beacon_chain.read_slot_clock().is_none() {
panic!("Cannot start client before genesis!")

View File

@ -1,8 +1,5 @@
// generates error types
use error_chain::{
error_chain, error_chain_processing, impl_error_chain_kind, impl_error_chain_processed,
impl_extract_backtrace,
};
use error_chain::error_chain;
error_chain! {}

View File

@ -5,9 +5,7 @@ use beacon_chain::{
AttestationValidationError, CheckPoint,
};
use eth2_libp2p::rpc::HelloMessage;
use types::{
Attestation, BeaconBlock, BeaconBlockBody, BeaconBlockHeader, Epoch, EthSpec, Hash256, Slot,
};
use types::{Attestation, BeaconBlock, BeaconBlockBody, BeaconBlockHeader, Epoch, Hash256, Slot};
pub use beacon_chain::{BeaconChainError, BeaconChainTypes, BlockProcessingOutcome, InvalidBlock};

View File

@ -1,10 +1,7 @@
// generates error types
use eth2_libp2p;
use error_chain::{
error_chain, error_chain_processing, impl_error_chain_kind, impl_error_chain_processed,
impl_extract_backtrace,
};
use error_chain::error_chain;
error_chain! {
links {

View File

@ -5,7 +5,7 @@ use beacon_chain::{
AttestationValidationError, BlockProductionError,
};
pub use beacon_chain::{BeaconChainError, BeaconChainTypes, BlockProcessingOutcome};
use types::{Attestation, AttestationData, BeaconBlock, EthSpec};
use types::{Attestation, AttestationData, BeaconBlock};
/// The RPC's API to the beacon chain.
pub trait BeaconChain<T: BeaconChainTypes>: Send + Sync {

View File

@ -6,6 +6,7 @@ use futures::sync::oneshot;
use futures::Future;
use slog::info;
use std::cell::RefCell;
use store::{DiskStore, MemoryStore};
use tokio::runtime::Builder;
use tokio::runtime::Runtime;
use tokio::runtime::TaskExecutor;
@ -32,8 +33,11 @@ pub fn run_beacon_node(config: ClientConfig, log: &slog::Logger) -> error::Resul
"BeaconNode starting";
"type" => "TestnetDiskBeaconChainTypes"
);
let store = DiskStore::open(&config.db_name).expect("Unable to open DB.");
let client: Client<TestnetDiskBeaconChainTypes> =
Client::new(config, log.clone(), &executor)?;
Client::new(config, store, log.clone(), &executor)?;
run(client, executor, runtime, log)
}
@ -43,8 +47,11 @@ pub fn run_beacon_node(config: ClientConfig, log: &slog::Logger) -> error::Resul
"BeaconNode starting";
"type" => "TestnetMemoryBeaconChainTypes"
);
let store = MemoryStore::open();
let client: Client<TestnetMemoryBeaconChainTypes> =
Client::new(config, log.clone(), &executor)?;
Client::new(config, store, log.clone(), &executor)?;
run(client, executor, runtime, log)
}