Allow harness to produce fork blocks
This commit is contained in:
parent
e904e0a5a8
commit
8ed03e391d
@ -22,7 +22,12 @@ pub enum BlockStrategy {
|
|||||||
/// Ignore the canonical head and produce blocks upon the block at the given slot.
|
/// Ignore the canonical head and produce blocks upon the block at the given slot.
|
||||||
///
|
///
|
||||||
/// Useful for simulating forks.
|
/// Useful for simulating forks.
|
||||||
ForkCanonicalChainAt(Slot),
|
ForkCanonicalChainAt {
|
||||||
|
/// The slot of the parent of the first block produced.
|
||||||
|
previous_slot: Slot,
|
||||||
|
/// The slot of the first block produced (must be higher than `previous_slot`.
|
||||||
|
first_slot: Slot,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Indicates how the `BeaconChainHarness` should produce attestations.
|
/// Indicates how the `BeaconChainHarness` should produce attestations.
|
||||||
@ -62,7 +67,7 @@ where
|
|||||||
L: LmdGhost<MemoryStore, E>,
|
L: LmdGhost<MemoryStore, E>,
|
||||||
E: EthSpec,
|
E: EthSpec,
|
||||||
{
|
{
|
||||||
chain: BeaconChain<CommonTypes<L, E>>,
|
pub chain: BeaconChain<CommonTypes<L, E>>,
|
||||||
keypairs: Vec<Keypair>,
|
keypairs: Vec<Keypair>,
|
||||||
spec: ChainSpec,
|
spec: ChainSpec,
|
||||||
}
|
}
|
||||||
@ -130,29 +135,20 @@ where
|
|||||||
block_strategy: BlockStrategy,
|
block_strategy: BlockStrategy,
|
||||||
attestation_strategy: AttestationStrategy,
|
attestation_strategy: AttestationStrategy,
|
||||||
) {
|
) {
|
||||||
// Get an initial state to build the block upon, based on the build strategy.
|
let mut state = {
|
||||||
let mut state = match block_strategy {
|
// Determine the slot for the first block (or skipped block).
|
||||||
BlockStrategy::OnCanonicalHead => self.chain.current_state().clone(),
|
let state_slot = match block_strategy {
|
||||||
BlockStrategy::ForkCanonicalChainAt(fork_slot) => {
|
BlockStrategy::OnCanonicalHead => self.chain.read_slot_clock().unwrap() - 1,
|
||||||
let state_root = self
|
BlockStrategy::ForkCanonicalChainAt { previous_slot, .. } => previous_slot,
|
||||||
.chain
|
};
|
||||||
.rev_iter_state_roots(self.chain.head().beacon_state.slot - 1)
|
|
||||||
.find(|(_hash, slot)| *slot == fork_slot)
|
|
||||||
.map(|(hash, _slot)| hash)
|
|
||||||
.expect("could not find state root for fork");
|
|
||||||
|
|
||||||
self.chain
|
self.get_state_at_slot(state_slot)
|
||||||
.store
|
|
||||||
.get(&state_root)
|
|
||||||
.expect("should read db")
|
|
||||||
.expect("should find state root")
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get an initial slot to build upon, based on the build strategy.
|
// Determine the first slot where a block should be built.
|
||||||
let mut slot = match block_strategy {
|
let mut slot = match block_strategy {
|
||||||
BlockStrategy::OnCanonicalHead => self.chain.read_slot_clock().unwrap(),
|
BlockStrategy::OnCanonicalHead => self.chain.read_slot_clock().unwrap(),
|
||||||
BlockStrategy::ForkCanonicalChainAt(slot) => slot,
|
BlockStrategy::ForkCanonicalChainAt { first_slot, .. } => first_slot,
|
||||||
};
|
};
|
||||||
|
|
||||||
for _ in 0..num_blocks {
|
for _ in 0..num_blocks {
|
||||||
@ -175,7 +171,7 @@ where
|
|||||||
slot,
|
slot,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
panic!("block should be successfully processed");
|
panic!("block should be successfully processed: {:?}", outcome);
|
||||||
}
|
}
|
||||||
|
|
||||||
state = new_state;
|
state = new_state;
|
||||||
@ -183,6 +179,21 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_state_at_slot(&self, state_slot: Slot) -> BeaconState<E> {
|
||||||
|
let state_root = self
|
||||||
|
.chain
|
||||||
|
.rev_iter_state_roots(self.chain.current_state().slot)
|
||||||
|
.find(|(_hash, slot)| *slot == state_slot)
|
||||||
|
.map(|(hash, _slot)| hash)
|
||||||
|
.expect("could not find state root");
|
||||||
|
|
||||||
|
self.chain
|
||||||
|
.store
|
||||||
|
.get(&state_root)
|
||||||
|
.expect("should read db")
|
||||||
|
.expect("should find state root")
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a newly created block, signed by the proposer for the given slot.
|
/// Returns a newly created block, signed by the proposer for the given slot.
|
||||||
fn build_block(
|
fn build_block(
|
||||||
&self,
|
&self,
|
||||||
@ -322,174 +333,3 @@ where
|
|||||||
&self.keypairs[validator_index].sk
|
&self.keypairs[validator_index].sk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
// #[cfg(not(debug_assertions))]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
use lmd_ghost::ThreadSafeReducedTree;
|
|
||||||
use types::MinimalEthSpec;
|
|
||||||
|
|
||||||
// Should ideally be divisible by 3.
|
|
||||||
pub const VALIDATOR_COUNT: usize = 24;
|
|
||||||
|
|
||||||
fn get_harness(
|
|
||||||
validator_count: usize,
|
|
||||||
) -> BeaconChainHarness<ThreadSafeReducedTree<MemoryStore, MinimalEthSpec>, MinimalEthSpec>
|
|
||||||
{
|
|
||||||
let harness = BeaconChainHarness::new(validator_count);
|
|
||||||
|
|
||||||
// Move past the zero slot.
|
|
||||||
harness.advance_slot();
|
|
||||||
|
|
||||||
harness
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn finalizes_with_full_participation() {
|
|
||||||
let num_blocks_produced = MinimalEthSpec::slots_per_epoch() * 5;
|
|
||||||
|
|
||||||
let harness = get_harness(VALIDATOR_COUNT);
|
|
||||||
|
|
||||||
harness.extend_chain(
|
|
||||||
num_blocks_produced as usize,
|
|
||||||
BlockStrategy::OnCanonicalHead,
|
|
||||||
AttestationStrategy::AllValidators,
|
|
||||||
);
|
|
||||||
|
|
||||||
let state = &harness.chain.head().beacon_state;
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
state.slot, num_blocks_produced,
|
|
||||||
"head should be at the current slot"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
state.current_epoch(),
|
|
||||||
num_blocks_produced / MinimalEthSpec::slots_per_epoch(),
|
|
||||||
"head should be at the expected epoch"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
state.current_justified_epoch,
|
|
||||||
state.current_epoch() - 1,
|
|
||||||
"the head should be justified one behind the current epoch"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
state.finalized_epoch,
|
|
||||||
state.current_epoch() - 2,
|
|
||||||
"the head should be finalized two behind the current epoch"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn finalizes_with_two_thirds_participation() {
|
|
||||||
let num_blocks_produced = MinimalEthSpec::slots_per_epoch() * 5;
|
|
||||||
|
|
||||||
let harness = get_harness(VALIDATOR_COUNT);
|
|
||||||
|
|
||||||
let two_thirds = (VALIDATOR_COUNT / 3) * 2;
|
|
||||||
let attesters = (0..two_thirds).collect();
|
|
||||||
|
|
||||||
harness.extend_chain(
|
|
||||||
num_blocks_produced as usize,
|
|
||||||
BlockStrategy::OnCanonicalHead,
|
|
||||||
AttestationStrategy::SomeValidators(attesters),
|
|
||||||
);
|
|
||||||
|
|
||||||
let state = &harness.chain.head().beacon_state;
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
state.slot, num_blocks_produced,
|
|
||||||
"head should be at the current slot"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
state.current_epoch(),
|
|
||||||
num_blocks_produced / MinimalEthSpec::slots_per_epoch(),
|
|
||||||
"head should be at the expected epoch"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Note: the 2/3rds tests are not justifying the immediately prior epochs because the
|
|
||||||
// `MIN_ATTESTATION_INCLUSION_DELAY` is preventing an adequate number of attestations being
|
|
||||||
// included in blocks during that epoch.
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
state.current_justified_epoch,
|
|
||||||
state.current_epoch() - 2,
|
|
||||||
"the head should be justified two behind the current epoch"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
state.finalized_epoch,
|
|
||||||
state.current_epoch() - 4,
|
|
||||||
"the head should be finalized three behind the current epoch"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn does_not_finalize_with_less_than_two_thirds_participation() {
|
|
||||||
let num_blocks_produced = MinimalEthSpec::slots_per_epoch() * 5;
|
|
||||||
|
|
||||||
let harness = get_harness(VALIDATOR_COUNT);
|
|
||||||
|
|
||||||
let two_thirds = (VALIDATOR_COUNT / 3) * 2;
|
|
||||||
let less_than_two_thirds = two_thirds - 1;
|
|
||||||
let attesters = (0..less_than_two_thirds).collect();
|
|
||||||
|
|
||||||
harness.extend_chain(
|
|
||||||
num_blocks_produced as usize,
|
|
||||||
BlockStrategy::OnCanonicalHead,
|
|
||||||
AttestationStrategy::SomeValidators(attesters),
|
|
||||||
);
|
|
||||||
|
|
||||||
let state = &harness.chain.head().beacon_state;
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
state.slot, num_blocks_produced,
|
|
||||||
"head should be at the current slot"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
state.current_epoch(),
|
|
||||||
num_blocks_produced / MinimalEthSpec::slots_per_epoch(),
|
|
||||||
"head should be at the expected epoch"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
state.current_justified_epoch, 0,
|
|
||||||
"no epoch should have been justified"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
state.finalized_epoch, 0,
|
|
||||||
"no epoch should have been finalized"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn does_not_finalize_without_attestation() {
|
|
||||||
let num_blocks_produced = MinimalEthSpec::slots_per_epoch() * 5;
|
|
||||||
|
|
||||||
let harness = get_harness(VALIDATOR_COUNT);
|
|
||||||
|
|
||||||
harness.extend_chain(
|
|
||||||
num_blocks_produced as usize,
|
|
||||||
BlockStrategy::OnCanonicalHead,
|
|
||||||
AttestationStrategy::SomeValidators(vec![]),
|
|
||||||
);
|
|
||||||
|
|
||||||
let state = &harness.chain.head().beacon_state;
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
state.slot, num_blocks_produced,
|
|
||||||
"head should be at the current slot"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
state.current_epoch(),
|
|
||||||
num_blocks_produced / MinimalEthSpec::slots_per_epoch(),
|
|
||||||
"head should be at the expected epoch"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
state.current_justified_epoch, 0,
|
|
||||||
"no epoch should have been justified"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
state.finalized_epoch, 0,
|
|
||||||
"no epoch should have been finalized"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user