Merge pull request #129 from sigp/chain-update
Update `BeaconChain` struct
This commit is contained in:
commit
de3ea2a64b
@ -34,7 +34,6 @@ name = "lighthouse"
|
||||
[workspace]
|
||||
members = [
|
||||
"beacon_chain/attestation_validation",
|
||||
"beacon_chain/chain",
|
||||
"beacon_chain/genesis",
|
||||
"beacon_chain/naive_fork_choice",
|
||||
"beacon_chain/spec",
|
||||
@ -43,10 +42,11 @@ members = [
|
||||
"beacon_chain/utils/boolean-bitfield",
|
||||
"beacon_chain/utils/hashing",
|
||||
"beacon_chain/utils/honey-badger-split",
|
||||
"beacon_chain/utils/slot-clock",
|
||||
"beacon_chain/utils/slot_clock",
|
||||
"beacon_chain/utils/ssz",
|
||||
"beacon_chain/utils/vec_shuffle",
|
||||
"beacon_chain/validator_induction",
|
||||
"beacon_chain/validator_shuffling",
|
||||
"lighthouse/beacon_chain",
|
||||
"lighthouse/db",
|
||||
]
|
||||
|
@ -1,16 +0,0 @@
|
||||
[package]
|
||||
name = "chain"
|
||||
version = "0.1.0"
|
||||
authors = ["Paul Hauner <paul@paulhauner.com>"]
|
||||
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" }
|
@ -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<T> BeaconChain<T>
|
||||
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)
|
||||
}
|
||||
}
|
@ -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<T: ClientDB + Sized> {
|
||||
/// 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<Hash256>,
|
||||
/// 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<Hash256, BeaconState>,
|
||||
/// A map of crystallized state to a proposer and attester map.
|
||||
pub attester_proposer_maps: HashMap<Hash256, (Arc<AttesterMap>, Arc<ProposerMap>)>,
|
||||
/// A collection of database stores used by the chain.
|
||||
pub store: BeaconChainStore<T>,
|
||||
/// The chain configuration.
|
||||
pub spec: ChainSpec,
|
||||
}
|
||||
|
||||
impl<T> BeaconChain<T>
|
||||
where
|
||||
T: ClientDB + Sized,
|
||||
{
|
||||
pub fn new(store: BeaconChainStore<T>, spec: ChainSpec) -> Result<Self, BeaconChainError> {
|
||||
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<AttesterAndProposerMapError> for BeaconChainError {
|
||||
fn from(e: AttesterAndProposerMapError) -> BeaconChainError {
|
||||
BeaconChainError::UnableToGenerateMaps(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GenesisError> for BeaconChainError {
|
||||
fn from(e: GenesisError) -> BeaconChainError {
|
||||
BeaconChainError::GenesisError(e)
|
||||
}
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
use types::{AttesterMap, ProposerMap, ShardCommittee};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum AttesterAndProposerMapError {
|
||||
NoShardCommitteeForSlot,
|
||||
NoAvailableProposer,
|
||||
}
|
||||
|
||||
/// Generate a map of `(slot, shard) |--> committee`.
|
||||
///
|
||||
/// The attester map is used to optimise the lookup of a committee.
|
||||
pub fn generate_attester_and_proposer_maps(
|
||||
shard_and_committee_for_slots: &Vec<Vec<ShardCommittee>>,
|
||||
start_slot: u64,
|
||||
) -> Result<(AttesterMap, ProposerMap), AttesterAndProposerMapError> {
|
||||
let mut attester_map = AttesterMap::new();
|
||||
let mut proposer_map = ProposerMap::new();
|
||||
for (i, slot) in shard_and_committee_for_slots.iter().enumerate() {
|
||||
/*
|
||||
* Store the proposer for the block.
|
||||
*/
|
||||
let slot_number = (i as u64).saturating_add(start_slot);
|
||||
let first_committee = &slot
|
||||
.get(0)
|
||||
.ok_or(AttesterAndProposerMapError::NoShardCommitteeForSlot)?
|
||||
.committee;
|
||||
let proposer_index = (slot_number as usize)
|
||||
.checked_rem(first_committee.len())
|
||||
.ok_or(AttesterAndProposerMapError::NoAvailableProposer)?;
|
||||
proposer_map.insert(slot_number, first_committee[proposer_index]);
|
||||
|
||||
/*
|
||||
* Loop through the shards and extend the attester map.
|
||||
*/
|
||||
for shard_and_committee in slot {
|
||||
let committee = shard_and_committee.committee.clone();
|
||||
attester_map.insert((slot_number, shard_and_committee.shard), committee);
|
||||
}
|
||||
}
|
||||
Ok((attester_map, proposer_map))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn sac_generator(
|
||||
shard_count: u64,
|
||||
slot_count: usize,
|
||||
sac_per_slot: usize,
|
||||
committee_size: usize,
|
||||
) -> Vec<Vec<ShardCommittee>> {
|
||||
let mut shard = 0;
|
||||
let mut validator = 0;
|
||||
let mut cycle = vec![];
|
||||
|
||||
for _ in 0..slot_count {
|
||||
let mut slot: Vec<ShardCommittee> = vec![];
|
||||
for _ in 0..sac_per_slot {
|
||||
let mut sac = ShardCommittee {
|
||||
shard: shard % shard_count,
|
||||
committee: vec![],
|
||||
};
|
||||
for _ in 0..committee_size {
|
||||
sac.committee.push(validator);
|
||||
validator += 1;
|
||||
}
|
||||
slot.push(sac);
|
||||
shard += 1;
|
||||
}
|
||||
cycle.push(slot);
|
||||
}
|
||||
cycle
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_attester_proposer_maps_empty_slots() {
|
||||
let sac = sac_generator(4, 4, 0, 1);
|
||||
let result = generate_attester_and_proposer_maps(&sac, 0);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(AttesterAndProposerMapError::NoShardCommitteeForSlot)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_attester_proposer_maps_empty_committees() {
|
||||
let sac = sac_generator(4, 4, 1, 0);
|
||||
let result = generate_attester_and_proposer_maps(&sac, 0);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(AttesterAndProposerMapError::NoAvailableProposer)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_attester_proposer_maps_scenario_a() {
|
||||
let sac = sac_generator(4, 4, 1, 1);
|
||||
let (a, p) = generate_attester_and_proposer_maps(&sac, 0).unwrap();
|
||||
|
||||
assert_eq!(*p.get(&0).unwrap(), 0);
|
||||
assert_eq!(*p.get(&1).unwrap(), 1);
|
||||
assert_eq!(*p.get(&2).unwrap(), 2);
|
||||
assert_eq!(*p.get(&3).unwrap(), 3);
|
||||
|
||||
assert_eq!(*a.get(&(0, 0)).unwrap(), vec![0]);
|
||||
assert_eq!(*a.get(&(1, 1)).unwrap(), vec![1]);
|
||||
assert_eq!(*a.get(&(2, 2)).unwrap(), vec![2]);
|
||||
assert_eq!(*a.get(&(3, 3)).unwrap(), vec![3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_attester_proposer_maps_scenario_b() {
|
||||
let sac = sac_generator(4, 4, 1, 4);
|
||||
let (a, p) = generate_attester_and_proposer_maps(&sac, 0).unwrap();
|
||||
|
||||
assert_eq!(*p.get(&0).unwrap(), 0);
|
||||
assert_eq!(*p.get(&1).unwrap(), 5);
|
||||
assert_eq!(*p.get(&2).unwrap(), 10);
|
||||
assert_eq!(*p.get(&3).unwrap(), 15);
|
||||
|
||||
assert_eq!(*a.get(&(0, 0)).unwrap(), vec![0, 1, 2, 3]);
|
||||
assert_eq!(*a.get(&(1, 1)).unwrap(), vec![4, 5, 6, 7]);
|
||||
assert_eq!(*a.get(&(2, 2)).unwrap(), vec![8, 9, 10, 11]);
|
||||
assert_eq!(*a.get(&(3, 3)).unwrap(), vec![12, 13, 14, 15]);
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
use db::stores::{BeaconBlockStore, PoWChainStore, ValidatorStore};
|
||||
use db::ClientDB;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct BeaconChainStore<T: ClientDB + Sized> {
|
||||
pub block: Arc<BeaconBlockStore<T>>,
|
||||
pub pow_chain: Arc<PoWChainStore<T>>,
|
||||
pub validator: Arc<ValidatorStore<T>>,
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
use bls::{Signature, BLS_AGG_SIG_BYTE_SIZE};
|
||||
use spec::ChainSpec;
|
||||
use ssz::{encode::encode_length, Decodable, LENGTH_BYTES};
|
||||
use types::{BeaconBlock, BeaconBlockBody};
|
||||
use types::{BeaconBlock, BeaconBlockBody, Hash256};
|
||||
|
||||
/// Generate a genesis BeaconBlock.
|
||||
pub fn genesis_beacon_block(spec: &ChainSpec) -> BeaconBlock {
|
||||
pub fn genesis_beacon_block(state_root: Hash256, spec: &ChainSpec) -> BeaconBlock {
|
||||
BeaconBlock {
|
||||
slot: spec.initial_slot_number,
|
||||
parent_root: spec.zero_hash,
|
||||
state_root: spec.zero_hash,
|
||||
state_root,
|
||||
randao_reveal: spec.zero_hash,
|
||||
candidate_pow_receipt_root: spec.zero_hash,
|
||||
signature: genesis_signature(),
|
||||
@ -42,8 +42,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_genesis() {
|
||||
let spec = ChainSpec::foundation();
|
||||
let state_root = Hash256::from("cats".as_bytes());
|
||||
|
||||
// This only checks that the function runs without panic.
|
||||
genesis_beacon_block(&spec);
|
||||
genesis_beacon_block(state_root, &spec);
|
||||
}
|
||||
}
|
||||
|
@ -15,8 +15,8 @@ pub enum ForkChoiceError {
|
||||
}
|
||||
|
||||
pub fn naive_fork_choice<T>(
|
||||
head_block_hashes: &Vec<Hash256>,
|
||||
block_store: Arc<BeaconBlockStore<T>>,
|
||||
head_block_hashes: &[Hash256],
|
||||
block_store: &Arc<BeaconBlockStore<T>>,
|
||||
) -> Result<Option<usize>, ForkChoiceError>
|
||||
where
|
||||
T: ClientDB + Sized,
|
||||
@ -28,7 +28,7 @@ where
|
||||
*/
|
||||
for (index, block_hash) in head_block_hashes.iter().enumerate() {
|
||||
let ssz = block_store
|
||||
.get_serialized_block(&block_hash.to_vec()[..])?
|
||||
.get(&block_hash)?
|
||||
.ok_or(ForkChoiceError::MissingBlock)?;
|
||||
let (block, _) = BeaconBlock::ssz_decode(&ssz, 0)?;
|
||||
head_blocks.push((index, block));
|
||||
|
@ -68,7 +68,7 @@ impl ChainSpec {
|
||||
*/
|
||||
initial_validators: initial_validators_for_testing(),
|
||||
initial_balances: initial_balances_for_testing(),
|
||||
genesis_time: 1544672897,
|
||||
genesis_time: 1_544_672_897,
|
||||
processed_pow_receipt_root: Hash256::from("pow_root".as_bytes()),
|
||||
}
|
||||
}
|
||||
@ -108,7 +108,7 @@ fn initial_validators_for_testing() -> Vec<ValidatorRecord> {
|
||||
exit_count: 0,
|
||||
custody_commitment: Hash256::zero(),
|
||||
latest_custody_reseed_slot: 0,
|
||||
penultimate_custody_reseed_slot: 0
|
||||
penultimate_custody_reseed_slot: 0,
|
||||
};
|
||||
initial_validators.push(validator_record);
|
||||
}
|
||||
|
@ -8,5 +8,6 @@ edition = "2018"
|
||||
bls = { path = "../utils/bls" }
|
||||
boolean-bitfield = { path = "../utils/boolean-bitfield" }
|
||||
ethereum-types = "0.4.0"
|
||||
hashing = { path = "../utils/hashing" }
|
||||
rand = "0.5.5"
|
||||
ssz = { path = "../utils/ssz" }
|
||||
|
@ -64,8 +64,7 @@ impl<T: RngCore> TestRandom<T> for Attestation {
|
||||
mod tests {
|
||||
use super::super::ssz::ssz_encode;
|
||||
use super::*;
|
||||
use crate::test_utils::TestRandom;
|
||||
use rand::{prng::XorShiftRng, SeedableRng};
|
||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
|
||||
#[test]
|
||||
pub fn test_ssz_round_trip() {
|
||||
|
@ -104,8 +104,7 @@ impl<T: RngCore> TestRandom<T> for AttestationData {
|
||||
mod tests {
|
||||
use super::super::ssz::ssz_encode;
|
||||
use super::*;
|
||||
use crate::test_utils::TestRandom;
|
||||
use rand::{prng::XorShiftRng, SeedableRng};
|
||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
|
||||
#[test]
|
||||
pub fn test_ssz_round_trip() {
|
||||
|
@ -1,7 +1,8 @@
|
||||
use super::ssz::{Decodable, DecodeError, Encodable, SszStream};
|
||||
use super::ssz::{ssz_encode, Decodable, DecodeError, Encodable, SszStream};
|
||||
use super::{BeaconBlockBody, Hash256};
|
||||
use crate::test_utils::TestRandom;
|
||||
use bls::Signature;
|
||||
use hashing::canonical_hash;
|
||||
use rand::RngCore;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
@ -15,6 +16,14 @@ pub struct BeaconBlock {
|
||||
pub body: BeaconBlockBody,
|
||||
}
|
||||
|
||||
impl BeaconBlock {
|
||||
pub fn canonical_root(&self) -> Hash256 {
|
||||
// TODO: implement tree hashing.
|
||||
// https://github.com/sigp/lighthouse/issues/70
|
||||
Hash256::from(&canonical_hash(&ssz_encode(self))[..])
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for BeaconBlock {
|
||||
fn ssz_append(&self, s: &mut SszStream) {
|
||||
s.append(&self.slot);
|
||||
@ -70,8 +79,7 @@ impl<T: RngCore> TestRandom<T> for BeaconBlock {
|
||||
mod tests {
|
||||
use super::super::ssz::ssz_encode;
|
||||
use super::*;
|
||||
use crate::test_utils::TestRandom;
|
||||
use rand::{prng::XorShiftRng, SeedableRng};
|
||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
|
||||
#[test]
|
||||
pub fn test_ssz_round_trip() {
|
||||
|
@ -59,8 +59,7 @@ impl<T: RngCore> TestRandom<T> for BeaconBlockBody {
|
||||
mod tests {
|
||||
use super::super::ssz::ssz_encode;
|
||||
use super::*;
|
||||
use crate::test_utils::TestRandom;
|
||||
use rand::{prng::XorShiftRng, SeedableRng};
|
||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
|
||||
#[test]
|
||||
pub fn test_ssz_round_trip() {
|
||||
|
@ -7,8 +7,9 @@ use super::shard_reassignment_record::ShardReassignmentRecord;
|
||||
use super::validator_record::ValidatorRecord;
|
||||
use super::Hash256;
|
||||
use crate::test_utils::TestRandom;
|
||||
use hashing::canonical_hash;
|
||||
use rand::RngCore;
|
||||
use ssz::{Decodable, DecodeError, Encodable, SszStream};
|
||||
use ssz::{ssz_encode, Decodable, DecodeError, Encodable, SszStream};
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Default)]
|
||||
pub struct BeaconState {
|
||||
@ -52,7 +53,7 @@ impl BeaconState {
|
||||
pub fn canonical_root(&self) -> Hash256 {
|
||||
// TODO: implement tree hashing.
|
||||
// https://github.com/sigp/lighthouse/issues/70
|
||||
Hash256::zero()
|
||||
Hash256::from(&canonical_hash(&ssz_encode(self))[..])
|
||||
}
|
||||
}
|
||||
|
||||
@ -170,3 +171,21 @@ impl<T: RngCore> TestRandom<T> for BeaconState {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::ssz::ssz_encode;
|
||||
use super::*;
|
||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
|
||||
#[test]
|
||||
pub fn test_ssz_round_trip() {
|
||||
let mut rng = XorShiftRng::from_seed([42; 16]);
|
||||
let original = BeaconState::random_for_test(&mut rng);
|
||||
|
||||
let bytes = ssz_encode(&original);
|
||||
let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap();
|
||||
|
||||
assert_eq!(original, decoded);
|
||||
}
|
||||
}
|
||||
|
@ -44,8 +44,7 @@ impl<T: RngCore> TestRandom<T> for CandidatePoWReceiptRootRecord {
|
||||
mod tests {
|
||||
use super::super::ssz::ssz_encode;
|
||||
use super::*;
|
||||
use crate::test_utils::TestRandom;
|
||||
use rand::{prng::XorShiftRng, SeedableRng};
|
||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
|
||||
#[test]
|
||||
pub fn test_ssz_round_trip() {
|
||||
|
@ -44,8 +44,7 @@ impl<T: RngCore> TestRandom<T> for CasperSlashing {
|
||||
mod tests {
|
||||
use super::super::ssz::ssz_encode;
|
||||
use super::*;
|
||||
use crate::test_utils::TestRandom;
|
||||
use rand::{prng::XorShiftRng, SeedableRng};
|
||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
|
||||
#[test]
|
||||
pub fn test_ssz_round_trip() {
|
||||
|
@ -54,8 +54,7 @@ impl<T: RngCore> TestRandom<T> for CrosslinkRecord {
|
||||
mod tests {
|
||||
use super::super::ssz::ssz_encode;
|
||||
use super::*;
|
||||
use crate::test_utils::TestRandom;
|
||||
use rand::{prng::XorShiftRng, SeedableRng};
|
||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
|
||||
#[test]
|
||||
pub fn test_ssz_round_trip() {
|
||||
|
@ -49,8 +49,7 @@ impl<T: RngCore> TestRandom<T> for DepositData {
|
||||
mod tests {
|
||||
use super::super::ssz::ssz_encode;
|
||||
use super::*;
|
||||
use crate::test_utils::TestRandom;
|
||||
use rand::{prng::XorShiftRng, SeedableRng};
|
||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
|
||||
#[test]
|
||||
pub fn test_ssz_round_trip() {
|
||||
|
@ -60,8 +60,7 @@ impl<T: RngCore> TestRandom<T> for DepositInput {
|
||||
mod tests {
|
||||
use super::super::ssz::ssz_encode;
|
||||
use super::*;
|
||||
use crate::test_utils::TestRandom;
|
||||
use rand::{prng::XorShiftRng, SeedableRng};
|
||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
|
||||
#[test]
|
||||
pub fn test_ssz_round_trip() {
|
||||
|
@ -49,8 +49,7 @@ impl<T: RngCore> TestRandom<T> for Exit {
|
||||
mod tests {
|
||||
use super::super::ssz::ssz_encode;
|
||||
use super::*;
|
||||
use crate::test_utils::TestRandom;
|
||||
use rand::{prng::XorShiftRng, SeedableRng};
|
||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
|
||||
#[test]
|
||||
pub fn test_ssz_round_trip() {
|
||||
|
@ -48,8 +48,7 @@ impl<T: RngCore> TestRandom<T> for ForkData {
|
||||
mod tests {
|
||||
use super::super::ssz::ssz_encode;
|
||||
use super::*;
|
||||
use crate::test_utils::TestRandom;
|
||||
use rand::{prng::XorShiftRng, SeedableRng};
|
||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
|
||||
#[test]
|
||||
pub fn test_ssz_round_trip() {
|
||||
|
@ -5,8 +5,8 @@ extern crate ssz;
|
||||
|
||||
pub mod test_utils;
|
||||
|
||||
pub mod attestation_data;
|
||||
pub mod attestation;
|
||||
pub mod attestation_data;
|
||||
pub mod beacon_block;
|
||||
pub mod beacon_block_body;
|
||||
pub mod beacon_state;
|
||||
@ -23,15 +23,17 @@ pub mod proposal_signed_data;
|
||||
pub mod proposer_slashing;
|
||||
pub mod shard_committee;
|
||||
pub mod shard_reassignment_record;
|
||||
pub mod special_record;
|
||||
pub mod slashable_vote_data;
|
||||
pub mod special_record;
|
||||
pub mod validator_record;
|
||||
|
||||
pub mod readers;
|
||||
|
||||
use self::ethereum_types::{H160, H256, U256};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub use crate::attestation_data::AttestationData;
|
||||
pub use crate::attestation::Attestation;
|
||||
pub use crate::attestation_data::AttestationData;
|
||||
pub use crate::beacon_block::BeaconBlock;
|
||||
pub use crate::beacon_block_body::BeaconBlockBody;
|
||||
pub use crate::beacon_state::BeaconState;
|
||||
@ -45,8 +47,8 @@ pub use crate::fork_data::ForkData;
|
||||
pub use crate::pending_attestation_record::PendingAttestationRecord;
|
||||
pub use crate::proposal_signed_data::ProposalSignedData;
|
||||
pub use crate::proposer_slashing::ProposerSlashing;
|
||||
pub use crate::slashable_vote_data::SlashableVoteData;
|
||||
pub use crate::shard_committee::ShardCommittee;
|
||||
pub use crate::slashable_vote_data::SlashableVoteData;
|
||||
pub use crate::special_record::{SpecialRecord, SpecialRecordKind};
|
||||
pub use crate::validator_record::{ValidatorRecord, ValidatorStatus};
|
||||
|
||||
|
@ -54,8 +54,7 @@ impl<T: RngCore> TestRandom<T> for PendingAttestationRecord {
|
||||
mod tests {
|
||||
use super::super::ssz::ssz_encode;
|
||||
use super::*;
|
||||
use crate::test_utils::TestRandom;
|
||||
use rand::{prng::XorShiftRng, SeedableRng};
|
||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
|
||||
#[test]
|
||||
pub fn test_ssz_round_trip() {
|
||||
|
@ -49,8 +49,7 @@ impl<T: RngCore> TestRandom<T> for ProposalSignedData {
|
||||
mod tests {
|
||||
use super::super::ssz::ssz_encode;
|
||||
use super::*;
|
||||
use crate::test_utils::TestRandom;
|
||||
use rand::{prng::XorShiftRng, SeedableRng};
|
||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
|
||||
#[test]
|
||||
pub fn test_ssz_round_trip() {
|
||||
|
@ -60,8 +60,7 @@ impl<T: RngCore> TestRandom<T> for ProposerSlashing {
|
||||
mod tests {
|
||||
use super::super::ssz::ssz_encode;
|
||||
use super::*;
|
||||
use crate::test_utils::TestRandom;
|
||||
use rand::{prng::XorShiftRng, SeedableRng};
|
||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
|
||||
#[test]
|
||||
pub fn test_ssz_round_trip() {
|
||||
|
40
beacon_chain/types/src/readers/block_reader.rs
Normal file
40
beacon_chain/types/src/readers/block_reader.rs
Normal file
@ -0,0 +1,40 @@
|
||||
use crate::{BeaconBlock, Hash256};
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// The `BeaconBlockReader` provides interfaces for reading a subset of fields of a `BeaconBlock`.
|
||||
///
|
||||
/// The purpose of this trait is to allow reading from either;
|
||||
/// - a standard `BeaconBlock` struct, or
|
||||
/// - a SSZ serialized byte array.
|
||||
///
|
||||
/// Note: presently, direct SSZ reading has not been implemented so this trait is being used for
|
||||
/// "future proofing".
|
||||
pub trait BeaconBlockReader: Debug + PartialEq {
|
||||
fn slot(&self) -> u64;
|
||||
fn parent_root(&self) -> Hash256;
|
||||
fn state_root(&self) -> Hash256;
|
||||
fn canonical_root(&self) -> Hash256;
|
||||
fn into_beacon_block(self) -> Option<BeaconBlock>;
|
||||
}
|
||||
|
||||
impl BeaconBlockReader for BeaconBlock {
|
||||
fn slot(&self) -> u64 {
|
||||
self.slot
|
||||
}
|
||||
|
||||
fn parent_root(&self) -> Hash256 {
|
||||
self.parent_root
|
||||
}
|
||||
|
||||
fn state_root(&self) -> Hash256 {
|
||||
self.state_root
|
||||
}
|
||||
|
||||
fn canonical_root(&self) -> Hash256 {
|
||||
self.canonical_root()
|
||||
}
|
||||
|
||||
fn into_beacon_block(self) -> Option<BeaconBlock> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
5
beacon_chain/types/src/readers/mod.rs
Normal file
5
beacon_chain/types/src/readers/mod.rs
Normal file
@ -0,0 +1,5 @@
|
||||
mod block_reader;
|
||||
mod state_reader;
|
||||
|
||||
pub use self::block_reader::BeaconBlockReader;
|
||||
pub use self::state_reader::BeaconStateReader;
|
30
beacon_chain/types/src/readers/state_reader.rs
Normal file
30
beacon_chain/types/src/readers/state_reader.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use crate::{BeaconState, Hash256};
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// The `BeaconStateReader` provides interfaces for reading a subset of fields of a `BeaconState`.
|
||||
///
|
||||
/// The purpose of this trait is to allow reading from either;
|
||||
/// - a standard `BeaconState` struct, or
|
||||
/// - a SSZ serialized byte array.
|
||||
///
|
||||
/// Note: presently, direct SSZ reading has not been implemented so this trait is being used for
|
||||
/// "future proofing".
|
||||
pub trait BeaconStateReader: Debug + PartialEq {
|
||||
fn slot(&self) -> u64;
|
||||
fn canonical_root(&self) -> Hash256;
|
||||
fn into_beacon_state(self) -> Option<BeaconState>;
|
||||
}
|
||||
|
||||
impl BeaconStateReader for BeaconState {
|
||||
fn slot(&self) -> u64 {
|
||||
self.slot
|
||||
}
|
||||
|
||||
fn canonical_root(&self) -> Hash256 {
|
||||
self.canonical_root()
|
||||
}
|
||||
|
||||
fn into_beacon_state(self) -> Option<BeaconState> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
@ -37,8 +37,7 @@ impl<T: RngCore> TestRandom<T> for ShardCommittee {
|
||||
mod tests {
|
||||
use super::super::ssz::ssz_encode;
|
||||
use super::*;
|
||||
use crate::test_utils::TestRandom;
|
||||
use rand::{prng::XorShiftRng, SeedableRng};
|
||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
|
||||
#[test]
|
||||
pub fn test_ssz_round_trip() {
|
||||
|
@ -48,8 +48,7 @@ impl<T: RngCore> TestRandom<T> for ShardReassignmentRecord {
|
||||
mod tests {
|
||||
use super::super::ssz::ssz_encode;
|
||||
use super::*;
|
||||
use crate::test_utils::TestRandom;
|
||||
use rand::{prng::XorShiftRng, SeedableRng};
|
||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
|
||||
#[test]
|
||||
pub fn test_ssz_round_trip() {
|
||||
|
@ -55,8 +55,7 @@ impl<T: RngCore> TestRandom<T> for SlashableVoteData {
|
||||
mod tests {
|
||||
use super::super::ssz::ssz_encode;
|
||||
use super::*;
|
||||
use crate::test_utils::TestRandom;
|
||||
use rand::{prng::XorShiftRng, SeedableRng};
|
||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
|
||||
#[test]
|
||||
pub fn test_ssz_round_trip() {
|
||||
|
@ -5,7 +5,7 @@ use rand::RngCore;
|
||||
use ssz::{Decodable, DecodeError, Encodable, SszStream};
|
||||
use std::convert;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub enum ValidatorStatus {
|
||||
PendingActivation,
|
||||
Active,
|
||||
@ -89,7 +89,7 @@ impl<T: RngCore> TestRandom<T> for ValidatorStatus {
|
||||
ValidatorStatus::Withdrawn,
|
||||
ValidatorStatus::Penalized,
|
||||
];
|
||||
options[(rng.next_u32() as usize) % options.len()].clone()
|
||||
options[(rng.next_u32() as usize) % options.len()]
|
||||
}
|
||||
}
|
||||
|
||||
@ -160,8 +160,7 @@ impl<T: RngCore> TestRandom<T> for ValidatorRecord {
|
||||
mod tests {
|
||||
use super::super::ssz::ssz_encode;
|
||||
use super::*;
|
||||
use crate::test_utils::TestRandom;
|
||||
use rand::{prng::XorShiftRng, SeedableRng};
|
||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
|
||||
#[test]
|
||||
pub fn test_ssz_round_trip() {
|
||||
|
@ -6,7 +6,7 @@ use bls_aggregates::AggregateSignature as RawAggregateSignature;
|
||||
///
|
||||
/// This struct is a wrapper upon a base type and provides helper functions (e.g., SSZ
|
||||
/// serialization).
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, PartialEq, Clone, Default)]
|
||||
pub struct AggregateSignature(RawAggregateSignature);
|
||||
|
||||
impl AggregateSignature {
|
||||
|
@ -1,74 +0,0 @@
|
||||
use std::time::{Duration, SystemTime, SystemTimeError};
|
||||
|
||||
pub fn slot_now(
|
||||
genesis_seconds: u64,
|
||||
slot_duration_seconds: u64,
|
||||
) -> Result<Option<u64>, SystemTimeError> {
|
||||
let sys_time = SystemTime::now();
|
||||
let duration_since_epoch = sys_time.duration_since(SystemTime::UNIX_EPOCH)?;
|
||||
let duration_since_genesis =
|
||||
duration_since_epoch.checked_sub(Duration::from_secs(genesis_seconds));
|
||||
match duration_since_genesis {
|
||||
None => Ok(None),
|
||||
Some(d) => Ok(slot_from_duration(slot_duration_seconds, d)),
|
||||
}
|
||||
}
|
||||
|
||||
fn slot_from_duration(slot_duration_seconds: u64, duration: Duration) -> Option<u64> {
|
||||
duration.as_secs().checked_div(slot_duration_seconds)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/*
|
||||
* Note: these tests are using actual system times and could fail if they are executed on a
|
||||
* very slow machine.
|
||||
*/
|
||||
#[test]
|
||||
fn test_slot_now() {
|
||||
let s_time = 100;
|
||||
|
||||
let now = SystemTime::now();
|
||||
let since_epoch = now.duration_since(SystemTime::UNIX_EPOCH).unwrap();
|
||||
|
||||
let genesis = since_epoch.as_secs() - s_time * 89;
|
||||
assert_eq!(slot_now(genesis, s_time).unwrap(), Some(89));
|
||||
|
||||
let genesis = since_epoch.as_secs();
|
||||
assert_eq!(slot_now(genesis, s_time).unwrap(), Some(0));
|
||||
|
||||
let genesis = since_epoch.as_secs() - s_time * 42 - 5;
|
||||
assert_eq!(slot_now(genesis, s_time).unwrap(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_slot_from_duration() {
|
||||
let s_time = 100;
|
||||
|
||||
assert_eq!(slot_from_duration(s_time, Duration::from_secs(0)), Some(0));
|
||||
assert_eq!(slot_from_duration(s_time, Duration::from_secs(10)), Some(0));
|
||||
assert_eq!(
|
||||
slot_from_duration(s_time, Duration::from_secs(100)),
|
||||
Some(1)
|
||||
);
|
||||
assert_eq!(
|
||||
slot_from_duration(s_time, Duration::from_secs(101)),
|
||||
Some(1)
|
||||
);
|
||||
assert_eq!(
|
||||
slot_from_duration(s_time, Duration::from_secs(1000)),
|
||||
Some(10)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_slot_from_duration_slot_time_zero() {
|
||||
let s_time = 0;
|
||||
|
||||
assert_eq!(slot_from_duration(s_time, Duration::from_secs(0)), None);
|
||||
assert_eq!(slot_from_duration(s_time, Duration::from_secs(10)), None);
|
||||
assert_eq!(slot_from_duration(s_time, Duration::from_secs(1000)), None);
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "slot-clock"
|
||||
name = "slot_clock"
|
||||
version = "0.1.0"
|
||||
authors = ["Paul Hauner <paul@paulhauner.com>"]
|
||||
edition = "2018"
|
11
beacon_chain/utils/slot_clock/src/lib.rs
Normal file
11
beacon_chain/utils/slot_clock/src/lib.rs
Normal file
@ -0,0 +1,11 @@
|
||||
mod system_time_slot_clock;
|
||||
mod testing_slot_clock;
|
||||
|
||||
pub use crate::system_time_slot_clock::{Error as SystemTimeSlotClockError, SystemTimeSlotClock};
|
||||
pub use crate::testing_slot_clock::{Error as TestingSlotClockError, TestingSlotClock};
|
||||
|
||||
pub trait SlotClock {
|
||||
type Error;
|
||||
|
||||
fn present_slot(&self) -> Result<Option<u64>, Self::Error>;
|
||||
}
|
135
beacon_chain/utils/slot_clock/src/system_time_slot_clock.rs
Normal file
135
beacon_chain/utils/slot_clock/src/system_time_slot_clock.rs
Normal file
@ -0,0 +1,135 @@
|
||||
use super::SlotClock;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
pub use std::time::SystemTimeError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
SlotDurationIsZero,
|
||||
SystemTimeError(SystemTimeError),
|
||||
}
|
||||
|
||||
/// Determines the present slot based upon the present system time.
|
||||
pub struct SystemTimeSlotClock {
|
||||
genesis_seconds: u64,
|
||||
slot_duration_seconds: u64,
|
||||
}
|
||||
|
||||
impl SystemTimeSlotClock {
|
||||
/// Create a new `SystemTimeSlotClock`.
|
||||
///
|
||||
/// Returns an Error if `slot_duration_seconds == 0`.
|
||||
pub fn new(
|
||||
genesis_seconds: u64,
|
||||
slot_duration_seconds: u64,
|
||||
) -> Result<SystemTimeSlotClock, Error> {
|
||||
if slot_duration_seconds == 0 {
|
||||
Err(Error::SlotDurationIsZero)
|
||||
} else {
|
||||
Ok(Self {
|
||||
genesis_seconds,
|
||||
slot_duration_seconds,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SlotClock for SystemTimeSlotClock {
|
||||
type Error = Error;
|
||||
|
||||
fn present_slot(&self) -> Result<Option<u64>, Error> {
|
||||
let syslot_time = SystemTime::now();
|
||||
let duration_since_epoch = syslot_time.duration_since(SystemTime::UNIX_EPOCH)?;
|
||||
let duration_since_genesis =
|
||||
duration_since_epoch.checked_sub(Duration::from_secs(self.genesis_seconds));
|
||||
match duration_since_genesis {
|
||||
None => Ok(None),
|
||||
Some(d) => Ok(slot_from_duration(self.slot_duration_seconds, d)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SystemTimeError> for Error {
|
||||
fn from(e: SystemTimeError) -> Error {
|
||||
Error::SystemTimeError(e)
|
||||
}
|
||||
}
|
||||
|
||||
fn slot_from_duration(slot_duration_seconds: u64, duration: Duration) -> Option<u64> {
|
||||
duration.as_secs().checked_div(slot_duration_seconds)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/*
|
||||
* Note: these tests are using actual system times and could fail if they are executed on a
|
||||
* very slow machine.
|
||||
*/
|
||||
#[test]
|
||||
fn test_slot_now() {
|
||||
let slot_time = 100;
|
||||
|
||||
let now = SystemTime::now();
|
||||
let since_epoch = now.duration_since(SystemTime::UNIX_EPOCH).unwrap();
|
||||
|
||||
let genesis = since_epoch.as_secs() - slot_time * 89;
|
||||
|
||||
let clock = SystemTimeSlotClock {
|
||||
genesis_seconds: genesis,
|
||||
slot_duration_seconds: slot_time,
|
||||
};
|
||||
assert_eq!(clock.present_slot().unwrap(), Some(89));
|
||||
|
||||
let clock = SystemTimeSlotClock {
|
||||
genesis_seconds: since_epoch.as_secs(),
|
||||
slot_duration_seconds: slot_time,
|
||||
};
|
||||
assert_eq!(clock.present_slot().unwrap(), Some(0));
|
||||
|
||||
let clock = SystemTimeSlotClock {
|
||||
genesis_seconds: since_epoch.as_secs() - slot_time * 42 - 5,
|
||||
slot_duration_seconds: slot_time,
|
||||
};
|
||||
assert_eq!(clock.present_slot().unwrap(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_slot_from_duration() {
|
||||
let slot_time = 100;
|
||||
|
||||
assert_eq!(
|
||||
slot_from_duration(slot_time, Duration::from_secs(0)),
|
||||
Some(0)
|
||||
);
|
||||
assert_eq!(
|
||||
slot_from_duration(slot_time, Duration::from_secs(10)),
|
||||
Some(0)
|
||||
);
|
||||
assert_eq!(
|
||||
slot_from_duration(slot_time, Duration::from_secs(100)),
|
||||
Some(1)
|
||||
);
|
||||
assert_eq!(
|
||||
slot_from_duration(slot_time, Duration::from_secs(101)),
|
||||
Some(1)
|
||||
);
|
||||
assert_eq!(
|
||||
slot_from_duration(slot_time, Duration::from_secs(1000)),
|
||||
Some(10)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_slot_from_duration_slot_time_zero() {
|
||||
let slot_time = 0;
|
||||
|
||||
assert_eq!(slot_from_duration(slot_time, Duration::from_secs(0)), None);
|
||||
assert_eq!(slot_from_duration(slot_time, Duration::from_secs(10)), None);
|
||||
assert_eq!(
|
||||
slot_from_duration(slot_time, Duration::from_secs(1000)),
|
||||
None
|
||||
);
|
||||
}
|
||||
}
|
43
beacon_chain/utils/slot_clock/src/testing_slot_clock.rs
Normal file
43
beacon_chain/utils/slot_clock/src/testing_slot_clock.rs
Normal file
@ -0,0 +1,43 @@
|
||||
use super::SlotClock;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {}
|
||||
|
||||
/// Determines the present slot based upon the present system time.
|
||||
pub struct TestingSlotClock {
|
||||
slot: u64,
|
||||
}
|
||||
|
||||
impl TestingSlotClock {
|
||||
/// Create a new `TestingSlotClock`.
|
||||
///
|
||||
/// Returns an Error if `slot_duration_seconds == 0`.
|
||||
pub fn new(slot: u64) -> TestingSlotClock {
|
||||
TestingSlotClock { slot }
|
||||
}
|
||||
|
||||
pub fn set_slot(&mut self, slot: u64) {
|
||||
self.slot = slot;
|
||||
}
|
||||
}
|
||||
|
||||
impl SlotClock for TestingSlotClock {
|
||||
type Error = Error;
|
||||
|
||||
fn present_slot(&self) -> Result<Option<u64>, Error> {
|
||||
Ok(Some(self.slot))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_slot_now() {
|
||||
let mut clock = TestingSlotClock::new(10);
|
||||
assert_eq!(clock.present_slot(), Ok(Some(10)));
|
||||
clock.set_slot(123);
|
||||
assert_eq!(clock.present_slot(), Ok(Some(123)));
|
||||
}
|
||||
}
|
17
lighthouse/beacon_chain/Cargo.toml
Normal file
17
lighthouse/beacon_chain/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "chain"
|
||||
version = "0.1.0"
|
||||
authors = ["Paul Hauner <paul@paulhauner.com>"]
|
||||
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" }
|
72
lighthouse/beacon_chain/src/block_processing.rs
Normal file
72
lighthouse/beacon_chain/src/block_processing.rs
Normal file
@ -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<T, U> BeaconChain<T, U>
|
||||
where
|
||||
T: ClientDB,
|
||||
U: SlotClock,
|
||||
Error: From<<U as SlotClock>::Error>,
|
||||
{
|
||||
pub fn process_block<V>(&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<DBError> for Error {
|
||||
fn from(e: DBError) -> Error {
|
||||
Error::DBError(e.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TestingSlotClockError> for Error {
|
||||
fn from(_: TestingSlotClockError) -> Error {
|
||||
unreachable!(); // Testing clock never throws an error.
|
||||
}
|
||||
}
|
75
lighthouse/beacon_chain/src/block_production.rs
Normal file
75
lighthouse/beacon_chain/src/block_production.rs
Normal file
@ -0,0 +1,75 @@
|
||||
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<T, U> BeaconChain<T, U>
|
||||
where
|
||||
T: ClientDB,
|
||||
U: SlotClock,
|
||||
Error: From<<U as SlotClock>::Error>,
|
||||
{
|
||||
pub fn produce_block(&mut self) -> Result<(BeaconBlock, BeaconState), Error> {
|
||||
/*
|
||||
* Important: this code is a big stub and only exists to ensure that tests pass.
|
||||
*
|
||||
* https://github.com/sigp/lighthouse/issues/107
|
||||
*/
|
||||
let present_slot = self
|
||||
.slot_clock
|
||||
.present_slot()?
|
||||
.ok_or(Error::PresentSlotIsNone)?;
|
||||
let parent_root = self.canonical_leaf_block;
|
||||
let parent_block_reader = self
|
||||
.block_store
|
||||
.get_reader(&parent_root)?
|
||||
.ok_or_else(|| Error::DBError("Block not found.".to_string()))?;
|
||||
let parent_state_reader = self
|
||||
.state_store
|
||||
.get_reader(&parent_block_reader.state_root())?
|
||||
.ok_or_else(|| Error::DBError("State not found.".to_string()))?;
|
||||
|
||||
let parent_block = parent_block_reader
|
||||
.into_beacon_block()
|
||||
.ok_or_else(|| Error::DBError("Bad parent block SSZ.".to_string()))?;
|
||||
let mut block = BeaconBlock {
|
||||
slot: present_slot,
|
||||
parent_root,
|
||||
state_root: Hash256::zero(), // Updated after the state is calculated.
|
||||
..parent_block
|
||||
};
|
||||
|
||||
let parent_state = parent_state_reader
|
||||
.into_beacon_state()
|
||||
.ok_or_else(|| Error::DBError("Bad parent block SSZ.".to_string()))?;
|
||||
let state = BeaconState {
|
||||
slot: present_slot,
|
||||
..parent_state
|
||||
};
|
||||
let state_root = state.canonical_root();
|
||||
|
||||
block.state_root = state_root;
|
||||
|
||||
Ok((block, state))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DBError> for Error {
|
||||
fn from(e: DBError) -> Error {
|
||||
Error::DBError(e.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TestingSlotClockError> for Error {
|
||||
fn from(_: TestingSlotClockError) -> Error {
|
||||
unreachable!(); // Testing clock never throws an error.
|
||||
}
|
||||
}
|
81
lighthouse/beacon_chain/src/lib.rs
Normal file
81
lighthouse/beacon_chain/src/lib.rs
Normal file
@ -0,0 +1,81 @@
|
||||
mod block_processing;
|
||||
mod block_production;
|
||||
|
||||
use db::{
|
||||
stores::{BeaconBlockStore, BeaconStateStore},
|
||||
ClientDB, DBError,
|
||||
};
|
||||
use genesis::{genesis_beacon_block, genesis_beacon_state, GenesisError};
|
||||
use slot_clock::SlotClock;
|
||||
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<T: ClientDB + Sized, U: SlotClock> {
|
||||
pub block_store: Arc<BeaconBlockStore<T>>,
|
||||
pub state_store: Arc<BeaconStateStore<T>>,
|
||||
pub slot_clock: U,
|
||||
pub leaf_blocks: HashSet<Hash256>,
|
||||
pub canonical_leaf_block: Hash256,
|
||||
pub spec: ChainSpec,
|
||||
}
|
||||
|
||||
impl<T, U> BeaconChain<T, U>
|
||||
where
|
||||
T: ClientDB,
|
||||
U: SlotClock,
|
||||
{
|
||||
pub fn genesis(
|
||||
state_store: Arc<BeaconStateStore<T>>,
|
||||
block_store: Arc<BeaconBlockStore<T>>,
|
||||
slot_clock: U,
|
||||
spec: ChainSpec,
|
||||
) -> Result<Self, BeaconChainError> {
|
||||
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);
|
||||
|
||||
Ok(Self {
|
||||
block_store,
|
||||
state_store,
|
||||
slot_clock,
|
||||
leaf_blocks,
|
||||
canonical_leaf_block: block_root,
|
||||
spec,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DBError> for BeaconChainError {
|
||||
fn from(e: DBError) -> BeaconChainError {
|
||||
BeaconChainError::DBError(e.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GenesisError> for BeaconChainError {
|
||||
fn from(e: GenesisError) -> BeaconChainError {
|
||||
BeaconChainError::GenesisError(e)
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ use db::ClientDB;
|
||||
use state_transition::{extend_active_state, StateTransitionError};
|
||||
use types::{ActiveState, BeaconBlock, CrystallizedState, Hash256};
|
||||
|
||||
impl<T> BeaconChain<T>
|
||||
impl<T, U> BeaconChain<T, U>
|
||||
where
|
||||
T: ClientDB + Sized,
|
||||
{
|
49
lighthouse/beacon_chain/tests/chain_test.rs
Normal file
49
lighthouse/beacon_chain/tests/chain_test.rs
Normal file
@ -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<MemoryDB>,
|
||||
Arc<BeaconBlockStore<MemoryDB>>,
|
||||
Arc<BeaconStateStore<MemoryDB>>,
|
||||
) {
|
||||
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<MemoryDB>, BeaconChain<MemoryDB, TestingSlotClock>) {
|
||||
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);
|
||||
}
|
@ -1,11 +1,8 @@
|
||||
use super::BLOCKS_DB_COLUMN as DB_COLUMN;
|
||||
use super::{ClientDB, DBError};
|
||||
use ssz::{Decodable, DecodeError};
|
||||
use ssz::Decodable;
|
||||
use std::sync::Arc;
|
||||
use types::Hash256;
|
||||
|
||||
type BeaconBlockHash = Vec<u8>;
|
||||
type BeaconBlockSsz = Vec<u8>;
|
||||
use types::{readers::BeaconBlockReader, BeaconBlock, Hash256};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum BeaconBlockAtSlotError {
|
||||
@ -21,25 +18,29 @@ where
|
||||
db: Arc<T>,
|
||||
}
|
||||
|
||||
// Implements `put`, `get`, `exists` and `delete` for the store.
|
||||
impl_crud_for_store!(BeaconBlockStore, DB_COLUMN);
|
||||
|
||||
impl<T: ClientDB> BeaconBlockStore<T> {
|
||||
pub fn new(db: Arc<T>) -> Self {
|
||||
Self { db }
|
||||
}
|
||||
|
||||
pub fn put_serialized_block(&self, hash: &[u8], ssz: &[u8]) -> Result<(), DBError> {
|
||||
self.db.put(DB_COLUMN, hash, ssz)
|
||||
}
|
||||
|
||||
pub fn get_serialized_block(&self, hash: &[u8]) -> Result<Option<Vec<u8>>, DBError> {
|
||||
self.db.get(DB_COLUMN, hash)
|
||||
}
|
||||
|
||||
pub fn block_exists(&self, hash: &[u8]) -> Result<bool, DBError> {
|
||||
self.db.exists(DB_COLUMN, hash)
|
||||
}
|
||||
|
||||
pub fn delete_block(&self, hash: &[u8]) -> Result<(), DBError> {
|
||||
self.db.delete(DB_COLUMN, hash)
|
||||
/// Retuns an object implementing `BeaconBlockReader`, or `None` (if hash not known).
|
||||
///
|
||||
/// Note: Presently, this function fully deserializes a `BeaconBlock` and returns that. In the
|
||||
/// future, it would be ideal to return an object capable of reading directly from serialized
|
||||
/// SSZ bytes.
|
||||
pub fn get_reader(&self, hash: &Hash256) -> Result<Option<impl BeaconBlockReader>, DBError> {
|
||||
match self.get(&hash)? {
|
||||
None => Ok(None),
|
||||
Some(ssz) => {
|
||||
let (block, _) = BeaconBlock::ssz_decode(&ssz, 0).map_err(|_| DBError {
|
||||
message: "Bad BeaconBlock SSZ.".to_string(),
|
||||
})?;
|
||||
Ok(Some(block))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the block at a slot given a "head_hash" and a slot.
|
||||
@ -51,37 +52,33 @@ impl<T: ClientDB> BeaconBlockStore<T> {
|
||||
/// slot number. If the slot is skipped, the function will return None.
|
||||
///
|
||||
/// If a block is found, a tuple of (block_hash, serialized_block) is returned.
|
||||
///
|
||||
/// Note: this function uses a loop instead of recursion as the compiler is over-strict when it
|
||||
/// comes to recursion and the `impl Trait` pattern. See:
|
||||
/// https://stackoverflow.com/questions/54032940/using-impl-trait-in-a-recursive-function
|
||||
pub fn block_at_slot(
|
||||
&self,
|
||||
head_hash: &[u8],
|
||||
head_hash: &Hash256,
|
||||
slot: u64,
|
||||
) -> Result<Option<(BeaconBlockHash, BeaconBlockSsz)>, BeaconBlockAtSlotError> {
|
||||
match self.get_serialized_block(head_hash)? {
|
||||
None => Err(BeaconBlockAtSlotError::UnknownBeaconBlock),
|
||||
Some(ssz) => {
|
||||
let (retrieved_slot, parent_hash) = slot_and_parent_from_block_ssz(&ssz, 0)
|
||||
.map_err(|_| BeaconBlockAtSlotError::InvalidBeaconBlock)?;
|
||||
match retrieved_slot {
|
||||
s if s == slot => Ok(Some((head_hash.to_vec(), ssz.to_vec()))),
|
||||
s if s < slot => Ok(None),
|
||||
_ => self.block_at_slot(&parent_hash, slot),
|
||||
) -> Result<Option<(Hash256, impl BeaconBlockReader)>, BeaconBlockAtSlotError> {
|
||||
let mut current_hash = *head_hash;
|
||||
|
||||
loop {
|
||||
if let Some(block_reader) = self.get_reader(¤t_hash)? {
|
||||
if block_reader.slot() == slot {
|
||||
break Ok(Some((current_hash, block_reader)));
|
||||
} else if block_reader.slot() < slot {
|
||||
break Ok(None);
|
||||
} else {
|
||||
current_hash = block_reader.parent_root();
|
||||
}
|
||||
} else {
|
||||
break Err(BeaconBlockAtSlotError::UnknownBeaconBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Read `block.slot` and `block.parent_root` from a SSZ-encoded block bytes.
|
||||
///
|
||||
/// Assumes the block starts at byte `i`.
|
||||
fn slot_and_parent_from_block_ssz(ssz: &[u8], i: usize) -> Result<(u64, Hash256), DecodeError> {
|
||||
// Assuming the slot is the first field on a block.
|
||||
let (slot, i) = u64::ssz_decode(&ssz, i)?;
|
||||
// Assuming the parent has is the second field on a block.
|
||||
let (parent_root, _) = Hash256::ssz_decode(&ssz, i)?;
|
||||
Ok((slot, parent_root))
|
||||
}
|
||||
|
||||
impl From<DBError> for BeaconBlockAtSlotError {
|
||||
fn from(e: DBError) -> Self {
|
||||
BeaconBlockAtSlotError::DBError(e.message)
|
||||
@ -101,81 +98,22 @@ mod tests {
|
||||
use types::BeaconBlock;
|
||||
use types::Hash256;
|
||||
|
||||
#[test]
|
||||
fn test_put_serialized_block() {
|
||||
let db = Arc::new(MemoryDB::open());
|
||||
let store = BeaconBlockStore::new(db.clone());
|
||||
|
||||
let ssz = "some bytes".as_bytes();
|
||||
let hash = &Hash256::from("some hash".as_bytes()).to_vec();
|
||||
|
||||
store.put_serialized_block(hash, ssz).unwrap();
|
||||
assert_eq!(db.get(DB_COLUMN, hash).unwrap().unwrap(), ssz);
|
||||
}
|
||||
test_crud_for_store!(BeaconBlockStore, DB_COLUMN);
|
||||
|
||||
#[test]
|
||||
fn test_get_serialized_block() {
|
||||
fn head_hash_slot_too_low() {
|
||||
let db = Arc::new(MemoryDB::open());
|
||||
let store = BeaconBlockStore::new(db.clone());
|
||||
let bs = Arc::new(BeaconBlockStore::new(db.clone()));
|
||||
let mut rng = XorShiftRng::from_seed([42; 16]);
|
||||
|
||||
let ssz = "some bytes".as_bytes();
|
||||
let hash = &Hash256::from("some hash".as_bytes()).to_vec();
|
||||
let mut block = BeaconBlock::random_for_test(&mut rng);
|
||||
block.slot = 10;
|
||||
|
||||
db.put(DB_COLUMN, hash, ssz).unwrap();
|
||||
assert_eq!(store.get_serialized_block(hash).unwrap().unwrap(), ssz);
|
||||
}
|
||||
let block_root = block.canonical_root();
|
||||
bs.put(&block_root, &ssz_encode(&block)).unwrap();
|
||||
|
||||
#[test]
|
||||
fn test_get_unknown_serialized_block() {
|
||||
let db = Arc::new(MemoryDB::open());
|
||||
let store = BeaconBlockStore::new(db.clone());
|
||||
|
||||
let ssz = "some bytes".as_bytes();
|
||||
let hash = &Hash256::from("some hash".as_bytes()).to_vec();
|
||||
let other_hash = &Hash256::from("another hash".as_bytes()).to_vec();
|
||||
|
||||
db.put(DB_COLUMN, other_hash, ssz).unwrap();
|
||||
assert_eq!(store.get_serialized_block(hash).unwrap(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_block_exists() {
|
||||
let db = Arc::new(MemoryDB::open());
|
||||
let store = BeaconBlockStore::new(db.clone());
|
||||
|
||||
let ssz = "some bytes".as_bytes();
|
||||
let hash = &Hash256::from("some hash".as_bytes()).to_vec();
|
||||
|
||||
db.put(DB_COLUMN, hash, ssz).unwrap();
|
||||
assert!(store.block_exists(hash).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_block_does_not_exist() {
|
||||
let db = Arc::new(MemoryDB::open());
|
||||
let store = BeaconBlockStore::new(db.clone());
|
||||
|
||||
let ssz = "some bytes".as_bytes();
|
||||
let hash = &Hash256::from("some hash".as_bytes()).to_vec();
|
||||
let other_hash = &Hash256::from("another hash".as_bytes()).to_vec();
|
||||
|
||||
db.put(DB_COLUMN, hash, ssz).unwrap();
|
||||
assert!(!store.block_exists(other_hash).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_block() {
|
||||
let db = Arc::new(MemoryDB::open());
|
||||
let store = BeaconBlockStore::new(db.clone());
|
||||
|
||||
let ssz = "some bytes".as_bytes();
|
||||
let hash = &Hash256::from("some hash".as_bytes()).to_vec();
|
||||
|
||||
db.put(DB_COLUMN, hash, ssz).unwrap();
|
||||
assert!(db.exists(DB_COLUMN, hash).unwrap());
|
||||
|
||||
store.delete_block(hash).unwrap();
|
||||
assert!(!db.exists(DB_COLUMN, hash).unwrap());
|
||||
let result = bs.block_at_slot(&block_root, 11).unwrap();
|
||||
assert_eq!(result, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -184,12 +122,14 @@ mod tests {
|
||||
let store = BeaconBlockStore::new(db.clone());
|
||||
|
||||
let ssz = "definitly not a valid block".as_bytes();
|
||||
let hash = &Hash256::from("some hash".as_bytes()).to_vec();
|
||||
let hash = &Hash256::from("some hash".as_bytes());
|
||||
|
||||
db.put(DB_COLUMN, hash, ssz).unwrap();
|
||||
assert_eq!(
|
||||
store.block_at_slot(hash, 42),
|
||||
Err(BeaconBlockAtSlotError::InvalidBeaconBlock)
|
||||
Err(BeaconBlockAtSlotError::DBError(
|
||||
"Bad BeaconBlock SSZ.".into()
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
@ -199,8 +139,8 @@ mod tests {
|
||||
let store = BeaconBlockStore::new(db.clone());
|
||||
|
||||
let ssz = "some bytes".as_bytes();
|
||||
let hash = &Hash256::from("some hash".as_bytes()).to_vec();
|
||||
let other_hash = &Hash256::from("another hash".as_bytes()).to_vec();
|
||||
let hash = &Hash256::from("some hash".as_bytes());
|
||||
let other_hash = &Hash256::from("another hash".as_bytes());
|
||||
|
||||
db.put(DB_COLUMN, hash, ssz).unwrap();
|
||||
assert_eq!(
|
||||
@ -228,7 +168,7 @@ mod tests {
|
||||
for w in 0..wc {
|
||||
let key = (t * w) as u8;
|
||||
let val = 42;
|
||||
bs.put_serialized_block(&vec![key], &vec![val]).unwrap();
|
||||
bs.put(&[key][..].into(), &vec![val]).unwrap();
|
||||
}
|
||||
});
|
||||
handles.push(handle);
|
||||
@ -241,8 +181,8 @@ mod tests {
|
||||
for t in 0..thread_count {
|
||||
for w in 0..write_count {
|
||||
let key = (t * w) as u8;
|
||||
assert!(bs.block_exists(&vec![key]).unwrap());
|
||||
let val = bs.get_serialized_block(&vec![key]).unwrap().unwrap();
|
||||
assert!(bs.exists(&[key][..].into()).unwrap());
|
||||
let val = bs.get(&[key][..].into()).unwrap().unwrap();
|
||||
assert_eq!(vec![42], val);
|
||||
}
|
||||
}
|
||||
@ -281,13 +221,7 @@ mod tests {
|
||||
block.slot = slots[i];
|
||||
|
||||
let ssz = ssz_encode(&block);
|
||||
db.put(DB_COLUMN, &hashes[i].to_vec(), &ssz).unwrap();
|
||||
|
||||
// Ensure the slot and parent_root decoding fn works correctly.
|
||||
let (decoded_slot, decoded_parent_root) =
|
||||
slot_and_parent_from_block_ssz(&ssz, 0).unwrap();
|
||||
assert_eq!(decoded_slot, block.slot);
|
||||
assert_eq!(decoded_parent_root, block.parent_root);
|
||||
db.put(DB_COLUMN, &hashes[i], &ssz).unwrap();
|
||||
|
||||
blocks.push(block);
|
||||
}
|
||||
@ -295,14 +229,12 @@ mod tests {
|
||||
// Test that certain slots can be reached from certain hashes.
|
||||
let test_cases = vec![(4, 4), (4, 3), (4, 2), (4, 1), (4, 0)];
|
||||
for (hashes_index, slot_index) in test_cases {
|
||||
let (matched_block_hash, matched_block_ssz) = bs
|
||||
let (matched_block_hash, reader) = bs
|
||||
.block_at_slot(&hashes[hashes_index], slots[slot_index])
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let (retrieved_slot, _) =
|
||||
slot_and_parent_from_block_ssz(&matched_block_ssz, 0).unwrap();
|
||||
assert_eq!(retrieved_slot, slots[slot_index]);
|
||||
assert_eq!(matched_block_hash, hashes[slot_index].to_vec());
|
||||
assert_eq!(matched_block_hash, hashes[slot_index]);
|
||||
assert_eq!(reader.slot(), slots[slot_index]);
|
||||
}
|
||||
|
||||
let ssz = bs.block_at_slot(&hashes[4], 2).unwrap();
|
||||
|
68
lighthouse/db/src/stores/beacon_state_store.rs
Normal file
68
lighthouse/db/src/stores/beacon_state_store.rs
Normal file
@ -0,0 +1,68 @@
|
||||
use super::STATES_DB_COLUMN as DB_COLUMN;
|
||||
use super::{ClientDB, DBError};
|
||||
use ssz::Decodable;
|
||||
use std::sync::Arc;
|
||||
use types::{readers::BeaconStateReader, BeaconState, Hash256};
|
||||
|
||||
pub struct BeaconStateStore<T>
|
||||
where
|
||||
T: ClientDB,
|
||||
{
|
||||
db: Arc<T>,
|
||||
}
|
||||
|
||||
// Implements `put`, `get`, `exists` and `delete` for the store.
|
||||
impl_crud_for_store!(BeaconStateStore, DB_COLUMN);
|
||||
|
||||
impl<T: ClientDB> BeaconStateStore<T> {
|
||||
pub fn new(db: Arc<T>) -> Self {
|
||||
Self { db }
|
||||
}
|
||||
|
||||
/// Retuns an object implementing `BeaconStateReader`, or `None` (if hash not known).
|
||||
///
|
||||
/// Note: Presently, this function fully deserializes a `BeaconState` and returns that. In the
|
||||
/// future, it would be ideal to return an object capable of reading directly from serialized
|
||||
/// SSZ bytes.
|
||||
pub fn get_reader(&self, hash: &Hash256) -> Result<Option<impl BeaconStateReader>, DBError> {
|
||||
match self.get(&hash)? {
|
||||
None => Ok(None),
|
||||
Some(ssz) => {
|
||||
let (state, _) = BeaconState::ssz_decode(&ssz, 0).map_err(|_| DBError {
|
||||
message: "Bad State SSZ.".to_string(),
|
||||
})?;
|
||||
Ok(Some(state))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::super::MemoryDB;
|
||||
use super::*;
|
||||
|
||||
use std::sync::Arc;
|
||||
use ssz::ssz_encode;
|
||||
use types::Hash256;
|
||||
use types::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
|
||||
test_crud_for_store!(BeaconStateStore, DB_COLUMN);
|
||||
|
||||
#[test]
|
||||
fn test_reader() {
|
||||
let db = Arc::new(MemoryDB::open());
|
||||
let store = BeaconStateStore::new(db.clone());
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([42; 16]);
|
||||
let state = BeaconState::random_for_test(&mut rng);
|
||||
let state_root = state.canonical_root();
|
||||
|
||||
store.put(&state_root, &ssz_encode(&state)).unwrap();
|
||||
|
||||
let reader = store.get_reader(&state_root).unwrap().unwrap();
|
||||
let decoded = reader.into_beacon_state().unwrap();
|
||||
|
||||
assert_eq!(state, decoded);
|
||||
}
|
||||
}
|
103
lighthouse/db/src/stores/macros.rs
Normal file
103
lighthouse/db/src/stores/macros.rs
Normal file
@ -0,0 +1,103 @@
|
||||
macro_rules! impl_crud_for_store {
|
||||
($store: ident, $db_column: expr) => {
|
||||
impl<T: ClientDB> $store<T> {
|
||||
pub fn put(&self, hash: &Hash256, ssz: &[u8]) -> Result<(), DBError> {
|
||||
self.db.put($db_column, hash, ssz)
|
||||
}
|
||||
|
||||
pub fn get(&self, hash: &Hash256) -> Result<Option<Vec<u8>>, DBError> {
|
||||
self.db.get($db_column, hash)
|
||||
}
|
||||
|
||||
pub fn exists(&self, hash: &Hash256) -> Result<bool, DBError> {
|
||||
self.db.exists($db_column, hash)
|
||||
}
|
||||
|
||||
pub fn delete(&self, hash: &Hash256) -> Result<(), DBError> {
|
||||
self.db.delete($db_column, hash)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! test_crud_for_store {
|
||||
($store: ident, $db_column: expr) => {
|
||||
#[test]
|
||||
fn test_put() {
|
||||
let db = Arc::new(MemoryDB::open());
|
||||
let store = $store::new(db.clone());
|
||||
|
||||
let ssz = "some bytes".as_bytes();
|
||||
let hash = &Hash256::from("some hash".as_bytes());
|
||||
|
||||
store.put(hash, ssz).unwrap();
|
||||
assert_eq!(db.get(DB_COLUMN, hash).unwrap().unwrap(), ssz);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get() {
|
||||
let db = Arc::new(MemoryDB::open());
|
||||
let store = $store::new(db.clone());
|
||||
|
||||
let ssz = "some bytes".as_bytes();
|
||||
let hash = &Hash256::from("some hash".as_bytes());
|
||||
|
||||
db.put(DB_COLUMN, hash, ssz).unwrap();
|
||||
assert_eq!(store.get(hash).unwrap().unwrap(), ssz);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_unknown() {
|
||||
let db = Arc::new(MemoryDB::open());
|
||||
let store = $store::new(db.clone());
|
||||
|
||||
let ssz = "some bytes".as_bytes();
|
||||
let hash = &Hash256::from("some hash".as_bytes());
|
||||
let other_hash = &Hash256::from("another hash".as_bytes());
|
||||
|
||||
db.put(DB_COLUMN, other_hash, ssz).unwrap();
|
||||
assert_eq!(store.get(hash).unwrap(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exists() {
|
||||
let db = Arc::new(MemoryDB::open());
|
||||
let store = $store::new(db.clone());
|
||||
|
||||
let ssz = "some bytes".as_bytes();
|
||||
let hash = &Hash256::from("some hash".as_bytes());
|
||||
|
||||
db.put(DB_COLUMN, hash, ssz).unwrap();
|
||||
assert!(store.exists(hash).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_block_does_not_exist() {
|
||||
let db = Arc::new(MemoryDB::open());
|
||||
let store = $store::new(db.clone());
|
||||
|
||||
let ssz = "some bytes".as_bytes();
|
||||
let hash = &Hash256::from("some hash".as_bytes());
|
||||
let other_hash = &Hash256::from("another hash".as_bytes());
|
||||
|
||||
db.put(DB_COLUMN, hash, ssz).unwrap();
|
||||
assert!(!store.exists(other_hash).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete() {
|
||||
let db = Arc::new(MemoryDB::open());
|
||||
let store = $store::new(db.clone());
|
||||
|
||||
let ssz = "some bytes".as_bytes();
|
||||
let hash = &Hash256::from("some hash".as_bytes());
|
||||
|
||||
db.put(DB_COLUMN, hash, ssz).unwrap();
|
||||
assert!(db.exists(DB_COLUMN, hash).unwrap());
|
||||
|
||||
store.delete(hash).unwrap();
|
||||
assert!(!db.exists(DB_COLUMN, hash).unwrap());
|
||||
}
|
||||
};
|
||||
}
|
@ -1,17 +1,27 @@
|
||||
use super::{ClientDB, DBError};
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
mod beacon_block_store;
|
||||
mod beacon_state_store;
|
||||
mod pow_chain_store;
|
||||
mod validator_store;
|
||||
|
||||
pub use self::beacon_block_store::{BeaconBlockAtSlotError, BeaconBlockStore};
|
||||
pub use self::beacon_state_store::BeaconStateStore;
|
||||
pub use self::pow_chain_store::PoWChainStore;
|
||||
pub use self::validator_store::{ValidatorStore, ValidatorStoreError};
|
||||
|
||||
use super::bls;
|
||||
|
||||
pub const BLOCKS_DB_COLUMN: &str = "blocks";
|
||||
pub const STATES_DB_COLUMN: &str = "states";
|
||||
pub const POW_CHAIN_DB_COLUMN: &str = "powchain";
|
||||
pub const VALIDATOR_DB_COLUMN: &str = "validator";
|
||||
|
||||
pub const COLUMNS: [&str; 3] = [BLOCKS_DB_COLUMN, POW_CHAIN_DB_COLUMN, VALIDATOR_DB_COLUMN];
|
||||
pub const COLUMNS: [&str; 4] = [
|
||||
BLOCKS_DB_COLUMN,
|
||||
STATES_DB_COLUMN,
|
||||
POW_CHAIN_DB_COLUMN,
|
||||
VALIDATOR_DB_COLUMN,
|
||||
];
|
||||
|
@ -26,9 +26,9 @@ impl<T: ClientDB> PoWChainStore<T> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
extern crate types;
|
||||
|
||||
use super::*;
|
||||
|
||||
use super::super::super::MemoryDB;
|
||||
use super::*;
|
||||
|
||||
use self::types::Hash256;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user