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" serde_json = "1.0"
slot_clock = { path = "../../eth2/utils/slot_clock" } slot_clock = { path = "../../eth2/utils/slot_clock" }
ssz = { path = "../../eth2/utils/ssz" } ssz = { path = "../../eth2/utils/ssz" }
ssz_derive = { path = "../../eth2/utils/ssz_derive" }
state_processing = { path = "../../eth2/state_processing" } state_processing = { path = "../../eth2/state_processing" }
tree_hash = { path = "../../eth2/utils/tree_hash" } tree_hash = { path = "../../eth2/utils/tree_hash" }
types = { path = "../../eth2/types" } types = { path = "../../eth2/types" }

View File

@ -1,5 +1,6 @@
use crate::checkpoint::CheckPoint; use crate::checkpoint::CheckPoint;
use crate::errors::{BeaconChainError as Error, BlockProductionError}; use crate::errors::{BeaconChainError as Error, BlockProductionError};
use crate::persisted_beacon_chain::{PersistedBeaconChain, BEACON_CHAIN_DB_KEY};
use fork_choice::{ForkChoice, ForkChoiceError}; use fork_choice::{ForkChoice, ForkChoiceError};
use log::{debug, trace}; use log::{debug, trace};
use operation_pool::DepositInsertStatus; 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`. /// Returns the beacon block body for each beacon block root in `roots`.
/// ///
/// Fails if any root in `roots` does not have a corresponding block. /// Fails if any root in `roots` does not have a corresponding block.

View File

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

View File

@ -1,6 +1,7 @@
mod beacon_chain; mod beacon_chain;
mod checkpoint; mod checkpoint;
mod errors; mod errors;
mod persisted_beacon_chain;
pub use self::beacon_chain::{ pub use self::beacon_chain::{
BeaconChain, BeaconChainTypes, BlockProcessingOutcome, InvalidBlock, ValidBlock, 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::{ use beacon_chain::{
fork_choice::BitwiseLMDGhost, fork_choice::BitwiseLMDGhost,
slot_clock::SystemTimeSlotClock, slot_clock::SystemTimeSlotClock,
@ -15,7 +14,7 @@ use types::{
/// Provides a new, initialized `BeaconChain` /// Provides a new, initialized `BeaconChain`
pub trait InitialiseBeaconChain<T: BeaconChainTypes> { 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`. /// A testnet-suitable BeaconChainType, using `MemoryStore`.
@ -29,16 +28,9 @@ impl BeaconChainTypes for TestnetMemoryBeaconChainTypes {
type EthSpec = FewValidatorsEthSpec; type EthSpec = FewValidatorsEthSpec;
} }
impl<T> InitialiseBeaconChain<T> for TestnetMemoryBeaconChainTypes impl<T: BeaconChainTypes> InitialiseBeaconChain<T> for TestnetMemoryBeaconChainTypes {
where fn initialise_beacon_chain(store: Arc<T::Store>) -> BeaconChain<T> {
T: BeaconChainTypes< maybe_load_from_store_for_testnet::<_, T::Store, T::EthSpec>(store)
Store = MemoryStore,
SlotClock = SystemTimeSlotClock,
ForkChoice = BitwiseLMDGhost<MemoryStore, FewValidatorsEthSpec>,
>,
{
fn initialise_beacon_chain(_config: &ClientConfig) -> BeaconChain<T> {
initialize_chain::<_, _, FewValidatorsEthSpec>(MemoryStore::open())
} }
} }
@ -53,32 +45,25 @@ impl BeaconChainTypes for TestnetDiskBeaconChainTypes {
type EthSpec = FewValidatorsEthSpec; type EthSpec = FewValidatorsEthSpec;
} }
impl<T> InitialiseBeaconChain<T> for TestnetDiskBeaconChainTypes impl<T: BeaconChainTypes> InitialiseBeaconChain<T> for TestnetDiskBeaconChainTypes {
where fn initialise_beacon_chain(store: Arc<T::Store>) -> BeaconChain<T> {
T: BeaconChainTypes< maybe_load_from_store_for_testnet::<_, T::Store, T::EthSpec>(store)
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)
} }
} }
/// Produces a `BeaconChain` given some pre-initialized `Store`. /// Loads a `BeaconChain` from `store`, if it exists. Otherwise, create a new chain from genesis.
fn initialize_chain<T, U: Store, V: EthSpec>(store: U) -> BeaconChain<T> fn maybe_load_from_store_for_testnet<T, U: Store, V: EthSpec>(store: Arc<U>) -> BeaconChain<T>
where where
T: BeaconChainTypes<Store = U>, T: BeaconChainTypes<Store = U>,
T::ForkChoice: ForkChoice<U>, T::ForkChoice: ForkChoice<U>,
{ {
if let Ok(Some(beacon_chain)) = BeaconChain::from_store(store.clone()) {
beacon_chain
} else {
let spec = T::EthSpec::spec(); let spec = T::EthSpec::spec();
let store = Arc::new(store); let state_builder =
TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(8, &spec);
let state_builder = TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(8, &spec);
let (genesis_state, _keypairs) = state_builder.build(); let (genesis_state, _keypairs) = state_builder.build();
let mut genesis_block = BeaconBlock::empty(&spec); let mut genesis_block = BeaconBlock::empty(&spec);
@ -104,4 +89,5 @@ where
fork_choice, fork_choice,
) )
.expect("Terminate if beacon chain generation fails") .expect("Terminate if beacon chain generation fails")
}
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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