Add progress on test rig
This commit is contained in:
parent
b29934aed4
commit
6fd3a1a03e
50
beacon_node/beacon_chain/src/attestation_targets.rs
Normal file
50
beacon_node/beacon_chain/src/attestation_targets.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use crate::{BeaconChain, CheckPoint, ClientDB, SlotClock};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::RwLockReadGuard;
|
||||
use types::{BeaconBlock, BeaconState, Hash256};
|
||||
|
||||
pub struct AttestationTargets {
|
||||
map: HashMap<u64, Hash256>,
|
||||
}
|
||||
|
||||
impl AttestationTargets {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
map: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, validator_index: u64) -> Option<&Hash256> {
|
||||
self.map.get(&validator_index)
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, validator_index: u64, block_hash: Hash256) -> Option<Hash256> {
|
||||
self.map.insert(validator_index, block_hash)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> BeaconChain<T, U>
|
||||
where
|
||||
T: ClientDB,
|
||||
U: SlotClock,
|
||||
{
|
||||
pub fn insert_latest_attestation_target(&self, validator_index: u64, block_root: Hash256) {
|
||||
let mut targets = self
|
||||
.latest_attestation_targets
|
||||
.write()
|
||||
.expect("CRITICAL: CanonicalHead poisioned.");
|
||||
targets.insert(validator_index, block_root);
|
||||
}
|
||||
|
||||
pub fn get_latest_attestation_target(&self, validator_index: u64) -> Option<Hash256> {
|
||||
let targets = self
|
||||
.latest_attestation_targets
|
||||
.read()
|
||||
.expect("CRITICAL: CanonicalHead poisioned.");
|
||||
|
||||
match targets.get(validator_index) {
|
||||
Some(hash) => Some(hash.clone()),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
37
beacon_node/beacon_chain/src/block_graph.rs
Normal file
37
beacon_node/beacon_chain/src/block_graph.rs
Normal file
@ -0,0 +1,37 @@
|
||||
use crate::{BeaconChain, CheckPoint, ClientDB, SlotClock};
|
||||
use std::collections::HashSet;
|
||||
use std::sync::{RwLock, RwLockReadGuard};
|
||||
use types::{BeaconBlock, BeaconState, Hash256};
|
||||
|
||||
pub struct BlockGraph {
|
||||
pub leaves: RwLock<HashSet<Hash256>>,
|
||||
}
|
||||
|
||||
impl BlockGraph {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
leaves: RwLock::new(HashSet::new()),
|
||||
}
|
||||
}
|
||||
/// Add a new leaf to the block hash graph. Returns `true` if the leaf was built upon another
|
||||
/// leaf.
|
||||
pub fn add_leaf(&self, parent: &Hash256, leaf: Hash256) -> bool {
|
||||
let mut leaves = self
|
||||
.leaves
|
||||
.write()
|
||||
.expect("CRITICAL: BlockGraph poisioned.");
|
||||
|
||||
if leaves.contains(parent) {
|
||||
leaves.remove(parent);
|
||||
leaves.insert(leaf);
|
||||
true
|
||||
} else {
|
||||
leaves.insert(leaf);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn leaves(&self) -> RwLockReadGuard<HashSet<Hash256>> {
|
||||
self.leaves.read().expect("CRITICAL: BlockGraph poisioned.")
|
||||
}
|
||||
}
|
@ -54,7 +54,7 @@ where
|
||||
U: SlotClock,
|
||||
Error: From<<U as SlotClock>::Error>,
|
||||
{
|
||||
pub fn process_block<V>(&mut self, block: V) -> Result<Outcome, Error>
|
||||
pub fn process_block<V>(&self, block: V) -> Result<Outcome, Error>
|
||||
where
|
||||
V: BeaconBlockReader + Encodable + Sized,
|
||||
{
|
||||
@ -103,14 +103,20 @@ where
|
||||
self.block_store.put(&block_root, &ssz_encode(&block)[..])?;
|
||||
self.state_store.put(&state_root, &ssz_encode(&state)[..])?;
|
||||
|
||||
// Update leaf blocks so the implementation can track the chain heads.
|
||||
if self.leaf_blocks.contains(&block.parent_root()) {
|
||||
self.leaf_blocks.remove(&block.parent_root());
|
||||
self.block_graph
|
||||
.add_leaf(&parent_block_root, block_root.clone());
|
||||
|
||||
// If the parent block was the parent_block, automatically update the canonical head.
|
||||
//
|
||||
// TODO: this is a first-in-best-dressed scenario that is not ideal -- find a solution.
|
||||
if self.canonical_head().beacon_block_root == parent_block_root {
|
||||
self.update_canonical_head(
|
||||
block.clone(),
|
||||
block_root.clone(),
|
||||
state.clone(),
|
||||
state_root.clone(),
|
||||
);
|
||||
}
|
||||
if self.canonical_leaf_block == block.parent_root() {
|
||||
self.canonical_leaf_block = block_root;
|
||||
}
|
||||
self.leaf_blocks.insert(block_root);
|
||||
|
||||
// The block was sucessfully processed.
|
||||
Ok(Outcome::Processed)
|
||||
|
@ -18,18 +18,22 @@ impl<T, U> BeaconChain<T, U>
|
||||
where
|
||||
T: ClientDB,
|
||||
U: SlotClock,
|
||||
Error: From<<U as SlotClock>::Error>,
|
||||
{
|
||||
pub fn produce_block(
|
||||
&mut self,
|
||||
&self,
|
||||
randao_reveal: Signature,
|
||||
) -> Result<(BeaconBlock, BeaconState), Error> {
|
||||
) -> Result<(BeaconBlock, BeaconState), Error>
|
||||
where
|
||||
Error: From<<U>::Error>,
|
||||
{
|
||||
// TODO: allow producing a block from a previous (or future?) slot.
|
||||
let present_slot = self
|
||||
.slot_clock
|
||||
.present_slot()?
|
||||
.present_slot()
|
||||
.map_err(|e| e.into())?
|
||||
.ok_or(Error::PresentSlotIsNone)?;
|
||||
|
||||
let parent_root = self.canonical_leaf_block;
|
||||
let parent_root = self.canonical_head().beacon_block_root;
|
||||
let parent_block_reader = self
|
||||
.block_store
|
||||
.get_reader(&parent_root)?
|
||||
@ -43,7 +47,7 @@ where
|
||||
|
||||
let mut block = BeaconBlock {
|
||||
slot: present_slot,
|
||||
parent_root,
|
||||
parent_root: parent_root.clone(),
|
||||
state_root: Hash256::zero(), // Updated after the state is calculated.
|
||||
randao_reveal: randao_reveal,
|
||||
eth1_data: Eth1Data {
|
||||
|
34
beacon_node/beacon_chain/src/canonical_head.rs
Normal file
34
beacon_node/beacon_chain/src/canonical_head.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use crate::{BeaconChain, CheckPoint, ClientDB, SlotClock};
|
||||
use std::sync::RwLockReadGuard;
|
||||
use types::{BeaconBlock, BeaconState, Hash256};
|
||||
|
||||
impl<T, U> BeaconChain<T, U>
|
||||
where
|
||||
T: ClientDB,
|
||||
U: SlotClock,
|
||||
{
|
||||
pub fn update_canonical_head(
|
||||
&self,
|
||||
new_beacon_block: BeaconBlock,
|
||||
new_beacon_block_root: Hash256,
|
||||
new_beacon_state: BeaconState,
|
||||
new_beacon_state_root: Hash256,
|
||||
) {
|
||||
let mut canonical_head = self
|
||||
.canonical_head
|
||||
.write()
|
||||
.expect("CRITICAL: CanonicalHead poisioned.");
|
||||
canonical_head.update(
|
||||
new_beacon_block,
|
||||
new_beacon_block_root,
|
||||
new_beacon_state,
|
||||
new_beacon_state_root,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn canonical_head(&self) -> RwLockReadGuard<CheckPoint> {
|
||||
self.canonical_head
|
||||
.read()
|
||||
.expect("CRITICAL: CanonicalHead poisioned.")
|
||||
}
|
||||
}
|
49
beacon_node/beacon_chain/src/info.rs
Normal file
49
beacon_node/beacon_chain/src/info.rs
Normal file
@ -0,0 +1,49 @@
|
||||
use super::{BeaconChain, ClientDB, SlotClock};
|
||||
use types::PublicKey;
|
||||
|
||||
impl<T, U> BeaconChain<T, U>
|
||||
where
|
||||
T: ClientDB,
|
||||
U: SlotClock,
|
||||
{
|
||||
pub fn validator_index(&self, pubkey: &PublicKey) -> Option<usize> {
|
||||
for (i, validator) in self
|
||||
.canonical_head()
|
||||
.beacon_state
|
||||
.validator_registry
|
||||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
if validator.pubkey == *pubkey {
|
||||
return Some(i);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn proposer_slots(&self, validator_index: usize) -> Option<u64> {
|
||||
if let Some(validator) = self
|
||||
.canonical_head()
|
||||
.beacon_state
|
||||
.validator_registry
|
||||
.get(validator_index)
|
||||
{
|
||||
Some(validator.proposer_slots)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn present_slot(&self) -> Option<u64> {
|
||||
match self.slot_clock.present_slot() {
|
||||
Ok(some_slot) => some_slot,
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn block_proposer(&self, slot: u64) -> Option<usize> {
|
||||
//TODO: this is a stub; fix.
|
||||
let validator_count = self.canonical_head().beacon_state.validator_registry.len();
|
||||
Some((slot as usize) % validator_count)
|
||||
}
|
||||
}
|
@ -1,8 +1,14 @@
|
||||
mod attestation_targets;
|
||||
mod block_graph;
|
||||
mod block_processing;
|
||||
mod block_production;
|
||||
pub mod block_production;
|
||||
mod canonical_head;
|
||||
mod info;
|
||||
mod lmd_ghost;
|
||||
mod state_transition;
|
||||
|
||||
use self::attestation_targets::AttestationTargets;
|
||||
use self::block_graph::BlockGraph;
|
||||
use db::{
|
||||
stores::{BeaconBlockStore, BeaconStateStore},
|
||||
ClientDB, DBError,
|
||||
@ -12,18 +18,11 @@ use slot_clock::SlotClock;
|
||||
use spec::ChainSpec;
|
||||
use ssz::ssz_encode;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::Arc;
|
||||
use types::Hash256;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use types::{BeaconBlock, BeaconState, Hash256, PublicKey};
|
||||
|
||||
pub use self::block_processing::Outcome as BlockProcessingOutcome;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct CheckPoint {
|
||||
block_root: Hash256,
|
||||
state_root: Hash256,
|
||||
slot: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum BeaconChainError {
|
||||
InsufficientValidators,
|
||||
@ -31,15 +30,51 @@ pub enum BeaconChainError {
|
||||
DBError(String),
|
||||
}
|
||||
|
||||
pub struct CheckPoint {
|
||||
beacon_block: BeaconBlock,
|
||||
beacon_block_root: Hash256,
|
||||
beacon_state: BeaconState,
|
||||
beacon_state_root: Hash256,
|
||||
}
|
||||
|
||||
impl CheckPoint {
|
||||
pub fn new(
|
||||
beacon_block: BeaconBlock,
|
||||
beacon_block_root: Hash256,
|
||||
beacon_state: BeaconState,
|
||||
beacon_state_root: Hash256,
|
||||
) -> Self {
|
||||
Self {
|
||||
beacon_block,
|
||||
beacon_block_root,
|
||||
beacon_state,
|
||||
beacon_state_root,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(
|
||||
&mut self,
|
||||
beacon_block: BeaconBlock,
|
||||
beacon_block_root: Hash256,
|
||||
beacon_state: BeaconState,
|
||||
beacon_state_root: Hash256,
|
||||
) {
|
||||
self.beacon_block = beacon_block;
|
||||
self.beacon_block_root = beacon_block_root;
|
||||
self.beacon_state = beacon_state;
|
||||
self.beacon_state_root = beacon_state_root;
|
||||
}
|
||||
}
|
||||
|
||||
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 block_graph: BlockGraph,
|
||||
canonical_head: RwLock<CheckPoint>,
|
||||
finalized_head: RwLock<CheckPoint>,
|
||||
pub latest_attestation_targets: RwLock<AttestationTargets>,
|
||||
pub spec: ChainSpec,
|
||||
latest_attestation_targets: HashMap<usize, Hash256>,
|
||||
finalized_checkpoint: CheckPoint,
|
||||
}
|
||||
|
||||
impl<T, U> BeaconChain<T, U>
|
||||
@ -65,24 +100,33 @@ where
|
||||
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);
|
||||
let block_graph = BlockGraph::new();
|
||||
block_graph.add_leaf(&Hash256::zero(), block_root.clone());
|
||||
|
||||
let finalized_checkpoint = CheckPoint {
|
||||
block_root,
|
||||
state_root,
|
||||
slot: genesis_block.slot,
|
||||
};
|
||||
let finalized_head = RwLock::new(CheckPoint::new(
|
||||
genesis_block.clone(),
|
||||
block_root.clone(),
|
||||
genesis_state.clone(),
|
||||
state_root.clone(),
|
||||
));
|
||||
let canonical_head = RwLock::new(CheckPoint::new(
|
||||
genesis_block.clone(),
|
||||
block_root.clone(),
|
||||
genesis_state.clone(),
|
||||
state_root.clone(),
|
||||
));
|
||||
|
||||
let latest_attestation_targets = RwLock::new(AttestationTargets::new());
|
||||
|
||||
Ok(Self {
|
||||
block_store,
|
||||
state_store,
|
||||
slot_clock,
|
||||
leaf_blocks,
|
||||
canonical_leaf_block: block_root,
|
||||
spec,
|
||||
latest_attestation_targets: HashMap::new(),
|
||||
finalized_checkpoint,
|
||||
block_graph,
|
||||
finalized_head,
|
||||
canonical_head,
|
||||
latest_attestation_targets,
|
||||
spec: spec,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -4,13 +4,12 @@ use db::{
|
||||
ClientDB, DBError,
|
||||
};
|
||||
use slot_clock::TestingSlotClockError;
|
||||
use ssz::{ssz_encode, Encodable};
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
use types::{
|
||||
readers::{BeaconBlockReader, BeaconStateReader},
|
||||
validator_registry::get_active_validator_indices,
|
||||
BeaconBlock, Hash256,
|
||||
Hash256,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
@ -53,7 +52,7 @@ where
|
||||
|
||||
let mut attestation_targets = Vec::with_capacity(active_validator_indices.len());
|
||||
for i in active_validator_indices {
|
||||
if let Some(target) = self.latest_attestation_targets.get(&i) {
|
||||
if let Some(target) = self.get_latest_attestation_target(i as u64) {
|
||||
attestation_targets.push(target);
|
||||
}
|
||||
}
|
||||
@ -62,8 +61,11 @@ where
|
||||
let mut head_vote_count = 0;
|
||||
|
||||
loop {
|
||||
let child_hashes_and_slots =
|
||||
get_child_hashes_and_slots(&self.block_store, &head_hash, &self.leaf_blocks)?;
|
||||
let child_hashes_and_slots = get_child_hashes_and_slots(
|
||||
&self.block_store,
|
||||
&head_hash,
|
||||
&self.block_graph.leaves(),
|
||||
)?;
|
||||
|
||||
if child_hashes_and_slots.len() == 0 {
|
||||
break;
|
||||
@ -90,7 +92,7 @@ where
|
||||
|
||||
fn get_vote_count<T: ClientDB>(
|
||||
block_store: &Arc<BeaconBlockStore<T>>,
|
||||
attestation_targets: &[&Hash256],
|
||||
attestation_targets: &[Hash256],
|
||||
block_root: &Hash256,
|
||||
slot: u64,
|
||||
) -> Result<u64, Error> {
|
||||
@ -99,7 +101,7 @@ fn get_vote_count<T: ClientDB>(
|
||||
let (root_at_slot, _) = block_store
|
||||
.block_at_slot(&block_root, slot)?
|
||||
.ok_or(Error::MissingBeaconBlock(*block_root))?;
|
||||
if root_at_slot == *block_root {
|
||||
if root_at_slot == *target {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
use beacon_chain::{BeaconChain, BlockProcessingOutcome};
|
||||
use self::utils::TestRig;
|
||||
use beacon_chain::BeaconChain;
|
||||
#[cfg(test)]
|
||||
use block_producer::{
|
||||
test_utils::{TestEpochMap, TestSigner},
|
||||
BeaconNode as BeaconBlockNode, BeaconNodeError as BeaconBlockNodeError, BlockProducer,
|
||||
};
|
||||
use block_producer::{test_utils::TestSigner, BlockProducer};
|
||||
use db::{
|
||||
stores::{BeaconBlockStore, BeaconStateStore},
|
||||
MemoryDB,
|
||||
@ -11,92 +9,19 @@ use db::{
|
||||
use slot_clock::TestingSlotClock;
|
||||
use spec::ChainSpec;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use types::{BeaconBlock, Keypair};
|
||||
use types::{Keypair, Validator};
|
||||
|
||||
struct DirectBeaconNode();
|
||||
|
||||
impl BeaconBlockNode for DirectBeaconNode {
|
||||
fn produce_beacon_block(&self, slot: u64) -> Result<Option<BeaconBlock>, BeaconBlockNodeError> {
|
||||
Err(BeaconBlockNodeError::DecodeFailure)
|
||||
}
|
||||
|
||||
/// Returns the value specified by the `set_next_publish_result`.
|
||||
fn publish_beacon_block(&self, block: BeaconBlock) -> Result<bool, BeaconBlockNodeError> {
|
||||
Err(BeaconBlockNodeError::DecodeFailure)
|
||||
}
|
||||
}
|
||||
|
||||
struct Validator {
|
||||
block_producer: BlockProducer<TestingSlotClock, DirectBeaconNode, TestEpochMap, TestSigner>,
|
||||
spec: Arc<ChainSpec>,
|
||||
epoch_map: Arc<TestEpochMap>,
|
||||
keypair: Keypair,
|
||||
beacon_node: Arc<DirectBeaconNode>,
|
||||
slot_clock: Arc<RwLock<TestingSlotClock>>,
|
||||
signer: Arc<TestSigner>,
|
||||
}
|
||||
|
||||
impl Validator {
|
||||
pub fn new() -> Self {
|
||||
let spec = Arc::new(ChainSpec::foundation());
|
||||
let keypair = Keypair::random();
|
||||
let slot_clock = Arc::new(RwLock::new(TestingSlotClock::new(0)));
|
||||
let signer = Arc::new(TestSigner::new(keypair.clone()));
|
||||
let beacon_node = Arc::new(DirectBeaconNode());
|
||||
let epoch_map = Arc::new(TestEpochMap::new());
|
||||
|
||||
let block_producer = BlockProducer::new(
|
||||
spec.clone(),
|
||||
epoch_map.clone(),
|
||||
slot_clock.clone(),
|
||||
beacon_node.clone(),
|
||||
signer.clone(),
|
||||
);
|
||||
|
||||
Self {
|
||||
block_producer,
|
||||
spec,
|
||||
epoch_map,
|
||||
keypair,
|
||||
beacon_node,
|
||||
slot_clock,
|
||||
signer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_validators(n: usize) -> Vec<Validator> {
|
||||
let mut validators = Vec::with_capacity(n);
|
||||
for _ in 0..n {
|
||||
validators.push(Validator::new());
|
||||
}
|
||||
validators
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
mod utils;
|
||||
|
||||
#[test]
|
||||
fn it_constructs() {
|
||||
let (_db, _chain) = in_memory_test_chain(ChainSpec::foundation());
|
||||
fn rig_can_generate_validators() {
|
||||
/*
|
||||
let (_db, mut chain) = in_memory_test_chain(ChainSpec::foundation());
|
||||
let validators = generate_validators(2, &chain);
|
||||
chain.spec = inject_validators_into_spec(chain.spec.clone(), &validators[..]);
|
||||
*/
|
||||
let mut rig = TestRig::new(ChainSpec::foundation());
|
||||
rig.generate_validators(2);
|
||||
}
|
||||
|
||||
/*
|
||||
|
58
beacon_node/beacon_chain/tests/utils/direct_beacon_node.rs
Normal file
58
beacon_node/beacon_chain/tests/utils/direct_beacon_node.rs
Normal file
@ -0,0 +1,58 @@
|
||||
use beacon_chain::{block_production::Error as BlockProductionError, BeaconChain};
|
||||
use block_producer::{BeaconNode as BeaconBlockNode, BeaconNodeError as BeaconBlockNodeError};
|
||||
use db::ClientDB;
|
||||
use slot_clock::SlotClock;
|
||||
use types::{BeaconBlock, PublicKey, Signature};
|
||||
|
||||
pub struct DirectBeaconNode<'a, T: ClientDB, U: SlotClock> {
|
||||
beacon_chain: &'a BeaconChain<T, U>,
|
||||
}
|
||||
|
||||
impl<'a, T: ClientDB, U: SlotClock> DirectBeaconNode<'a, T, U> {
|
||||
pub fn new(beacon_chain: &'a BeaconChain<T, U>) -> Self {
|
||||
Self { beacon_chain }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ClientDB, U: SlotClock> BeaconBlockNode for DirectBeaconNode<'a, T, U>
|
||||
where
|
||||
BlockProductionError: From<<U>::Error>,
|
||||
{
|
||||
fn proposer_nonce(&self, pubkey: &PublicKey) -> Result<u64, BeaconBlockNodeError> {
|
||||
let validator_index = self
|
||||
.beacon_chain
|
||||
.validator_index(pubkey)
|
||||
.ok_or_else(|| BeaconBlockNodeError::RemoteFailure("pubkey unknown.".to_string()))?;
|
||||
|
||||
self.beacon_chain
|
||||
.proposer_slots(validator_index)
|
||||
.ok_or_else(|| {
|
||||
BeaconBlockNodeError::RemoteFailure("validator_index unknown.".to_string())
|
||||
})
|
||||
}
|
||||
|
||||
fn produce_beacon_block(
|
||||
&self,
|
||||
slot: u64,
|
||||
randao_reveal: &Signature,
|
||||
) -> Result<Option<BeaconBlock>, BeaconBlockNodeError>
|
||||
where {
|
||||
let (block, _state) = self
|
||||
.beacon_chain
|
||||
.produce_block(randao_reveal.clone())
|
||||
.map_err(|e| BeaconBlockNodeError::RemoteFailure(format!("{:?}", e)))?;
|
||||
|
||||
if block.slot == slot {
|
||||
Ok(Some(block))
|
||||
} else {
|
||||
Err(BeaconBlockNodeError::RemoteFailure(
|
||||
"Unable to produce at non-current slot.".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the value specified by the `set_next_publish_result`.
|
||||
fn publish_beacon_block(&self, block: BeaconBlock) -> Result<bool, BeaconBlockNodeError> {
|
||||
Err(BeaconBlockNodeError::DecodeFailure)
|
||||
}
|
||||
}
|
37
beacon_node/beacon_chain/tests/utils/direct_duties.rs
Normal file
37
beacon_node/beacon_chain/tests/utils/direct_duties.rs
Normal file
@ -0,0 +1,37 @@
|
||||
use beacon_chain::{block_production::Error as BlockProductionError, BeaconChain};
|
||||
use block_producer::{DutiesReader, DutiesReaderError};
|
||||
use db::ClientDB;
|
||||
use slot_clock::SlotClock;
|
||||
use types::PublicKey;
|
||||
|
||||
pub struct DirectDuties<'a, T: ClientDB, U: SlotClock> {
|
||||
beacon_chain: &'a BeaconChain<T, U>,
|
||||
pubkey: PublicKey,
|
||||
}
|
||||
|
||||
impl<'a, T: ClientDB, U: SlotClock> DirectDuties<'a, T, U> {
|
||||
pub fn new(pubkey: PublicKey, beacon_chain: &'a BeaconChain<T, U>) -> Self {
|
||||
Self {
|
||||
beacon_chain,
|
||||
pubkey,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ClientDB, U: SlotClock> DutiesReader for DirectDuties<'a, T, U>
|
||||
where
|
||||
BlockProductionError: From<<U>::Error>,
|
||||
{
|
||||
fn is_block_production_slot(&self, _epoch: u64, slot: u64) -> Result<bool, DutiesReaderError> {
|
||||
let validator_index = self
|
||||
.beacon_chain
|
||||
.validator_index(&self.pubkey)
|
||||
.ok_or_else(|| DutiesReaderError::UnknownValidator)?;
|
||||
|
||||
match self.beacon_chain.block_proposer(slot) {
|
||||
Some(proposer) if proposer == validator_index => Ok(true),
|
||||
Some(_) => Ok(false),
|
||||
None => Err(DutiesReaderError::UnknownEpoch),
|
||||
}
|
||||
}
|
||||
}
|
9
beacon_node/beacon_chain/tests/utils/mod.rs
Normal file
9
beacon_node/beacon_chain/tests/utils/mod.rs
Normal file
@ -0,0 +1,9 @@
|
||||
mod direct_beacon_node;
|
||||
mod direct_duties;
|
||||
mod test_rig;
|
||||
mod validator;
|
||||
|
||||
pub use self::direct_beacon_node::DirectBeaconNode;
|
||||
pub use self::direct_duties::DirectDuties;
|
||||
pub use self::test_rig::TestRig;
|
||||
pub use self::validator::TestValidator;
|
85
beacon_node/beacon_chain/tests/utils/test_rig.rs
Normal file
85
beacon_node/beacon_chain/tests/utils/test_rig.rs
Normal file
@ -0,0 +1,85 @@
|
||||
use super::{DirectBeaconNode, DirectDuties, TestValidator};
|
||||
use beacon_chain::BeaconChain;
|
||||
#[cfg(test)]
|
||||
use block_producer::{test_utils::TestSigner, BlockProducer};
|
||||
use db::{
|
||||
stores::{BeaconBlockStore, BeaconStateStore},
|
||||
MemoryDB,
|
||||
};
|
||||
use slot_clock::TestingSlotClock;
|
||||
use spec::ChainSpec;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use types::{Keypair, Validator};
|
||||
|
||||
pub struct TestRig<'a> {
|
||||
db: Arc<MemoryDB>,
|
||||
beacon_chain: BeaconChain<MemoryDB, TestingSlotClock>,
|
||||
block_store: Arc<BeaconBlockStore<MemoryDB>>,
|
||||
state_store: Arc<BeaconStateStore<MemoryDB>>,
|
||||
validators: Vec<TestValidator<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> TestRig<'a> {
|
||||
pub fn new(spec: ChainSpec) -> Self {
|
||||
let db = Arc::new(MemoryDB::open());
|
||||
let block_store = Arc::new(BeaconBlockStore::new(db.clone()));
|
||||
let state_store = Arc::new(BeaconStateStore::new(db.clone()));
|
||||
|
||||
let slot_clock = TestingSlotClock::new(0);
|
||||
|
||||
let mut beacon_chain =
|
||||
BeaconChain::genesis(state_store.clone(), block_store.clone(), slot_clock, spec)
|
||||
.unwrap();
|
||||
|
||||
/*
|
||||
let validators = generate_validators(validator_count, &beacon_chain);
|
||||
beacon_chain.spec = inject_validators_into_spec(beacon_chain.spec.clone(), &validators[..]);
|
||||
*/
|
||||
|
||||
Self {
|
||||
db,
|
||||
beacon_chain,
|
||||
block_store,
|
||||
state_store,
|
||||
validators: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_validators(&'a mut self, validator_count: usize) {
|
||||
self.validators = Vec::with_capacity(validator_count);
|
||||
for _ in 0..validator_count {
|
||||
self.validators.push(TestValidator::new(&self.beacon_chain));
|
||||
}
|
||||
self.beacon_chain.spec =
|
||||
inject_validators_into_spec(self.beacon_chain.spec.clone(), &self.validators[..]);
|
||||
}
|
||||
|
||||
pub fn process_next_slot(&mut self) {
|
||||
let slot = self
|
||||
.beacon_chain
|
||||
.present_slot()
|
||||
.expect("Unable to determine slot.")
|
||||
+ 1;
|
||||
self.beacon_chain.slot_clock.set_slot(slot);
|
||||
|
||||
let block_proposer = self
|
||||
.beacon_chain
|
||||
.block_proposer(slot)
|
||||
.expect("Unable to determine proposer.");
|
||||
|
||||
let validator = self
|
||||
.validators
|
||||
.get(block_proposer)
|
||||
.expect("Block proposer unknown");
|
||||
}
|
||||
}
|
||||
|
||||
fn inject_validators_into_spec(mut spec: ChainSpec, validators: &[TestValidator]) -> ChainSpec {
|
||||
spec.initial_validators = Vec::with_capacity(validators.len());
|
||||
spec.initial_balances = Vec::with_capacity(validators.len());
|
||||
for validator in validators {
|
||||
spec.initial_validators.push(validator.validator_record());
|
||||
spec.initial_balances.push(32_000_000_000); // 32 ETH
|
||||
}
|
||||
spec
|
||||
}
|
61
beacon_node/beacon_chain/tests/utils/validator.rs
Normal file
61
beacon_node/beacon_chain/tests/utils/validator.rs
Normal file
@ -0,0 +1,61 @@
|
||||
use super::{DirectBeaconNode, DirectDuties};
|
||||
use beacon_chain::BeaconChain;
|
||||
#[cfg(test)]
|
||||
use block_producer::{test_utils::TestSigner, BlockProducer};
|
||||
use db::MemoryDB;
|
||||
use slot_clock::TestingSlotClock;
|
||||
use spec::ChainSpec;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use types::{Keypair, Validator};
|
||||
|
||||
pub struct TestValidator<'a> {
|
||||
block_producer: BlockProducer<
|
||||
TestingSlotClock,
|
||||
DirectBeaconNode<'a, MemoryDB, TestingSlotClock>,
|
||||
DirectDuties<'a, MemoryDB, TestingSlotClock>,
|
||||
TestSigner,
|
||||
>,
|
||||
spec: Arc<ChainSpec>,
|
||||
epoch_map: Arc<DirectDuties<'a, MemoryDB, TestingSlotClock>>,
|
||||
keypair: Keypair,
|
||||
beacon_node: Arc<DirectBeaconNode<'a, MemoryDB, TestingSlotClock>>,
|
||||
slot_clock: Arc<RwLock<TestingSlotClock>>,
|
||||
signer: Arc<TestSigner>,
|
||||
}
|
||||
|
||||
impl<'a> TestValidator<'a> {
|
||||
pub fn new(beacon_chain: &'a BeaconChain<MemoryDB, TestingSlotClock>) -> Self {
|
||||
let spec = Arc::new(ChainSpec::foundation());
|
||||
let keypair = Keypair::random();
|
||||
let slot_clock = Arc::new(RwLock::new(TestingSlotClock::new(0)));
|
||||
let signer = Arc::new(TestSigner::new(keypair.clone()));
|
||||
let beacon_node = Arc::new(DirectBeaconNode::new(beacon_chain));
|
||||
let epoch_map = Arc::new(DirectDuties::new(keypair.pk.clone(), beacon_chain));
|
||||
|
||||
let block_producer = BlockProducer::new(
|
||||
spec.clone(),
|
||||
keypair.pk.clone(),
|
||||
epoch_map.clone(),
|
||||
slot_clock.clone(),
|
||||
beacon_node.clone(),
|
||||
signer.clone(),
|
||||
);
|
||||
|
||||
Self {
|
||||
block_producer,
|
||||
spec,
|
||||
epoch_map,
|
||||
keypair,
|
||||
beacon_node,
|
||||
slot_clock,
|
||||
signer,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validator_record(&self) -> Validator {
|
||||
Validator {
|
||||
pubkey: self.keypair.pk.clone(),
|
||||
..std::default::Default::default()
|
||||
}
|
||||
}
|
||||
}
|
@ -7,4 +7,5 @@ edition = "2018"
|
||||
[dependencies]
|
||||
slot_clock = { path = "../../eth2/utils/slot_clock" }
|
||||
spec = { path = "../../eth2/spec" }
|
||||
ssz = { path = "../../eth2/utils/ssz" }
|
||||
types = { path = "../../eth2/types" }
|
||||
|
@ -3,8 +3,9 @@ mod traits;
|
||||
|
||||
use slot_clock::SlotClock;
|
||||
use spec::ChainSpec;
|
||||
use ssz::ssz_encode;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use types::{BeaconBlock, Hash256, ProposalSignedData};
|
||||
use types::{BeaconBlock, Hash256, ProposalSignedData, PublicKey};
|
||||
|
||||
pub use self::traits::{BeaconNode, BeaconNodeError, DutiesReader, DutiesReaderError, Signer};
|
||||
|
||||
@ -24,6 +25,8 @@ pub enum PollOutcome {
|
||||
BeaconNodeUnableToProduceBlock(u64),
|
||||
/// The signer failed to sign the message.
|
||||
SignerRejection(u64),
|
||||
/// The public key for this validator is not an active validator.
|
||||
ValidatorIsUnknown(u64),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
@ -44,6 +47,7 @@ pub enum Error {
|
||||
/// Relies upon an external service to keep the `EpochDutiesMap` updated.
|
||||
pub struct BlockProducer<T: SlotClock, U: BeaconNode, V: DutiesReader, W: Signer> {
|
||||
pub last_processed_slot: u64,
|
||||
pubkey: PublicKey,
|
||||
spec: Arc<ChainSpec>,
|
||||
epoch_map: Arc<V>,
|
||||
slot_clock: Arc<RwLock<T>>,
|
||||
@ -55,6 +59,7 @@ impl<T: SlotClock, U: BeaconNode, V: DutiesReader, W: Signer> BlockProducer<T, U
|
||||
/// Returns a new instance where `last_processed_slot == 0`.
|
||||
pub fn new(
|
||||
spec: Arc<ChainSpec>,
|
||||
pubkey: PublicKey,
|
||||
epoch_map: Arc<V>,
|
||||
slot_clock: Arc<RwLock<T>>,
|
||||
beacon_node: Arc<U>,
|
||||
@ -62,6 +67,7 @@ impl<T: SlotClock, U: BeaconNode, V: DutiesReader, W: Signer> BlockProducer<T, U
|
||||
) -> Self {
|
||||
Self {
|
||||
last_processed_slot: 0,
|
||||
pubkey,
|
||||
spec,
|
||||
epoch_map,
|
||||
slot_clock,
|
||||
@ -96,6 +102,9 @@ impl<T: SlotClock, U: BeaconNode, V: DutiesReader, W: Signer> BlockProducer<T, U
|
||||
Err(DutiesReaderError::UnknownEpoch) => {
|
||||
return Ok(PollOutcome::ProducerDutiesUnknown(slot))
|
||||
}
|
||||
Err(DutiesReaderError::UnknownValidator) => {
|
||||
return Ok(PollOutcome::ValidatorIsUnknown(slot))
|
||||
}
|
||||
Err(DutiesReaderError::Poisoned) => return Err(Error::EpochMapPoisoned),
|
||||
};
|
||||
|
||||
@ -122,7 +131,20 @@ impl<T: SlotClock, U: BeaconNode, V: DutiesReader, W: Signer> BlockProducer<T, U
|
||||
/// The slash-protection code is not yet implemented. There is zero protection against
|
||||
/// slashing.
|
||||
fn produce_block(&mut self, slot: u64) -> Result<PollOutcome, Error> {
|
||||
if let Some(block) = self.beacon_node.produce_beacon_block(slot)? {
|
||||
let randao_reveal = {
|
||||
let producer_nonce = self.beacon_node.proposer_nonce(&self.pubkey)?;
|
||||
// TODO: add domain, etc to this message.
|
||||
let message = ssz_encode(&producer_nonce);
|
||||
match self.signer.bls_sign(&message) {
|
||||
None => return Ok(PollOutcome::SignerRejection(slot)),
|
||||
Some(signature) => signature,
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(block) = self
|
||||
.beacon_node
|
||||
.produce_beacon_block(slot, &randao_reveal)?
|
||||
{
|
||||
if self.safe_to_produce(&block) {
|
||||
if let Some(block) = self.sign_block(block) {
|
||||
self.beacon_node.publish_beacon_block(block)?;
|
||||
|
@ -1,20 +1,30 @@
|
||||
use crate::traits::{BeaconNode, BeaconNodeError};
|
||||
use std::sync::RwLock;
|
||||
use types::BeaconBlock;
|
||||
use types::{BeaconBlock, PublicKey, Signature};
|
||||
|
||||
type NonceResult = Result<u64, BeaconNodeError>;
|
||||
type ProduceResult = Result<Option<BeaconBlock>, BeaconNodeError>;
|
||||
type PublishResult = Result<bool, BeaconNodeError>;
|
||||
|
||||
/// A test-only struct used to simulate a Beacon Node.
|
||||
#[derive(Default)]
|
||||
pub struct TestBeaconNode {
|
||||
pub produce_input: RwLock<Option<u64>>,
|
||||
pub nonce_input: RwLock<Option<PublicKey>>,
|
||||
pub nonce_result: RwLock<Option<NonceResult>>,
|
||||
|
||||
pub produce_input: RwLock<Option<(u64, Signature)>>,
|
||||
pub produce_result: RwLock<Option<ProduceResult>>,
|
||||
|
||||
pub publish_input: RwLock<Option<BeaconBlock>>,
|
||||
pub publish_result: RwLock<Option<PublishResult>>,
|
||||
}
|
||||
|
||||
impl TestBeaconNode {
|
||||
/// Set the result to be returned when `produce_beacon_block` is called.
|
||||
pub fn set_next_nonce_result(&self, result: NonceResult) {
|
||||
*self.nonce_result.write().unwrap() = Some(result);
|
||||
}
|
||||
|
||||
/// Set the result to be returned when `produce_beacon_block` is called.
|
||||
pub fn set_next_produce_result(&self, result: ProduceResult) {
|
||||
*self.produce_result.write().unwrap() = Some(result);
|
||||
@ -27,9 +37,17 @@ impl TestBeaconNode {
|
||||
}
|
||||
|
||||
impl BeaconNode for TestBeaconNode {
|
||||
fn proposer_nonce(&self, pubkey: &PublicKey) -> NonceResult {
|
||||
*self.nonce_input.write().unwrap() = Some(pubkey.clone());
|
||||
match *self.nonce_result.read().unwrap() {
|
||||
Some(ref r) => r.clone(),
|
||||
None => panic!("TestBeaconNode: nonce_result == None"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the value specified by the `set_next_produce_result`.
|
||||
fn produce_beacon_block(&self, slot: u64) -> ProduceResult {
|
||||
*self.produce_input.write().unwrap() = Some(slot);
|
||||
fn produce_beacon_block(&self, slot: u64, randao_reveal: &Signature) -> ProduceResult {
|
||||
*self.produce_input.write().unwrap() = Some((slot, randao_reveal.clone()));
|
||||
match *self.produce_result.read().unwrap() {
|
||||
Some(ref r) => r.clone(),
|
||||
None => panic!("TestBeaconNode: produce_result == None"),
|
||||
|
@ -1,4 +1,4 @@
|
||||
use types::{BeaconBlock, Signature};
|
||||
use types::{BeaconBlock, PublicKey, Signature};
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum BeaconNodeError {
|
||||
@ -8,10 +8,18 @@ pub enum BeaconNodeError {
|
||||
|
||||
/// Defines the methods required to produce and publish blocks on a Beacon Node.
|
||||
pub trait BeaconNode: Send + Sync {
|
||||
/// Requests the proposer nonce (presently named `proposer_slots`).
|
||||
fn proposer_nonce(&self, pubkey: &PublicKey) -> Result<u64, BeaconNodeError>;
|
||||
|
||||
/// Request that the node produces a block.
|
||||
///
|
||||
/// Returns Ok(None) if the Beacon Node is unable to produce at the given slot.
|
||||
fn produce_beacon_block(&self, slot: u64) -> Result<Option<BeaconBlock>, BeaconNodeError>;
|
||||
fn produce_beacon_block(
|
||||
&self,
|
||||
slot: u64,
|
||||
randao_reveal: &Signature,
|
||||
) -> Result<Option<BeaconBlock>, BeaconNodeError>;
|
||||
|
||||
/// Request that the node publishes a block.
|
||||
///
|
||||
/// Returns `true` if the publish was sucessful.
|
||||
@ -20,6 +28,7 @@ pub trait BeaconNode: Send + Sync {
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum DutiesReaderError {
|
||||
UnknownValidator,
|
||||
UnknownEpoch,
|
||||
Poisoned,
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ mod foundation;
|
||||
use bls::Signature;
|
||||
use types::{Address, Eth1Data, Hash256, Validator};
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct ChainSpec {
|
||||
/*
|
||||
* Misc
|
||||
|
Loading…
Reference in New Issue
Block a user