diff --git a/beacon_chain/chain/Cargo.toml b/beacon_chain/chain/Cargo.toml deleted file mode 100644 index 3302d15d4..000000000 --- a/beacon_chain/chain/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "chain" -version = "0.1.0" -authors = ["Paul Hauner "] -edition = "2018" - -[dependencies] -bls = { path = "../utils/bls" } -db = { path = "../../lighthouse/db" } -genesis = { path = "../genesis" } -naive_fork_choice = { path = "../naive_fork_choice" } -spec = { path = "../spec" } -ssz = { path = "../utils/ssz" } -types = { path = "../types" } -validator_induction = { path = "../validator_induction" } -validator_shuffling = { path = "../validator_shuffling" } diff --git a/beacon_chain/chain/src/block_processing.rs b/beacon_chain/chain/src/block_processing.rs deleted file mode 100644 index b576bdbce..000000000 --- a/beacon_chain/chain/src/block_processing.rs +++ /dev/null @@ -1,29 +0,0 @@ -use super::BeaconChain; -use db::ClientDB; -use types::Hash256; - -pub enum BlockProcessingOutcome { - BlockAlreadyKnown, - NewCanonicalBlock, - NewReorgBlock, - NewForkBlock, -} - -pub enum Error { - NotImplemented, -} - -impl BeaconChain -where - T: ClientDB + Sized, -{ - pub fn process_block( - &mut self, - _ssz: &[u8], - _present_slot: u64, - ) -> Result<(BlockProcessingOutcome, Hash256), Error> { - // TODO: block processing has been removed. - // https://github.com/sigp/lighthouse/issues/98 - Err(Error::NotImplemented) - } -} diff --git a/beacon_chain/chain/src/lib.rs b/beacon_chain/chain/src/lib.rs deleted file mode 100644 index b1499ac1a..000000000 --- a/beacon_chain/chain/src/lib.rs +++ /dev/null @@ -1,110 +0,0 @@ -extern crate db; -extern crate naive_fork_choice; -extern crate genesis; -extern crate spec; -extern crate ssz; -extern crate types; -extern crate validator_induction; -extern crate validator_shuffling; - -mod block_processing; -mod maps; -mod stores; - -use db::ClientDB; -use crate::maps::{generate_attester_and_proposer_maps, AttesterAndProposerMapError}; -use crate::stores::BeaconChainStore; -use genesis::{genesis_beacon_state, GenesisError}; -use spec::ChainSpec; -use std::collections::HashMap; -use std::sync::Arc; -use types::{AttesterMap, BeaconState, Hash256, ProposerMap}; - -#[derive(Debug, PartialEq)] -pub enum BeaconChainError { - InvalidGenesis, - InsufficientValidators, - UnableToGenerateMaps(AttesterAndProposerMapError), - GenesisError(GenesisError), - DBError(String), -} - -pub struct BeaconChain { - /// The last slot which has been finalized, this is common to all forks. - pub last_finalized_slot: u64, - /// A vec of all block heads (tips of chains). - pub head_block_hashes: Vec, - /// The index of the canonical block in `head_block_hashes`. - pub canonical_head_block_hash: usize, - /// An in-memory map of root hash to beacon state. - pub beacon_states: HashMap, - /// A map of crystallized state to a proposer and attester map. - pub attester_proposer_maps: HashMap, Arc)>, - /// A collection of database stores used by the chain. - pub store: BeaconChainStore, - /// The chain configuration. - pub spec: ChainSpec, -} - -impl BeaconChain -where - T: ClientDB + Sized, -{ - pub fn new(store: BeaconChainStore, spec: ChainSpec) -> Result { - if spec.initial_validators.is_empty() { - return Err(BeaconChainError::InsufficientValidators); - } - - /* - * Generate and process the genesis state. - */ - let genesis_state = genesis_beacon_state(&spec)?; - let mut beacon_states = HashMap::new(); - beacon_states.insert(genesis_state.canonical_root(), genesis_state.clone()); - - // TODO: implement genesis block - // https://github.com/sigp/lighthouse/issues/105 - let canonical_latest_block_hash = Hash256::zero(); - - let head_block_hashes = vec![canonical_latest_block_hash]; - let canonical_head_block_hash = 0; - - let mut attester_proposer_maps = HashMap::new(); - - let (attester_map, proposer_map) = generate_attester_and_proposer_maps( - &genesis_state.shard_committees_at_slots, - 0, - )?; - - attester_proposer_maps.insert( - canonical_latest_block_hash, - (Arc::new(attester_map), Arc::new(proposer_map)), - ); - - Ok(Self { - last_finalized_slot: 0, - head_block_hashes, - canonical_head_block_hash, - beacon_states, - attester_proposer_maps, - store, - spec, - }) - } - - pub fn canonical_block_hash(&self) -> Hash256 { - self.head_block_hashes[self.canonical_head_block_hash] - } -} - -impl From for BeaconChainError { - fn from(e: AttesterAndProposerMapError) -> BeaconChainError { - BeaconChainError::UnableToGenerateMaps(e) - } -} - -impl From for BeaconChainError { - fn from(e: GenesisError) -> BeaconChainError { - BeaconChainError::GenesisError(e) - } -} diff --git a/lighthouse/beacon_chain/Cargo.toml b/lighthouse/beacon_chain/Cargo.toml new file mode 100644 index 000000000..5e2794977 --- /dev/null +++ b/lighthouse/beacon_chain/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "chain" +version = "0.1.0" +authors = ["Paul Hauner "] +edition = "2018" + +[dependencies] +bls = { path = "../../beacon_chain/utils/bls" } +db = { path = "../db" } +genesis = { path = "../../beacon_chain/genesis" } +naive_fork_choice = { path = "../../beacon_chain/naive_fork_choice" } +slot_clock = { path = "../../beacon_chain/utils/slot_clock" } +spec = { path = "../../beacon_chain/spec" } +ssz = { path = "../../beacon_chain/utils/ssz" } +types = { path = "../../beacon_chain/types" } +validator_induction = { path = "../../beacon_chain/validator_induction" } +validator_shuffling = { path = "../../beacon_chain/validator_shuffling" } diff --git a/lighthouse/beacon_chain/src/block_processing.rs b/lighthouse/beacon_chain/src/block_processing.rs new file mode 100644 index 000000000..b24a6f1f8 --- /dev/null +++ b/lighthouse/beacon_chain/src/block_processing.rs @@ -0,0 +1,72 @@ +use super::{BeaconChain, ClientDB, DBError, SlotClock}; +use slot_clock::TestingSlotClockError; +use ssz::{ssz_encode, Encodable}; +use types::{readers::BeaconBlockReader, Hash256}; + +#[derive(Debug, PartialEq)] +pub enum Outcome { + FutureSlot, + Processed, + + NewCanonicalBlock, + NewReorgBlock, + NewForkBlock, +} + +#[derive(Debug, PartialEq)] +pub enum Error { + DBError(String), + NotImplemented, + PresentSlotIsNone, +} + +impl BeaconChain +where + T: ClientDB, + U: SlotClock, + Error: From<::Error>, +{ + pub fn process_block(&mut self, block: &V) -> Result<(Outcome, Hash256), Error> + where + V: BeaconBlockReader + Encodable + Sized, + { + let block_root = block.canonical_root(); + + let present_slot = self + .slot_clock + .present_slot()? + .ok_or(Error::PresentSlotIsNone)?; + + // Block from future slots (i.e., greater than the present slot) should not be processed. + if block.slot() > present_slot { + return Ok((Outcome::FutureSlot, block_root)); + } + + // TODO: block processing has been removed. + // https://github.com/sigp/lighthouse/issues/98 + + // Update leaf blocks. + self.block_store.put(&block_root, &ssz_encode(block)[..])?; + if self.leaf_blocks.contains(&block.parent_root()) { + self.leaf_blocks.remove(&block.parent_root()); + } + if self.canonical_leaf_block == block.parent_root() { + self.canonical_leaf_block = block_root; + } + self.leaf_blocks.insert(block_root); + + Ok((Outcome::Processed, block_root)) + } +} + +impl From for Error { + fn from(e: DBError) -> Error { + Error::DBError(e.message) + } +} + +impl From for Error { + fn from(_: TestingSlotClockError) -> Error { + unreachable!(); // Testing clock never throws an error. + } +} diff --git a/lighthouse/beacon_chain/src/block_production.rs b/lighthouse/beacon_chain/src/block_production.rs new file mode 100644 index 000000000..9754b4a54 --- /dev/null +++ b/lighthouse/beacon_chain/src/block_production.rs @@ -0,0 +1,64 @@ +use super::{BeaconChain, ClientDB, DBError, SlotClock}; +use slot_clock::TestingSlotClockError; +use types::{ + readers::{BeaconBlockReader, BeaconStateReader}, + BeaconBlock, BeaconState, Hash256, +}; + +#[derive(Debug, PartialEq)] +pub enum Error { + DBError(String), + PresentSlotIsNone, +} + +impl BeaconChain +where + T: ClientDB, + U: SlotClock, + Error: From<::Error>, +{ + pub fn produce_block(&mut self) -> Result<(BeaconBlock, BeaconState), Error> { + let present_slot = self + .slot_clock + .present_slot()? + .ok_or(Error::PresentSlotIsNone)?; + let parent_root = self.canonical_leaf_block; + let parent_block = self + .block_store + .get_deserialized(&parent_root)? + .ok_or(Error::DBError("Block not found.".to_string()))?; + let parent_state = self + .state_store + .get_deserialized(&parent_block.state_root())? + .ok_or(Error::DBError("State not found.".to_string()))?; + + let mut block = BeaconBlock { + slot: present_slot, + parent_root, + state_root: Hash256::zero(), // Updated after the state is calculated. + ..parent_block.to_beacon_block() + }; + + let state = BeaconState { + slot: present_slot, + ..parent_state.to_beacon_state() + }; + let state_root = state.canonical_root(); + + block.state_root = state_root; + + Ok((block, state)) + } +} + +impl From for Error { + fn from(e: DBError) -> Error { + Error::DBError(e.message) + } +} + +impl From for Error { + fn from(_: TestingSlotClockError) -> Error { + unreachable!(); // Testing clock never throws an error. + } +} diff --git a/lighthouse/beacon_chain/src/lib.rs b/lighthouse/beacon_chain/src/lib.rs new file mode 100644 index 000000000..632aa2c85 --- /dev/null +++ b/lighthouse/beacon_chain/src/lib.rs @@ -0,0 +1,83 @@ +mod block_processing; +mod block_production; +mod maps; +mod stores; + +use db::{ + stores::{BeaconBlockStore, BeaconStateStore}, + ClientDB, DBError, +}; +use genesis::{genesis_beacon_block, genesis_beacon_state, GenesisError}; +use slot_clock::{SlotClock, TestingSlotClockError}; +use spec::ChainSpec; +use ssz::ssz_encode; +use std::collections::HashSet; +use std::sync::Arc; +use types::Hash256; + +pub use crate::block_processing::Outcome as BlockProcessingOutcome; + +#[derive(Debug, PartialEq)] +pub enum BeaconChainError { + InsufficientValidators, + GenesisError(GenesisError), + DBError(String), +} + +pub struct BeaconChain { + pub block_store: Arc>, + pub state_store: Arc>, + pub slot_clock: U, + pub leaf_blocks: HashSet, + pub canonical_leaf_block: Hash256, + pub spec: ChainSpec, +} + +impl BeaconChain +where + T: ClientDB, + U: SlotClock, +{ + pub fn genesis( + state_store: Arc>, + block_store: Arc>, + slot_clock: U, + spec: ChainSpec, + ) -> Result { + if spec.initial_validators.is_empty() { + return Err(BeaconChainError::InsufficientValidators); + } + + let genesis_state = genesis_beacon_state(&spec)?; + let state_root = genesis_state.canonical_root(); + state_store.put(&state_root, &ssz_encode(&genesis_state)[..])?; + + let genesis_block = genesis_beacon_block(state_root, &spec); + let block_root = genesis_block.canonical_root(); + block_store.put(&block_root, &ssz_encode(&genesis_block)[..])?; + + let mut leaf_blocks = HashSet::new(); + leaf_blocks.insert(block_root.clone()); + + Ok(Self { + block_store, + state_store, + slot_clock, + leaf_blocks, + canonical_leaf_block: block_root, + spec, + }) + } +} + +impl From for BeaconChainError { + fn from(e: DBError) -> BeaconChainError { + BeaconChainError::DBError(e.message) + } +} + +impl From for BeaconChainError { + fn from(e: GenesisError) -> BeaconChainError { + BeaconChainError::GenesisError(e) + } +} diff --git a/beacon_chain/chain/src/maps.rs b/lighthouse/beacon_chain/src/maps.rs similarity index 100% rename from beacon_chain/chain/src/maps.rs rename to lighthouse/beacon_chain/src/maps.rs diff --git a/beacon_chain/chain/src/stores.rs b/lighthouse/beacon_chain/src/stores.rs similarity index 100% rename from beacon_chain/chain/src/stores.rs rename to lighthouse/beacon_chain/src/stores.rs diff --git a/beacon_chain/chain/src/transition.rs b/lighthouse/beacon_chain/src/transition.rs similarity index 97% rename from beacon_chain/chain/src/transition.rs rename to lighthouse/beacon_chain/src/transition.rs index df8803b27..df434cc0c 100644 --- a/beacon_chain/chain/src/transition.rs +++ b/lighthouse/beacon_chain/src/transition.rs @@ -3,7 +3,7 @@ use db::ClientDB; use state_transition::{extend_active_state, StateTransitionError}; use types::{ActiveState, BeaconBlock, CrystallizedState, Hash256}; -impl BeaconChain +impl BeaconChain where T: ClientDB + Sized, { diff --git a/lighthouse/beacon_chain/tests/chain_test.rs b/lighthouse/beacon_chain/tests/chain_test.rs new file mode 100644 index 000000000..aec6c08e6 --- /dev/null +++ b/lighthouse/beacon_chain/tests/chain_test.rs @@ -0,0 +1,49 @@ +use chain::{BlockProcessingOutcome, BeaconChain}; +use db::{ + stores::{BeaconBlockStore, BeaconStateStore}, + MemoryDB, +}; +use slot_clock::TestingSlotClock; +use spec::ChainSpec; +use std::sync::Arc; + +fn in_memory_test_stores() -> ( + Arc, + Arc>, + Arc>, +) { + let db = Arc::new(MemoryDB::open()); + let block_store = Arc::new(BeaconBlockStore::new(db.clone())); + let state_store = Arc::new(BeaconStateStore::new(db.clone())); + (db, block_store, state_store) +} + +fn in_memory_test_chain( + spec: ChainSpec, +) -> (Arc, BeaconChain) { + let (db, block_store, state_store) = in_memory_test_stores(); + let slot_clock = TestingSlotClock::new(0); + + let chain = BeaconChain::genesis(state_store, block_store, slot_clock, spec); + (db, chain.unwrap()) +} + +#[test] +fn it_constructs() { + let (_db, _chain) = in_memory_test_chain(ChainSpec::foundation()); +} + +#[test] +fn it_produces() { + let (_db, mut chain) = in_memory_test_chain(ChainSpec::foundation()); + let (_block, _state) = chain.produce_block().unwrap(); +} + +#[test] +fn it_processes_a_block_it_produces() { + let (_db, mut chain) = in_memory_test_chain(ChainSpec::foundation()); + let (block, _state) = chain.produce_block().unwrap(); + let (outcome, new_block_hash) = chain.process_block(&block).unwrap(); + assert_eq!(outcome, BlockProcessingOutcome::Processed); + assert_eq!(chain.canonical_leaf_block, new_block_hash); +}