Tidy and add docstring to chain test harness.
This commit is contained in:
parent
4cc88c8cc7
commit
cb85fbcdb2
@ -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();
|
||||
|
@ -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,
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user