Implement basic BeaconChain
persistence.
This commit is contained in:
parent
76602a65fc
commit
9ed8a4d380
@ -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" }
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
30
beacon_node/beacon_chain/src/persisted_beacon_chain.rs
Normal file
30
beacon_node/beacon_chain/src/persisted_beacon_chain.rs
Normal 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)
|
||||
}
|
||||
}
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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!")
|
||||
|
@ -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! {}
|
||||
|
@ -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};
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user