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"
|
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" }
|
||||||
|
@ -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.
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
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::{
|
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,55 +45,49 @@ 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>,
|
||||||
{
|
{
|
||||||
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 mut genesis_block = BeaconBlock::empty(&spec);
|
||||||
let (genesis_state, _keypairs) = state_builder.build();
|
genesis_block.state_root = Hash256::from_slice(&genesis_state.tree_hash_root());
|
||||||
|
|
||||||
let mut genesis_block = BeaconBlock::empty(&spec);
|
// Slot clock
|
||||||
genesis_block.state_root = Hash256::from_slice(&genesis_state.tree_hash_root());
|
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
|
// Genesis chain
|
||||||
let slot_clock = T::SlotClock::new(
|
//TODO: Handle error correctly
|
||||||
spec.genesis_slot,
|
BeaconChain::from_genesis(
|
||||||
genesis_state.genesis_time,
|
store,
|
||||||
spec.seconds_per_slot,
|
slot_clock,
|
||||||
);
|
genesis_state,
|
||||||
// Choose the fork choice
|
genesis_block,
|
||||||
let fork_choice = T::ForkChoice::new(store.clone());
|
spec.clone(),
|
||||||
|
fork_choice,
|
||||||
// Genesis chain
|
)
|
||||||
//TODO: Handle error correctly
|
.expect("Terminate if beacon chain generation fails")
|
||||||
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 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 {
|
||||||
|
@ -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!")
|
||||||
|
@ -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! {}
|
||||||
|
@ -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};
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user