Add progress on test rig

This commit is contained in:
Paul Hauner 2019-01-24 17:05:48 +11:00
parent b29934aed4
commit 6fd3a1a03e
No known key found for this signature in database
GPG Key ID: D362883A9218FCC6
19 changed files with 595 additions and 144 deletions

View 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,
}
}
}

View 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.")
}
}

View File

@ -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)

View File

@ -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 {

View 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.")
}
}

View 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)
}
}

View File

@ -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,
})
}
}

View File

@ -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;
}
}

View File

@ -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);
}
/*

View 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)
}
}

View 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),
}
}
}

View 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;

View 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
}

View 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()
}
}
}

View File

@ -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" }

View File

@ -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)?;

View File

@ -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"),

View File

@ -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,
}

View File

@ -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