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 std::sync::Arc;
|
||||||
use types::{BeaconBlock, ChainSpec, FreeAttestation, Keypair, Validator};
|
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 struct BeaconChainHarness {
|
||||||
pub db: Arc<MemoryDB>,
|
pub db: Arc<MemoryDB>,
|
||||||
pub beacon_chain: Arc<BeaconChain<MemoryDB, TestingSlotClock>>,
|
pub beacon_chain: Arc<BeaconChain<MemoryDB, TestingSlotClock>>,
|
||||||
@ -25,6 +31,10 @@ pub struct BeaconChainHarness {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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 {
|
pub fn new(mut spec: ChainSpec, validator_count: usize) -> Self {
|
||||||
let db = Arc::new(MemoryDB::open());
|
let db = Arc::new(MemoryDB::open());
|
||||||
let block_store = Arc::new(BeaconBlockStore::new(db.clone()));
|
let block_store = Arc::new(BeaconBlockStore::new(db.clone()));
|
||||||
@ -208,10 +218,12 @@ impl BeaconChainHarness {
|
|||||||
debug!("Free attestations processed.");
|
debug!("Free attestations processed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Dump all blocks and states from the canonical beacon chain.
|
||||||
pub fn chain_dump(&self) -> Result<Vec<CheckPoint>, DumpError> {
|
pub fn chain_dump(&self) -> Result<Vec<CheckPoint>, DumpError> {
|
||||||
self.beacon_chain.chain_dump()
|
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>) {
|
pub fn dump_to_file(&self, filename: String, chain_dump: &Vec<CheckPoint>) {
|
||||||
let json = serde_json::to_string(chain_dump).unwrap();
|
let json = serde_json::to_string(chain_dump).unwrap();
|
||||||
let mut file = File::create(filename).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 attester::{BeaconNode as AttesterBeaconNode, BeaconNodeError as NodeError, PublishOutcome};
|
||||||
use db::ClientDB;
|
use db::ClientDB;
|
||||||
use slot_clock::SlotClock;
|
use slot_clock::SlotClock;
|
||||||
use types::{AttestationData, FreeAttestation};
|
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(
|
fn produce_attestation_data(
|
||||||
&self,
|
&self,
|
||||||
_slot: u64,
|
_slot: u64,
|
||||||
|
@ -8,13 +8,19 @@ use types::{BeaconBlock, FreeAttestation};
|
|||||||
mod attester;
|
mod attester;
|
||||||
mod producer;
|
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>>,
|
beacon_chain: Arc<BeaconChain<T, U>>,
|
||||||
published_blocks: RwLock<Vec<BeaconBlock>>,
|
published_blocks: RwLock<Vec<BeaconBlock>>,
|
||||||
published_attestations: RwLock<Vec<FreeAttestation>>,
|
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 {
|
pub fn new(beacon_chain: Arc<BeaconChain<T, U>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
beacon_chain,
|
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> {
|
pub fn last_published_block(&self) -> Option<BeaconBlock> {
|
||||||
Some(self.published_blocks.read().last()?.clone())
|
Some(self.published_blocks.read().last()?.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the last published attestation (if any).
|
||||||
pub fn last_published_free_attestation(&self) -> Option<FreeAttestation> {
|
pub fn last_published_free_attestation(&self) -> Option<FreeAttestation> {
|
||||||
Some(self.published_attestations.read().last()?.clone())
|
Some(self.published_attestations.read().last()?.clone())
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use super::BenchingBeaconNode;
|
use super::DirectBeaconNode;
|
||||||
use block_producer::{
|
use block_producer::{
|
||||||
BeaconNode as BeaconBlockNode, BeaconNodeError as BeaconBlockNodeError, PublishOutcome,
|
BeaconNode as BeaconBlockNode, BeaconNodeError as BeaconBlockNodeError, PublishOutcome,
|
||||||
};
|
};
|
||||||
@ -6,7 +6,7 @@ use db::ClientDB;
|
|||||||
use slot_clock::SlotClock;
|
use slot_clock::SlotClock;
|
||||||
use types::{BeaconBlock, PublicKey, Signature};
|
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`.
|
/// Requests the `proposer_nonce` from the `BeaconChain`.
|
||||||
fn proposer_nonce(&self, pubkey: &PublicKey) -> Result<u64, BeaconBlockNodeError> {
|
fn proposer_nonce(&self, pubkey: &PublicKey) -> Result<u64, BeaconBlockNodeError> {
|
||||||
let validator_index = self
|
let validator_index = self
|
||||||
|
@ -10,6 +10,8 @@ use slot_clock::SlotClock;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use types::PublicKey;
|
use types::PublicKey;
|
||||||
|
|
||||||
|
/// Connects directly to a borrowed `BeaconChain` and reads attester/proposer duties directly from
|
||||||
|
/// it.
|
||||||
pub struct DirectDuties<T: ClientDB, U: SlotClock> {
|
pub struct DirectDuties<T: ClientDB, U: SlotClock> {
|
||||||
beacon_chain: Arc<BeaconChain<T, U>>,
|
beacon_chain: Arc<BeaconChain<T, U>>,
|
||||||
pubkey: PublicKey,
|
pubkey: PublicKey,
|
||||||
|
@ -10,7 +10,7 @@ mod beacon_node;
|
|||||||
mod direct_duties;
|
mod direct_duties;
|
||||||
mod signer;
|
mod signer;
|
||||||
|
|
||||||
pub use self::beacon_node::BenchingBeaconNode;
|
pub use self::beacon_node::DirectBeaconNode;
|
||||||
pub use self::direct_duties::DirectDuties;
|
pub use self::direct_duties::DirectDuties;
|
||||||
pub use attester::PollOutcome as AttestationPollOutcome;
|
pub use attester::PollOutcome as AttestationPollOutcome;
|
||||||
pub use block_producer::PollOutcome as BlockPollOutcome;
|
pub use block_producer::PollOutcome as BlockPollOutcome;
|
||||||
@ -27,28 +27,37 @@ pub enum AttestationProduceError {
|
|||||||
PollError(AttestationPollError),
|
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 struct TestValidator {
|
||||||
pub block_producer: BlockProducer<
|
pub block_producer: BlockProducer<
|
||||||
TestingSlotClock,
|
TestingSlotClock,
|
||||||
BenchingBeaconNode<MemoryDB, TestingSlotClock>,
|
DirectBeaconNode<MemoryDB, TestingSlotClock>,
|
||||||
DirectDuties<MemoryDB, TestingSlotClock>,
|
DirectDuties<MemoryDB, TestingSlotClock>,
|
||||||
TestSigner,
|
TestSigner,
|
||||||
>,
|
>,
|
||||||
pub attester: Attester<
|
pub attester: Attester<
|
||||||
TestingSlotClock,
|
TestingSlotClock,
|
||||||
BenchingBeaconNode<MemoryDB, TestingSlotClock>,
|
DirectBeaconNode<MemoryDB, TestingSlotClock>,
|
||||||
DirectDuties<MemoryDB, TestingSlotClock>,
|
DirectDuties<MemoryDB, TestingSlotClock>,
|
||||||
TestSigner,
|
TestSigner,
|
||||||
>,
|
>,
|
||||||
pub spec: Arc<ChainSpec>,
|
pub spec: Arc<ChainSpec>,
|
||||||
pub epoch_map: Arc<DirectDuties<MemoryDB, TestingSlotClock>>,
|
pub epoch_map: Arc<DirectDuties<MemoryDB, TestingSlotClock>>,
|
||||||
pub keypair: Keypair,
|
pub keypair: Keypair,
|
||||||
pub beacon_node: Arc<BenchingBeaconNode<MemoryDB, TestingSlotClock>>,
|
pub beacon_node: Arc<DirectBeaconNode<MemoryDB, TestingSlotClock>>,
|
||||||
pub slot_clock: Arc<TestingSlotClock>,
|
pub slot_clock: Arc<TestingSlotClock>,
|
||||||
pub signer: Arc<TestSigner>,
|
pub signer: Arc<TestSigner>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestValidator {
|
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(
|
pub fn new(
|
||||||
keypair: Keypair,
|
keypair: Keypair,
|
||||||
beacon_chain: Arc<BeaconChain<MemoryDB, TestingSlotClock>>,
|
beacon_chain: Arc<BeaconChain<MemoryDB, TestingSlotClock>>,
|
||||||
@ -56,7 +65,7 @@ impl TestValidator {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
let slot_clock = Arc::new(TestingSlotClock::new(spec.genesis_slot));
|
let slot_clock = Arc::new(TestingSlotClock::new(spec.genesis_slot));
|
||||||
let signer = Arc::new(TestSigner::new(keypair.clone()));
|
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 epoch_map = Arc::new(DirectDuties::new(keypair.pk.clone(), beacon_chain.clone()));
|
||||||
|
|
||||||
let block_producer = BlockProducer::new(
|
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> {
|
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.
|
// publish a block.
|
||||||
match self.block_producer.poll() {
|
match self.block_producer.poll() {
|
||||||
Ok(BlockPollOutcome::BlockProduced(_)) => {}
|
Ok(BlockPollOutcome::BlockProduced(_)) => {}
|
||||||
@ -101,6 +113,9 @@ impl TestValidator {
|
|||||||
.expect("Unable to obtain produced block."))
|
.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> {
|
pub fn produce_free_attestation(&mut self) -> Result<FreeAttestation, AttestationProduceError> {
|
||||||
match self.attester.poll() {
|
match self.attester.poll() {
|
||||||
Ok(AttestationPollOutcome::AttestationProduced(_)) => {}
|
Ok(AttestationPollOutcome::AttestationProduced(_)) => {}
|
||||||
@ -113,6 +128,9 @@ impl TestValidator {
|
|||||||
.expect("Unable to obtain produced attestation."))
|
.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) {
|
pub fn set_slot(&mut self, slot: u64) {
|
||||||
self.slot_clock.set_slot(slot)
|
self.slot_clock.set_slot(slot)
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ use block_producer::Signer as BlockProposerSigner;
|
|||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
use types::{Keypair, Signature};
|
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 {
|
pub struct TestSigner {
|
||||||
keypair: Keypair,
|
keypair: Keypair,
|
||||||
should_sign: RwLock<bool>,
|
should_sign: RwLock<bool>,
|
||||||
@ -24,6 +24,7 @@ impl TestSigner {
|
|||||||
*self.should_sign.write().unwrap() = enabled;
|
*self.should_sign.write().unwrap() = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sign some message.
|
||||||
fn bls_sign(&self, message: &[u8]) -> Option<Signature> {
|
fn bls_sign(&self, message: &[u8]) -> Option<Signature> {
|
||||||
Some(Signature::new(message, &self.keypair.sk))
|
Some(Signature::new(message, &self.keypair.sk))
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user