mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-03 00:31:50 +00:00
Altair consensus changes and refactors (#2279)
## Proposed Changes Implement the consensus changes necessary for the upcoming Altair hard fork. ## Additional Info This is quite a heavy refactor, with pivotal types like the `BeaconState` and `BeaconBlock` changing from structs to enums. This ripples through the whole codebase with field accesses changing to methods, e.g. `state.slot` => `state.slot()`. Co-authored-by: realbigsean <seananderson33@gmail.com>
This commit is contained in:
@@ -10,3 +10,5 @@ edition = "2018"
|
||||
state_processing = { path = "../../consensus/state_processing" }
|
||||
types = { path = "../../consensus/types" }
|
||||
eth2_ssz = "0.1.2"
|
||||
beacon_chain = { path = "../../beacon_node/beacon_chain" }
|
||||
lazy_static = "1.4.0"
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use super::*;
|
||||
use beacon_chain::test_utils::{BeaconChainHarness, EphemeralHarnessType};
|
||||
use state_processing::{
|
||||
per_block_processing, per_block_processing::errors::ExitInvalid,
|
||||
test_utils::BlockProcessingBuilder, BlockProcessingError, BlockSignatureStrategy,
|
||||
per_block_processing, per_block_processing::errors::ExitInvalid, BlockProcessingError,
|
||||
BlockSignatureStrategy,
|
||||
};
|
||||
use types::{BeaconBlock, BeaconState, Epoch, EthSpec, SignedBeaconBlock};
|
||||
|
||||
@@ -14,8 +15,10 @@ struct ExitTest {
|
||||
validator_index: u64,
|
||||
exit_epoch: Epoch,
|
||||
state_epoch: Epoch,
|
||||
block_modifier: Box<dyn FnOnce(&mut BeaconBlock<E>)>,
|
||||
builder_modifier: Box<dyn FnOnce(BlockProcessingBuilder<E>) -> BlockProcessingBuilder<E>>,
|
||||
state_modifier: Box<dyn FnOnce(&mut BeaconState<E>)>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
block_modifier:
|
||||
Box<dyn FnOnce(&BeaconChainHarness<EphemeralHarnessType<E>>, &mut BeaconBlock<E>)>,
|
||||
#[allow(dead_code)]
|
||||
expected: Result<(), BlockProcessingError>,
|
||||
}
|
||||
@@ -26,8 +29,8 @@ impl Default for ExitTest {
|
||||
validator_index: VALIDATOR_INDEX,
|
||||
exit_epoch: STATE_EPOCH,
|
||||
state_epoch: STATE_EPOCH,
|
||||
block_modifier: Box::new(|_| ()),
|
||||
builder_modifier: Box::new(|x| x),
|
||||
state_modifier: Box::new(|_| ()),
|
||||
block_modifier: Box::new(|_, _| ()),
|
||||
expected: Ok(()),
|
||||
}
|
||||
}
|
||||
@@ -35,14 +38,23 @@ impl Default for ExitTest {
|
||||
|
||||
impl ExitTest {
|
||||
fn block_and_pre_state(self) -> (SignedBeaconBlock<E>, BeaconState<E>) {
|
||||
let spec = &E::default_spec();
|
||||
let harness = get_harness::<E>(
|
||||
self.state_epoch.start_slot(E::slots_per_epoch()),
|
||||
VALIDATOR_COUNT,
|
||||
);
|
||||
let mut state = harness.get_current_state();
|
||||
(self.state_modifier)(&mut state);
|
||||
|
||||
(self.builder_modifier)(
|
||||
get_builder(spec, self.state_epoch.as_u64(), VALIDATOR_COUNT)
|
||||
.insert_exit(self.validator_index, self.exit_epoch)
|
||||
.modify(self.block_modifier),
|
||||
)
|
||||
.build(None, None)
|
||||
let block_modifier = self.block_modifier;
|
||||
let validator_index = self.validator_index;
|
||||
let exit_epoch = self.exit_epoch;
|
||||
|
||||
let (signed_block, state) =
|
||||
harness.make_block_with_modifier(state.clone(), state.slot() + 1, |block| {
|
||||
harness.add_voluntary_exit(block, validator_index, exit_epoch);
|
||||
block_modifier(&harness, block);
|
||||
});
|
||||
(signed_block, state)
|
||||
}
|
||||
|
||||
fn process(
|
||||
@@ -58,7 +70,7 @@ impl ExitTest {
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(all(test, not(debug_assertions)))]
|
||||
fn run(self) -> BeaconState<E> {
|
||||
let spec = &E::default_spec();
|
||||
let expected = self.expected.clone();
|
||||
@@ -95,23 +107,22 @@ vectors_and_tests!(
|
||||
// Ensures we can process a valid exit,
|
||||
valid_single_exit,
|
||||
ExitTest::default(),
|
||||
// Tests three exists in the same block.
|
||||
// Tests three exits in the same block.
|
||||
valid_three_exits,
|
||||
ExitTest {
|
||||
builder_modifier: Box::new(|builder| {
|
||||
builder
|
||||
.insert_exit(1, STATE_EPOCH)
|
||||
.insert_exit(2, STATE_EPOCH)
|
||||
block_modifier: Box::new(|harness, block| {
|
||||
harness.add_voluntary_exit(block, 1, STATE_EPOCH);
|
||||
harness.add_voluntary_exit(block, 2, STATE_EPOCH);
|
||||
}),
|
||||
..ExitTest::default()
|
||||
},
|
||||
// Ensures that a validator cannot be exited twice in the same block.
|
||||
invalid_duplicate,
|
||||
ExitTest {
|
||||
block_modifier: Box::new(|block| {
|
||||
block_modifier: Box::new(|_, block| {
|
||||
// Duplicate the exit
|
||||
let exit = block.body.voluntary_exits[0].clone();
|
||||
block.body.voluntary_exits.push(exit).unwrap();
|
||||
let exit = block.body().voluntary_exits()[0].clone();
|
||||
block.body_mut().voluntary_exits_mut().push(exit).unwrap();
|
||||
}),
|
||||
expected: Err(BlockProcessingError::ExitInvalid {
|
||||
index: 1,
|
||||
@@ -128,8 +139,10 @@ vectors_and_tests!(
|
||||
// ```
|
||||
invalid_validator_unknown,
|
||||
ExitTest {
|
||||
block_modifier: Box::new(|block| {
|
||||
block.body.voluntary_exits[0].message.validator_index = VALIDATOR_COUNT as u64;
|
||||
block_modifier: Box::new(|_, block| {
|
||||
block.body_mut().voluntary_exits_mut()[0]
|
||||
.message
|
||||
.validator_index = VALIDATOR_COUNT as u64;
|
||||
}),
|
||||
expected: Err(BlockProcessingError::ExitInvalid {
|
||||
index: 0,
|
||||
@@ -147,9 +160,8 @@ vectors_and_tests!(
|
||||
// ```
|
||||
invalid_exit_already_initiated,
|
||||
ExitTest {
|
||||
builder_modifier: Box::new(|mut builder| {
|
||||
builder.state.validators[0].exit_epoch = STATE_EPOCH + 1;
|
||||
builder
|
||||
state_modifier: Box::new(|state| {
|
||||
state.validators_mut()[0].exit_epoch = STATE_EPOCH + 1;
|
||||
}),
|
||||
expected: Err(BlockProcessingError::ExitInvalid {
|
||||
index: 0,
|
||||
@@ -167,9 +179,8 @@ vectors_and_tests!(
|
||||
// ```
|
||||
invalid_not_active_before_activation_epoch,
|
||||
ExitTest {
|
||||
builder_modifier: Box::new(|mut builder| {
|
||||
builder.state.validators[0].activation_epoch = builder.spec.far_future_epoch;
|
||||
builder
|
||||
state_modifier: Box::new(|state| {
|
||||
state.validators_mut()[0].activation_epoch = E::default_spec().far_future_epoch;
|
||||
}),
|
||||
expected: Err(BlockProcessingError::ExitInvalid {
|
||||
index: 0,
|
||||
@@ -187,9 +198,8 @@ vectors_and_tests!(
|
||||
// ```
|
||||
invalid_not_active_after_exit_epoch,
|
||||
ExitTest {
|
||||
builder_modifier: Box::new(|mut builder| {
|
||||
builder.state.validators[0].exit_epoch = STATE_EPOCH;
|
||||
builder
|
||||
state_modifier: Box::new(|state| {
|
||||
state.validators_mut()[0].exit_epoch = STATE_EPOCH;
|
||||
}),
|
||||
expected: Err(BlockProcessingError::ExitInvalid {
|
||||
index: 0,
|
||||
@@ -286,10 +296,12 @@ vectors_and_tests!(
|
||||
// ```
|
||||
invalid_bad_signature,
|
||||
ExitTest {
|
||||
block_modifier: Box::new(|block| {
|
||||
block_modifier: Box::new(|_, block| {
|
||||
// Shift the validator index by 1 so that it's mismatched from the key that was
|
||||
// used to sign.
|
||||
block.body.voluntary_exits[0].message.validator_index = VALIDATOR_INDEX + 1;
|
||||
block.body_mut().voluntary_exits_mut()[0]
|
||||
.message
|
||||
.validator_index = VALIDATOR_INDEX + 1;
|
||||
}),
|
||||
expected: Err(BlockProcessingError::ExitInvalid {
|
||||
index: 0,
|
||||
@@ -299,14 +311,14 @@ vectors_and_tests!(
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(all(test, not(debug_assertions)))]
|
||||
mod custom_tests {
|
||||
use super::*;
|
||||
|
||||
fn assert_exited(state: &BeaconState<E>, validator_index: usize) {
|
||||
let spec = E::default_spec();
|
||||
|
||||
let validator = &state.validators[validator_index];
|
||||
let validator = &state.validators()[validator_index];
|
||||
assert_eq!(
|
||||
validator.exit_epoch,
|
||||
// This is correct until we exceed the churn limit. If that happens, we
|
||||
@@ -330,10 +342,9 @@ mod custom_tests {
|
||||
#[test]
|
||||
fn valid_three() {
|
||||
let state = ExitTest {
|
||||
builder_modifier: Box::new(|builder| {
|
||||
builder
|
||||
.insert_exit(1, STATE_EPOCH)
|
||||
.insert_exit(2, STATE_EPOCH)
|
||||
block_modifier: Box::new(|harness, block| {
|
||||
harness.add_voluntary_exit(block, 1, STATE_EPOCH);
|
||||
harness.add_voluntary_exit(block, 2, STATE_EPOCH);
|
||||
}),
|
||||
..ExitTest::default()
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ macro_rules! vectors_and_tests {
|
||||
vec
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(all(test, not(debug_assertions)))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
$(
|
||||
|
||||
@@ -2,15 +2,21 @@
|
||||
mod macros;
|
||||
mod exit;
|
||||
|
||||
use beacon_chain::{
|
||||
store::StoreConfig,
|
||||
test_utils::{BeaconChainHarness, EphemeralHarnessType},
|
||||
};
|
||||
use lazy_static::lazy_static;
|
||||
use ssz::Encode;
|
||||
use state_processing::test_utils::BlockProcessingBuilder;
|
||||
use std::env;
|
||||
use std::fs::{self, File};
|
||||
use std::io::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::exit;
|
||||
use types::MainnetEthSpec;
|
||||
use types::{BeaconState, ChainSpec, EthSpec, SignedBeaconBlock};
|
||||
use types::{
|
||||
test_utils::generate_deterministic_keypairs, BeaconState, EthSpec, Keypair, SignedBeaconBlock,
|
||||
};
|
||||
use types::{Hash256, MainnetEthSpec, Slot};
|
||||
|
||||
type E = MainnetEthSpec;
|
||||
|
||||
@@ -19,6 +25,8 @@ pub const VALIDATOR_COUNT: usize = 64;
|
||||
/// The base output directory for test vectors.
|
||||
pub const BASE_VECTOR_DIR: &str = "vectors";
|
||||
|
||||
pub const SLOT_OFFSET: u64 = 1;
|
||||
|
||||
/// Writes all known test vectors to `CARGO_MANIFEST_DIR/vectors`.
|
||||
fn main() {
|
||||
match write_all_vectors() {
|
||||
@@ -39,16 +47,36 @@ pub struct TestVector {
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
/// Gets a `BlockProcessingBuilder` to be used in testing.
|
||||
fn get_builder(
|
||||
spec: &ChainSpec,
|
||||
epoch_offset: u64,
|
||||
num_validators: usize,
|
||||
) -> BlockProcessingBuilder<MainnetEthSpec> {
|
||||
// Set the state and block to be in the last slot of the `epoch_offset`th epoch.
|
||||
let last_slot_of_epoch = (MainnetEthSpec::genesis_epoch() + epoch_offset)
|
||||
.end_slot(MainnetEthSpec::slots_per_epoch());
|
||||
BlockProcessingBuilder::new(num_validators, last_slot_of_epoch, &spec).build_caches()
|
||||
lazy_static! {
|
||||
/// A cached set of keys.
|
||||
static ref KEYPAIRS: Vec<Keypair> = generate_deterministic_keypairs(VALIDATOR_COUNT);
|
||||
}
|
||||
|
||||
fn get_harness<E: EthSpec>(
|
||||
slot: Slot,
|
||||
validator_count: usize,
|
||||
) -> BeaconChainHarness<EphemeralHarnessType<E>> {
|
||||
let harness = BeaconChainHarness::new_with_store_config(
|
||||
E::default(),
|
||||
None,
|
||||
KEYPAIRS[0..validator_count].to_vec(),
|
||||
StoreConfig::default(),
|
||||
);
|
||||
let skip_to_slot = slot - SLOT_OFFSET;
|
||||
if skip_to_slot > Slot::new(0) {
|
||||
let state = harness.get_current_state();
|
||||
harness.add_attested_blocks_at_slots(
|
||||
state,
|
||||
Hash256::zero(),
|
||||
(skip_to_slot.as_u64()..slot.as_u64())
|
||||
.map(Slot::new)
|
||||
.collect::<Vec<_>>()
|
||||
.as_slice(),
|
||||
(0..validator_count).collect::<Vec<_>>().as_slice(),
|
||||
);
|
||||
}
|
||||
|
||||
harness
|
||||
}
|
||||
|
||||
/// Writes all vectors to file.
|
||||
|
||||
Reference in New Issue
Block a user