Tidy and add docstring to chain test harness.

This commit is contained in:
Paul Hauner 2019-02-01 18:48:37 +11:00
parent 4cc88c8cc7
commit cb85fbcdb2
No known key found for this signature in database
GPG Key ID: D362883A9218FCC6
7 changed files with 54 additions and 13 deletions

View File

@ -15,6 +15,12 @@ use std::iter::FromIterator;
use std::sync::Arc;
use types::{BeaconBlock, ChainSpec, FreeAttestation, Keypair, Validator};
/// The beacon chain harness simulates a single beacon node with `validator_count` validators connected
/// to it. Each validator is provided a borrow to the beacon chain, where it may read
/// information and submit blocks/attesations for processing.
///
/// This test harness is useful for testing validator and internal state transition logic. It
/// is not useful for testing that multiple beacon nodes can reach consensus.
pub struct BeaconChainHarness {
pub db: Arc<MemoryDB>,
pub beacon_chain: Arc<BeaconChain<MemoryDB, TestingSlotClock>>,
@ -25,6 +31,10 @@ pub struct BeaconChainHarness {
}
impl BeaconChainHarness {
/// Create a new harness with:
///
/// - A keypair, `BlockProducer` and `Attester` for each validator.
/// - A new BeaconChain struct where the given validators are in the genesis.
pub fn new(mut spec: ChainSpec, validator_count: usize) -> Self {
let db = Arc::new(MemoryDB::open());
let block_store = Arc::new(BeaconBlockStore::new(db.clone()));
@ -208,10 +218,12 @@ impl BeaconChainHarness {
debug!("Free attestations processed.");
}
/// Dump all blocks and states from the canonical beacon chain.
pub fn chain_dump(&self) -> Result<Vec<CheckPoint>, DumpError> {
self.beacon_chain.chain_dump()
}
/// Write the output of `chain_dump` to a JSON file.
pub fn dump_to_file(&self, filename: String, chain_dump: &Vec<CheckPoint>) {
let json = serde_json::to_string(chain_dump).unwrap();
let mut file = File::create(filename).unwrap();

View File

@ -1,10 +1,10 @@
use super::BenchingBeaconNode;
use super::DirectBeaconNode;
use attester::{BeaconNode as AttesterBeaconNode, BeaconNodeError as NodeError, PublishOutcome};
use db::ClientDB;
use slot_clock::SlotClock;
use types::{AttestationData, FreeAttestation};
impl<T: ClientDB, U: SlotClock> AttesterBeaconNode for BenchingBeaconNode<T, U> {
impl<T: ClientDB, U: SlotClock> AttesterBeaconNode for DirectBeaconNode<T, U> {
fn produce_attestation_data(
&self,
_slot: u64,

View File

@ -8,13 +8,19 @@ use types::{BeaconBlock, FreeAttestation};
mod attester;
mod producer;
pub struct BenchingBeaconNode<T: ClientDB, U: SlotClock> {
/// Connect directly to a borrowed `BeaconChain` instance so an attester/producer can request/submit
/// blocks/attestations.
///
/// `BeaconBlock`s and `FreeAttestation`s are not actually published to the `BeaconChain`, instead
/// they are stored inside this struct. This is to allow one to benchmark the submission of the
/// block/attestation directly, or modify it before submission.
pub struct DirectBeaconNode<T: ClientDB, U: SlotClock> {
beacon_chain: Arc<BeaconChain<T, U>>,
published_blocks: RwLock<Vec<BeaconBlock>>,
published_attestations: RwLock<Vec<FreeAttestation>>,
}
impl<T: ClientDB, U: SlotClock> BenchingBeaconNode<T, U> {
impl<T: ClientDB, U: SlotClock> DirectBeaconNode<T, U> {
pub fn new(beacon_chain: Arc<BeaconChain<T, U>>) -> Self {
Self {
beacon_chain,
@ -23,10 +29,12 @@ impl<T: ClientDB, U: SlotClock> BenchingBeaconNode<T, U> {
}
}
/// Get the last published block (if any).
pub fn last_published_block(&self) -> Option<BeaconBlock> {
Some(self.published_blocks.read().last()?.clone())
}
/// Get the last published attestation (if any).
pub fn last_published_free_attestation(&self) -> Option<FreeAttestation> {
Some(self.published_attestations.read().last()?.clone())
}

View File

@ -1,4 +1,4 @@
use super::BenchingBeaconNode;
use super::DirectBeaconNode;
use block_producer::{
BeaconNode as BeaconBlockNode, BeaconNodeError as BeaconBlockNodeError, PublishOutcome,
};
@ -6,7 +6,7 @@ use db::ClientDB;
use slot_clock::SlotClock;
use types::{BeaconBlock, PublicKey, Signature};
impl<T: ClientDB, U: SlotClock> BeaconBlockNode for BenchingBeaconNode<T, U> {
impl<T: ClientDB, U: SlotClock> BeaconBlockNode for DirectBeaconNode<T, U> {
/// Requests the `proposer_nonce` from the `BeaconChain`.
fn proposer_nonce(&self, pubkey: &PublicKey) -> Result<u64, BeaconBlockNodeError> {
let validator_index = self

View File

@ -10,6 +10,8 @@ use slot_clock::SlotClock;
use std::sync::Arc;
use types::PublicKey;
/// Connects directly to a borrowed `BeaconChain` and reads attester/proposer duties directly from
/// it.
pub struct DirectDuties<T: ClientDB, U: SlotClock> {
beacon_chain: Arc<BeaconChain<T, U>>,
pubkey: PublicKey,

View File

@ -10,7 +10,7 @@ mod beacon_node;
mod direct_duties;
mod signer;
pub use self::beacon_node::BenchingBeaconNode;
pub use self::beacon_node::DirectBeaconNode;
pub use self::direct_duties::DirectDuties;
pub use attester::PollOutcome as AttestationPollOutcome;
pub use block_producer::PollOutcome as BlockPollOutcome;
@ -27,28 +27,37 @@ pub enum AttestationProduceError {
PollError(AttestationPollError),
}
/// A `BlockProducer` and `Attester` which sign using a common keypair.
///
/// The test validator connects directly to a borrowed `BeaconChain` struct. It is useful for
/// testing that the core proposer and attester logic is functioning. Also for supporting beacon
/// chain tests.
pub struct TestValidator {
pub block_producer: BlockProducer<
TestingSlotClock,
BenchingBeaconNode<MemoryDB, TestingSlotClock>,
DirectBeaconNode<MemoryDB, TestingSlotClock>,
DirectDuties<MemoryDB, TestingSlotClock>,
TestSigner,
>,
pub attester: Attester<
TestingSlotClock,
BenchingBeaconNode<MemoryDB, TestingSlotClock>,
DirectBeaconNode<MemoryDB, TestingSlotClock>,
DirectDuties<MemoryDB, TestingSlotClock>,
TestSigner,
>,
pub spec: Arc<ChainSpec>,
pub epoch_map: Arc<DirectDuties<MemoryDB, TestingSlotClock>>,
pub keypair: Keypair,
pub beacon_node: Arc<BenchingBeaconNode<MemoryDB, TestingSlotClock>>,
pub beacon_node: Arc<DirectBeaconNode<MemoryDB, TestingSlotClock>>,
pub slot_clock: Arc<TestingSlotClock>,
pub signer: Arc<TestSigner>,
}
impl TestValidator {
/// Create a new TestValidator that signs with the given keypair, operates per the given spec and connects to the
/// supplied beacon node.
///
/// A `BlockProducer` and `Attester` is created..
pub fn new(
keypair: Keypair,
beacon_chain: Arc<BeaconChain<MemoryDB, TestingSlotClock>>,
@ -56,7 +65,7 @@ impl TestValidator {
) -> Self {
let slot_clock = Arc::new(TestingSlotClock::new(spec.genesis_slot));
let signer = Arc::new(TestSigner::new(keypair.clone()));
let beacon_node = Arc::new(BenchingBeaconNode::new(beacon_chain.clone()));
let beacon_node = Arc::new(DirectBeaconNode::new(beacon_chain.clone()));
let epoch_map = Arc::new(DirectDuties::new(keypair.pk.clone(), beacon_chain.clone()));
let block_producer = BlockProducer::new(
@ -87,8 +96,11 @@ impl TestValidator {
}
}
/// Run the `poll` function on the `BlockProducer` and produce a block.
///
/// An error is returned if the producer refuses to produce.
pub fn produce_block(&mut self) -> Result<BeaconBlock, BlockProduceError> {
// Using `BenchingBeaconNode`, the validator will always return sucessufully if it tries to
// Using `DirectBeaconNode`, the validator will always return sucessufully if it tries to
// publish a block.
match self.block_producer.poll() {
Ok(BlockPollOutcome::BlockProduced(_)) => {}
@ -101,6 +113,9 @@ impl TestValidator {
.expect("Unable to obtain produced block."))
}
/// Run the `poll` function on the `Attester` and produce a `FreeAttestation`.
///
/// An error is returned if the attester refuses to attest.
pub fn produce_free_attestation(&mut self) -> Result<FreeAttestation, AttestationProduceError> {
match self.attester.poll() {
Ok(AttestationPollOutcome::AttestationProduced(_)) => {}
@ -113,6 +128,9 @@ impl TestValidator {
.expect("Unable to obtain produced attestation."))
}
/// Set the validators slot clock to the specified slot.
///
/// The validators slot clock will always read this value until it is set to something else.
pub fn set_slot(&mut self, slot: u64) {
self.slot_clock.set_slot(slot)
}

View File

@ -3,7 +3,7 @@ use block_producer::Signer as BlockProposerSigner;
use std::sync::RwLock;
use types::{Keypair, Signature};
/// A test-only struct used to simulate a Beacon Node.
/// A test-only struct used to perform signing for a proposer or attester.
pub struct TestSigner {
keypair: Keypair,
should_sign: RwLock<bool>,
@ -24,6 +24,7 @@ impl TestSigner {
*self.should_sign.write().unwrap() = enabled;
}
/// Sign some message.
fn bls_sign(&self, message: &[u8]) -> Option<Signature> {
Some(Signature::new(message, &self.keypair.sk))
}