diff --git a/beacon_node/beacon_chain/test_harness/src/harness.rs b/beacon_node/beacon_chain/test_harness/src/harness.rs index f55fb7666..7bc86a7e1 100644 --- a/beacon_node/beacon_chain/test_harness/src/harness.rs +++ b/beacon_node/beacon_chain/test_harness/src/harness.rs @@ -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, pub beacon_chain: Arc>, @@ -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, 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) { let json = serde_json::to_string(chain_dump).unwrap(); let mut file = File::create(filename).unwrap(); diff --git a/beacon_node/beacon_chain/test_harness/src/validator/beacon_node/attester.rs b/beacon_node/beacon_chain/test_harness/src/validator/beacon_node/attester.rs index 19225b671..e7198461e 100644 --- a/beacon_node/beacon_chain/test_harness/src/validator/beacon_node/attester.rs +++ b/beacon_node/beacon_chain/test_harness/src/validator/beacon_node/attester.rs @@ -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 AttesterBeaconNode for BenchingBeaconNode { +impl AttesterBeaconNode for DirectBeaconNode { fn produce_attestation_data( &self, _slot: u64, diff --git a/beacon_node/beacon_chain/test_harness/src/validator/beacon_node/mod.rs b/beacon_node/beacon_chain/test_harness/src/validator/beacon_node/mod.rs index caed2bc5f..90f8943fc 100644 --- a/beacon_node/beacon_chain/test_harness/src/validator/beacon_node/mod.rs +++ b/beacon_node/beacon_chain/test_harness/src/validator/beacon_node/mod.rs @@ -8,13 +8,19 @@ use types::{BeaconBlock, FreeAttestation}; mod attester; mod producer; -pub struct BenchingBeaconNode { +/// 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 { beacon_chain: Arc>, published_blocks: RwLock>, published_attestations: RwLock>, } -impl BenchingBeaconNode { +impl DirectBeaconNode { pub fn new(beacon_chain: Arc>) -> Self { Self { beacon_chain, @@ -23,10 +29,12 @@ impl BenchingBeaconNode { } } + /// Get the last published block (if any). pub fn last_published_block(&self) -> Option { Some(self.published_blocks.read().last()?.clone()) } + /// Get the last published attestation (if any). pub fn last_published_free_attestation(&self) -> Option { Some(self.published_attestations.read().last()?.clone()) } diff --git a/beacon_node/beacon_chain/test_harness/src/validator/beacon_node/producer.rs b/beacon_node/beacon_chain/test_harness/src/validator/beacon_node/producer.rs index 861acd25e..36c82f6cc 100644 --- a/beacon_node/beacon_chain/test_harness/src/validator/beacon_node/producer.rs +++ b/beacon_node/beacon_chain/test_harness/src/validator/beacon_node/producer.rs @@ -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 BeaconBlockNode for BenchingBeaconNode { +impl BeaconBlockNode for DirectBeaconNode { /// Requests the `proposer_nonce` from the `BeaconChain`. fn proposer_nonce(&self, pubkey: &PublicKey) -> Result { let validator_index = self diff --git a/beacon_node/beacon_chain/test_harness/src/validator/direct_duties.rs b/beacon_node/beacon_chain/test_harness/src/validator/direct_duties.rs index 9eda378a0..e724b3e55 100644 --- a/beacon_node/beacon_chain/test_harness/src/validator/direct_duties.rs +++ b/beacon_node/beacon_chain/test_harness/src/validator/direct_duties.rs @@ -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 { beacon_chain: Arc>, pubkey: PublicKey, diff --git a/beacon_node/beacon_chain/test_harness/src/validator/mod.rs b/beacon_node/beacon_chain/test_harness/src/validator/mod.rs index 018ffbc86..4eae0bb3a 100644 --- a/beacon_node/beacon_chain/test_harness/src/validator/mod.rs +++ b/beacon_node/beacon_chain/test_harness/src/validator/mod.rs @@ -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, + DirectBeaconNode, DirectDuties, TestSigner, >, pub attester: Attester< TestingSlotClock, - BenchingBeaconNode, + DirectBeaconNode, DirectDuties, TestSigner, >, pub spec: Arc, pub epoch_map: Arc>, pub keypair: Keypair, - pub beacon_node: Arc>, + pub beacon_node: Arc>, pub slot_clock: Arc, pub signer: Arc, } 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>, @@ -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 { - // 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 { 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) } diff --git a/beacon_node/beacon_chain/test_harness/src/validator/signer.rs b/beacon_node/beacon_chain/test_harness/src/validator/signer.rs index 572a870ff..6e63c7f99 100644 --- a/beacon_node/beacon_chain/test_harness/src/validator/signer.rs +++ b/beacon_node/beacon_chain/test_harness/src/validator/signer.rs @@ -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, @@ -24,6 +24,7 @@ impl TestSigner { *self.should_sign.write().unwrap() = enabled; } + /// Sign some message. fn bls_sign(&self, message: &[u8]) -> Option { Some(Signature::new(message, &self.keypair.sk)) }