mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-29 20:27:14 +00:00
Merge remote-tracking branch 'origin/unstable' into tree-states
This commit is contained in:
16
consensus/types/src/application_domain.rs
Normal file
16
consensus/types/src/application_domain.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
/// This value is an application index of 0 with the bitmask applied (so it's equivalent to the bit mask).
|
||||
/// Little endian hex: 0x00000001, Binary: 1000000000000000000000000
|
||||
pub const APPLICATION_DOMAIN_BUILDER: u32 = 16777216;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub enum ApplicationDomain {
|
||||
Builder,
|
||||
}
|
||||
|
||||
impl ApplicationDomain {
|
||||
pub fn get_domain_constant(&self) -> u32 {
|
||||
match self {
|
||||
ApplicationDomain::Builder => APPLICATION_DOMAIN_BUILDER,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ use tree_hash_derive::TreeHash;
|
||||
derive(Debug, PartialEq, TreeHash),
|
||||
tree_hash(enum_behaviour = "transparent")
|
||||
),
|
||||
map_ref_into(BeaconBlockBodyRef),
|
||||
map_ref_into(BeaconBlockBodyRef, BeaconBlock),
|
||||
map_ref_mut_into(BeaconBlockBodyRefMut)
|
||||
)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Encode, TreeHash, Derivative)]
|
||||
@@ -541,6 +541,50 @@ impl_from!(BeaconBlockBase, <E, FullPayload<E>>, <E, BlindedPayload<E>>, |body:
|
||||
impl_from!(BeaconBlockAltair, <E, FullPayload<E>>, <E, BlindedPayload<E>>, |body: BeaconBlockBodyAltair<_, _>| body.into());
|
||||
impl_from!(BeaconBlockMerge, <E, FullPayload<E>>, <E, BlindedPayload<E>>, |body: BeaconBlockBodyMerge<_, _>| body.into());
|
||||
|
||||
// We can clone blocks with payloads to blocks without payloads, without cloning the payload.
|
||||
macro_rules! impl_clone_as_blinded {
|
||||
($ty_name:ident, <$($from_params:ty),*>, <$($to_params:ty),*>) => {
|
||||
impl<E: EthSpec> $ty_name<$($from_params),*>
|
||||
{
|
||||
pub fn clone_as_blinded(&self) -> $ty_name<$($to_params),*> {
|
||||
let $ty_name {
|
||||
slot,
|
||||
proposer_index,
|
||||
parent_root,
|
||||
state_root,
|
||||
body,
|
||||
} = self;
|
||||
|
||||
$ty_name {
|
||||
slot: *slot,
|
||||
proposer_index: *proposer_index,
|
||||
parent_root: *parent_root,
|
||||
state_root: *state_root,
|
||||
body: body.clone_as_blinded(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_clone_as_blinded!(BeaconBlockBase, <E, FullPayload<E>>, <E, BlindedPayload<E>>);
|
||||
impl_clone_as_blinded!(BeaconBlockAltair, <E, FullPayload<E>>, <E, BlindedPayload<E>>);
|
||||
impl_clone_as_blinded!(BeaconBlockMerge, <E, FullPayload<E>>, <E, BlindedPayload<E>>);
|
||||
|
||||
// A reference to a full beacon block can be cloned into a blinded beacon block, without cloning the
|
||||
// execution payload.
|
||||
impl<'a, E: EthSpec> From<BeaconBlockRef<'a, E, FullPayload<E>>>
|
||||
for BeaconBlock<E, BlindedPayload<E>>
|
||||
{
|
||||
fn from(
|
||||
full_block: BeaconBlockRef<'a, E, FullPayload<E>>,
|
||||
) -> BeaconBlock<E, BlindedPayload<E>> {
|
||||
map_beacon_block_ref_into_beacon_block!(&'a _, full_block, |inner, cons| {
|
||||
cons(inner.clone_as_blinded())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> From<BeaconBlock<E, FullPayload<E>>>
|
||||
for (
|
||||
BeaconBlock<E, BlindedPayload<E>>,
|
||||
@@ -607,19 +651,17 @@ mod tests {
|
||||
#[test]
|
||||
fn decode_base_and_altair() {
|
||||
type E = MainnetEthSpec;
|
||||
let spec = E::default_spec();
|
||||
|
||||
let rng = &mut XorShiftRng::from_seed([42; 16]);
|
||||
|
||||
let fork_epoch = Epoch::from_ssz_bytes(&[7, 6, 5, 4, 3, 2, 1, 0]).unwrap();
|
||||
let fork_epoch = spec.altair_fork_epoch.unwrap();
|
||||
|
||||
let base_epoch = fork_epoch.saturating_sub(1_u64);
|
||||
let base_slot = base_epoch.end_slot(E::slots_per_epoch());
|
||||
let altair_epoch = fork_epoch;
|
||||
let altair_slot = altair_epoch.start_slot(E::slots_per_epoch());
|
||||
|
||||
let mut spec = E::default_spec();
|
||||
spec.altair_fork_epoch = Some(fork_epoch);
|
||||
|
||||
// BeaconBlockBase
|
||||
{
|
||||
let good_base_block = BeaconBlock::Base(BeaconBlockBase {
|
||||
|
||||
@@ -251,6 +251,53 @@ impl<E: EthSpec> From<BeaconBlockBodyMerge<E, FullPayload<E>>>
|
||||
}
|
||||
}
|
||||
|
||||
// We can clone a full block into a blinded block, without cloning the payload.
|
||||
impl<E: EthSpec> BeaconBlockBodyBase<E, FullPayload<E>> {
|
||||
pub fn clone_as_blinded(&self) -> BeaconBlockBodyBase<E, BlindedPayload<E>> {
|
||||
let (block_body, _payload) = self.clone().into();
|
||||
block_body
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> BeaconBlockBodyAltair<E, FullPayload<E>> {
|
||||
pub fn clone_as_blinded(&self) -> BeaconBlockBodyAltair<E, BlindedPayload<E>> {
|
||||
let (block_body, _payload) = self.clone().into();
|
||||
block_body
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> BeaconBlockBodyMerge<E, FullPayload<E>> {
|
||||
pub fn clone_as_blinded(&self) -> BeaconBlockBodyMerge<E, BlindedPayload<E>> {
|
||||
let BeaconBlockBodyMerge {
|
||||
randao_reveal,
|
||||
eth1_data,
|
||||
graffiti,
|
||||
proposer_slashings,
|
||||
attester_slashings,
|
||||
attestations,
|
||||
deposits,
|
||||
voluntary_exits,
|
||||
sync_aggregate,
|
||||
execution_payload: FullPayload { execution_payload },
|
||||
} = self;
|
||||
|
||||
BeaconBlockBodyMerge {
|
||||
randao_reveal: randao_reveal.clone(),
|
||||
eth1_data: eth1_data.clone(),
|
||||
graffiti: *graffiti,
|
||||
proposer_slashings: proposer_slashings.clone(),
|
||||
attester_slashings: attester_slashings.clone(),
|
||||
attestations: attestations.clone(),
|
||||
deposits: deposits.clone(),
|
||||
voluntary_exits: voluntary_exits.clone(),
|
||||
sync_aggregate: sync_aggregate.clone(),
|
||||
execution_payload: BlindedPayload {
|
||||
execution_payload_header: From::from(execution_payload),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> From<BeaconBlockBody<E, FullPayload<E>>>
|
||||
for (
|
||||
BeaconBlockBody<E, BlindedPayload<E>>,
|
||||
|
||||
@@ -985,6 +985,13 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the minimum epoch for which `get_randao_mix` will return a non-error value.
|
||||
pub fn min_randao_epoch(&self) -> Epoch {
|
||||
self.current_epoch()
|
||||
.saturating_add(1u64)
|
||||
.saturating_sub(T::EpochsPerHistoricalVector::to_u64())
|
||||
}
|
||||
|
||||
/// XOR-assigns the existing `epoch` randao mix with the hash of the `signature`.
|
||||
///
|
||||
/// # Errors:
|
||||
@@ -1625,15 +1632,17 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
Ok(self.validators().tree_hash_root())
|
||||
}
|
||||
|
||||
pub fn is_eligible_validator(&self, val: &Validator) -> bool {
|
||||
let previous_epoch = self.previous_epoch();
|
||||
/// Passing `previous_epoch` to this function rather than computing it internally provides
|
||||
/// a tangible speed improvement in state processing.
|
||||
pub fn is_eligible_validator(&self, previous_epoch: Epoch, val: &Validator) -> bool {
|
||||
val.is_active_at(previous_epoch)
|
||||
|| (val.slashed && previous_epoch + Epoch::new(1) < val.withdrawable_epoch)
|
||||
}
|
||||
|
||||
pub fn is_in_inactivity_leak(&self, spec: &ChainSpec) -> bool {
|
||||
(self.previous_epoch() - self.finalized_checkpoint().epoch)
|
||||
> spec.min_epochs_to_inactivity_penalty
|
||||
/// Passing `previous_epoch` to this function rather than computing it internally provides
|
||||
/// a tangible speed improvement in state processing.
|
||||
pub fn is_in_inactivity_leak(&self, previous_epoch: Epoch, spec: &ChainSpec) -> bool {
|
||||
(previous_epoch - self.finalized_checkpoint().epoch) > spec.min_epochs_to_inactivity_penalty
|
||||
}
|
||||
|
||||
/// Get the `SyncCommittee` associated with the next slot. Useful because sync committees
|
||||
|
||||
@@ -39,8 +39,18 @@ impl CommitteeCache {
|
||||
epoch: Epoch,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Arc<CommitteeCache>, Error> {
|
||||
RelativeEpoch::from_epoch(state.current_epoch(), epoch)
|
||||
.map_err(|_| Error::EpochOutOfBounds)?;
|
||||
// Check that the cache is being built for an in-range epoch.
|
||||
//
|
||||
// We allow caches to be constructed for historic epochs, per:
|
||||
//
|
||||
// https://github.com/sigp/lighthouse/issues/3270
|
||||
let reqd_randao_epoch = epoch
|
||||
.saturating_sub(spec.min_seed_lookahead)
|
||||
.saturating_sub(1u64);
|
||||
|
||||
if reqd_randao_epoch < state.min_randao_epoch() || epoch > state.current_epoch() + 1 {
|
||||
return Err(Error::EpochOutOfBounds);
|
||||
}
|
||||
|
||||
// May cause divide-by-zero errors.
|
||||
if T::slots_per_epoch() == 0 {
|
||||
|
||||
@@ -34,32 +34,34 @@ fn default_values() {
|
||||
assert!(cache.get_beacon_committees_at_slot(Slot::new(0)).is_err());
|
||||
}
|
||||
|
||||
fn new_state<T: EthSpec>(validator_count: usize, slot: Slot) -> BeaconState<T> {
|
||||
async fn new_state<T: EthSpec>(validator_count: usize, slot: Slot) -> BeaconState<T> {
|
||||
let harness = get_harness(validator_count);
|
||||
let head_state = harness.get_current_state();
|
||||
if slot > Slot::new(0) {
|
||||
harness.add_attested_blocks_at_slots(
|
||||
head_state,
|
||||
Hash256::zero(),
|
||||
(1..slot.as_u64())
|
||||
.map(Slot::new)
|
||||
.collect::<Vec<_>>()
|
||||
.as_slice(),
|
||||
(0..validator_count).collect::<Vec<_>>().as_slice(),
|
||||
);
|
||||
harness
|
||||
.add_attested_blocks_at_slots(
|
||||
head_state,
|
||||
Hash256::zero(),
|
||||
(1..=slot.as_u64())
|
||||
.map(Slot::new)
|
||||
.collect::<Vec<_>>()
|
||||
.as_slice(),
|
||||
(0..validator_count).collect::<Vec<_>>().as_slice(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
harness.get_current_state()
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[tokio::test]
|
||||
#[should_panic]
|
||||
fn fails_without_validators() {
|
||||
new_state::<MinimalEthSpec>(0, Slot::new(0));
|
||||
async fn fails_without_validators() {
|
||||
new_state::<MinimalEthSpec>(0, Slot::new(0)).await;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn initializes_with_the_right_epoch() {
|
||||
let state = new_state::<MinimalEthSpec>(16, Slot::new(0));
|
||||
#[tokio::test]
|
||||
async fn initializes_with_the_right_epoch() {
|
||||
let state = new_state::<MinimalEthSpec>(16, Slot::new(0)).await;
|
||||
let spec = &MinimalEthSpec::default_spec();
|
||||
|
||||
let cache = CommitteeCache::default();
|
||||
@@ -75,17 +77,20 @@ fn initializes_with_the_right_epoch() {
|
||||
assert!(cache.is_initialized_at(state.next_epoch().unwrap()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shuffles_for_the_right_epoch() {
|
||||
#[tokio::test]
|
||||
async fn shuffles_for_the_right_epoch() {
|
||||
let num_validators = MinimalEthSpec::minimum_validator_count() * 2;
|
||||
let epoch = Epoch::new(6);
|
||||
let slot = epoch.start_slot(MinimalEthSpec::slots_per_epoch());
|
||||
|
||||
let mut state = new_state::<MinimalEthSpec>(num_validators, slot);
|
||||
let mut state = new_state::<MinimalEthSpec>(num_validators, slot).await;
|
||||
let spec = &MinimalEthSpec::default_spec();
|
||||
|
||||
let distinct_hashes = (0..MinimalEthSpec::epochs_per_historical_vector())
|
||||
.map(|i| Hash256::from_low_u64_be(i as u64));
|
||||
assert_eq!(state.current_epoch(), epoch);
|
||||
|
||||
let distinct_hashes: Vec<Hash256> = (0..MinimalEthSpec::epochs_per_historical_vector())
|
||||
.map(|i| Hash256::from_low_u64_be(i as u64))
|
||||
.collect();
|
||||
|
||||
*state.randao_mixes_mut() = FixedVector::try_from_iter(distinct_hashes).unwrap();
|
||||
|
||||
@@ -121,15 +126,41 @@ fn shuffles_for_the_right_epoch() {
|
||||
}
|
||||
};
|
||||
|
||||
let cache = CommitteeCache::initialized(&state, state.current_epoch(), spec).unwrap();
|
||||
assert_eq!(cache.shuffling(), shuffling_with_seed(current_seed));
|
||||
assert_shuffling_positions_accurate(&cache);
|
||||
// We can initialize the committee cache at recent epochs in the past, and one epoch into the
|
||||
// future.
|
||||
for e in (0..=epoch.as_u64() + 1).map(Epoch::new) {
|
||||
let seed = state.get_seed(e, Domain::BeaconAttester, spec).unwrap();
|
||||
let cache = CommitteeCache::initialized(&state, e, spec)
|
||||
.unwrap_or_else(|_| panic!("failed at epoch {}", e));
|
||||
assert_eq!(cache.shuffling(), shuffling_with_seed(seed));
|
||||
assert_shuffling_positions_accurate(&cache);
|
||||
}
|
||||
|
||||
let cache = CommitteeCache::initialized(&state, state.previous_epoch(), spec).unwrap();
|
||||
assert_eq!(cache.shuffling(), shuffling_with_seed(previous_seed));
|
||||
assert_shuffling_positions_accurate(&cache);
|
||||
|
||||
let cache = CommitteeCache::initialized(&state, state.next_epoch().unwrap(), spec).unwrap();
|
||||
assert_eq!(cache.shuffling(), shuffling_with_seed(next_seed));
|
||||
assert_shuffling_positions_accurate(&cache);
|
||||
// We should *not* be able to build a committee cache for the epoch after the next epoch.
|
||||
assert_eq!(
|
||||
CommitteeCache::initialized(&state, epoch + 2, spec),
|
||||
Err(BeaconStateError::EpochOutOfBounds)
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn min_randao_epoch_correct() {
|
||||
let num_validators = MinimalEthSpec::minimum_validator_count() * 2;
|
||||
let current_epoch = Epoch::new(MinimalEthSpec::epochs_per_historical_vector() as u64 * 2);
|
||||
|
||||
let mut state = new_state::<MinimalEthSpec>(
|
||||
num_validators,
|
||||
Epoch::new(1).start_slot(MinimalEthSpec::slots_per_epoch()),
|
||||
)
|
||||
.await;
|
||||
|
||||
// Override the epoch so that there's some room to move.
|
||||
*state.slot_mut() = current_epoch.start_slot(MinimalEthSpec::slots_per_epoch());
|
||||
assert_eq!(state.current_epoch(), current_epoch);
|
||||
|
||||
// The min_randao_epoch should be the minimum epoch such that `get_randao_mix` returns `Ok`.
|
||||
let min_randao_epoch = state.min_randao_epoch();
|
||||
state.get_randao_mix(min_randao_epoch).unwrap();
|
||||
state.get_randao_mix(min_randao_epoch - 1).unwrap_err();
|
||||
state.get_randao_mix(min_randao_epoch + 1).unwrap();
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ use beacon_chain::types::{
|
||||
ChainSpec, Domain, Epoch, EthSpec, FixedVector, Hash256, Keypair, MainnetEthSpec,
|
||||
MinimalEthSpec, RelativeEpoch, Slot,
|
||||
};
|
||||
use safe_arith::SafeArith;
|
||||
use ssz::{Decode, Encode};
|
||||
use state_processing::per_slot_processing;
|
||||
use std::ops::Mul;
|
||||
use swap_or_not_shuffle::compute_shuffled_index;
|
||||
use tree_hash::TreeHash;
|
||||
@@ -20,7 +22,7 @@ lazy_static! {
|
||||
static ref KEYPAIRS: Vec<Keypair> = generate_deterministic_keypairs(MAX_VALIDATOR_COUNT);
|
||||
}
|
||||
|
||||
fn get_harness<E: EthSpec>(
|
||||
async fn get_harness<E: EthSpec>(
|
||||
validator_count: usize,
|
||||
slot: Slot,
|
||||
) -> BeaconChainHarness<EphemeralHarnessType<E>> {
|
||||
@@ -36,24 +38,26 @@ fn get_harness<E: EthSpec>(
|
||||
.map(Slot::new)
|
||||
.collect::<Vec<_>>();
|
||||
let state = harness.get_current_state();
|
||||
harness.add_attested_blocks_at_slots(
|
||||
state,
|
||||
Hash256::zero(),
|
||||
slots.as_slice(),
|
||||
(0..validator_count).collect::<Vec<_>>().as_slice(),
|
||||
);
|
||||
harness
|
||||
.add_attested_blocks_at_slots(
|
||||
state,
|
||||
Hash256::zero(),
|
||||
slots.as_slice(),
|
||||
(0..validator_count).collect::<Vec<_>>().as_slice(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
harness
|
||||
}
|
||||
|
||||
fn build_state<E: EthSpec>(validator_count: usize) -> BeaconState<E> {
|
||||
async fn build_state<E: EthSpec>(validator_count: usize) -> BeaconState<E> {
|
||||
get_harness(validator_count, Slot::new(0))
|
||||
.await
|
||||
.chain
|
||||
.head_beacon_state()
|
||||
.unwrap()
|
||||
.head_beacon_state_cloned()
|
||||
}
|
||||
|
||||
fn test_beacon_proposer_index<T: EthSpec>() {
|
||||
async fn test_beacon_proposer_index<T: EthSpec>() {
|
||||
let spec = T::default_spec();
|
||||
|
||||
// Get the i'th candidate proposer for the given state and slot
|
||||
@@ -80,20 +84,20 @@ fn test_beacon_proposer_index<T: EthSpec>() {
|
||||
|
||||
// Test where we have one validator per slot.
|
||||
// 0th candidate should be chosen every time.
|
||||
let state = build_state(T::slots_per_epoch() as usize);
|
||||
let state = build_state(T::slots_per_epoch() as usize).await;
|
||||
for i in 0..T::slots_per_epoch() {
|
||||
test(&state, Slot::from(i), 0);
|
||||
}
|
||||
|
||||
// Test where we have two validators per slot.
|
||||
// 0th candidate should be chosen every time.
|
||||
let state = build_state((T::slots_per_epoch() as usize).mul(2));
|
||||
let state = build_state((T::slots_per_epoch() as usize).mul(2)).await;
|
||||
for i in 0..T::slots_per_epoch() {
|
||||
test(&state, Slot::from(i), 0);
|
||||
}
|
||||
|
||||
// Test with two validators per slot, first validator has zero balance.
|
||||
let mut state = build_state::<T>((T::slots_per_epoch() as usize).mul(2));
|
||||
let mut state = build_state::<T>((T::slots_per_epoch() as usize).mul(2)).await;
|
||||
let slot0_candidate0 = ith_candidate(&state, Slot::new(0), 0, &spec);
|
||||
state
|
||||
.validators_mut()
|
||||
@@ -106,9 +110,9 @@ fn test_beacon_proposer_index<T: EthSpec>() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn beacon_proposer_index() {
|
||||
test_beacon_proposer_index::<MinimalEthSpec>();
|
||||
#[tokio::test]
|
||||
async fn beacon_proposer_index() {
|
||||
test_beacon_proposer_index::<MinimalEthSpec>().await;
|
||||
}
|
||||
|
||||
/// Test that
|
||||
@@ -143,11 +147,11 @@ fn test_cache_initialization<T: EthSpec>(
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_initialization() {
|
||||
#[tokio::test]
|
||||
async fn cache_initialization() {
|
||||
let spec = MinimalEthSpec::default_spec();
|
||||
|
||||
let mut state = build_state::<MinimalEthSpec>(16);
|
||||
let mut state = build_state::<MinimalEthSpec>(16).await;
|
||||
|
||||
*state.slot_mut() =
|
||||
(MinimalEthSpec::genesis_epoch() + 1).start_slot(MinimalEthSpec::slots_per_epoch());
|
||||
@@ -236,7 +240,7 @@ mod committees {
|
||||
assert!(expected_indices_iter.next().is_none());
|
||||
}
|
||||
|
||||
fn committee_consistency_test<T: EthSpec>(
|
||||
async fn committee_consistency_test<T: EthSpec>(
|
||||
validator_count: usize,
|
||||
state_epoch: Epoch,
|
||||
cache_epoch: RelativeEpoch,
|
||||
@@ -244,7 +248,7 @@ mod committees {
|
||||
let spec = &T::default_spec();
|
||||
|
||||
let slot = state_epoch.start_slot(T::slots_per_epoch());
|
||||
let harness = get_harness::<T>(validator_count, slot);
|
||||
let harness = get_harness::<T>(validator_count, slot).await;
|
||||
let mut new_head_state = harness.get_current_state();
|
||||
|
||||
let distinct_hashes =
|
||||
@@ -271,7 +275,7 @@ mod committees {
|
||||
);
|
||||
}
|
||||
|
||||
fn committee_consistency_test_suite<T: EthSpec>(cached_epoch: RelativeEpoch) {
|
||||
async fn committee_consistency_test_suite<T: EthSpec>(cached_epoch: RelativeEpoch) {
|
||||
let spec = T::default_spec();
|
||||
|
||||
let validator_count = spec
|
||||
@@ -280,13 +284,15 @@ mod committees {
|
||||
.mul(spec.target_committee_size)
|
||||
.add(1);
|
||||
|
||||
committee_consistency_test::<T>(validator_count as usize, Epoch::new(0), cached_epoch);
|
||||
committee_consistency_test::<T>(validator_count as usize, Epoch::new(0), cached_epoch)
|
||||
.await;
|
||||
|
||||
committee_consistency_test::<T>(
|
||||
validator_count as usize,
|
||||
T::genesis_epoch() + 4,
|
||||
cached_epoch,
|
||||
);
|
||||
)
|
||||
.await;
|
||||
|
||||
committee_consistency_test::<T>(
|
||||
validator_count as usize,
|
||||
@@ -295,38 +301,39 @@ mod committees {
|
||||
.mul(T::slots_per_epoch())
|
||||
.mul(4),
|
||||
cached_epoch,
|
||||
);
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn current_epoch_committee_consistency() {
|
||||
committee_consistency_test_suite::<MinimalEthSpec>(RelativeEpoch::Current);
|
||||
#[tokio::test]
|
||||
async fn current_epoch_committee_consistency() {
|
||||
committee_consistency_test_suite::<MinimalEthSpec>(RelativeEpoch::Current).await;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn previous_epoch_committee_consistency() {
|
||||
committee_consistency_test_suite::<MinimalEthSpec>(RelativeEpoch::Previous);
|
||||
#[tokio::test]
|
||||
async fn previous_epoch_committee_consistency() {
|
||||
committee_consistency_test_suite::<MinimalEthSpec>(RelativeEpoch::Previous).await;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn next_epoch_committee_consistency() {
|
||||
committee_consistency_test_suite::<MinimalEthSpec>(RelativeEpoch::Next);
|
||||
#[tokio::test]
|
||||
async fn next_epoch_committee_consistency() {
|
||||
committee_consistency_test_suite::<MinimalEthSpec>(RelativeEpoch::Next).await;
|
||||
}
|
||||
}
|
||||
|
||||
mod get_outstanding_deposit_len {
|
||||
use super::*;
|
||||
|
||||
fn state() -> BeaconState<MinimalEthSpec> {
|
||||
async fn state() -> BeaconState<MinimalEthSpec> {
|
||||
get_harness(16, Slot::new(0))
|
||||
.await
|
||||
.chain
|
||||
.head_beacon_state()
|
||||
.unwrap()
|
||||
.head_beacon_state_cloned()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn returns_ok() {
|
||||
let mut state = state();
|
||||
#[tokio::test]
|
||||
async fn returns_ok() {
|
||||
let mut state = state().await;
|
||||
assert_eq!(state.get_outstanding_deposit_len(), Ok(0));
|
||||
|
||||
state.eth1_data_mut().deposit_count = 17;
|
||||
@@ -334,9 +341,9 @@ mod get_outstanding_deposit_len {
|
||||
assert_eq!(state.get_outstanding_deposit_len(), Ok(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn returns_err_if_the_state_is_invalid() {
|
||||
let mut state = state();
|
||||
#[tokio::test]
|
||||
async fn returns_err_if_the_state_is_invalid() {
|
||||
let mut state = state().await;
|
||||
// The state is invalid, deposit count is lower than deposit index.
|
||||
state.eth1_data_mut().deposit_count = 16;
|
||||
*state.eth1_deposit_index_mut() = 17;
|
||||
@@ -354,62 +361,60 @@ mod get_outstanding_deposit_len {
|
||||
#[test]
|
||||
fn decode_base_and_altair() {
|
||||
type E = MainnetEthSpec;
|
||||
let spec = E::default_spec();
|
||||
|
||||
let rng = &mut XorShiftRng::from_seed([42; 16]);
|
||||
|
||||
let fork_epoch = Epoch::from_ssz_bytes(&[7, 6, 5, 4, 3, 2, 1, 0]).unwrap();
|
||||
let fork_epoch = spec.altair_fork_epoch.unwrap();
|
||||
|
||||
let base_epoch = fork_epoch.saturating_sub(1_u64);
|
||||
let base_slot = base_epoch.end_slot(E::slots_per_epoch());
|
||||
let altair_epoch = fork_epoch;
|
||||
let altair_slot = altair_epoch.start_slot(E::slots_per_epoch());
|
||||
|
||||
let mut spec = E::default_spec();
|
||||
spec.altair_fork_epoch = Some(altair_epoch);
|
||||
|
||||
// BeaconStateBase
|
||||
{
|
||||
let good_base_block: BeaconState<MainnetEthSpec> = BeaconState::Base(BeaconStateBase {
|
||||
let good_base_state: BeaconState<MainnetEthSpec> = BeaconState::Base(BeaconStateBase {
|
||||
slot: base_slot,
|
||||
..<_>::random_for_test(rng)
|
||||
});
|
||||
// It's invalid to have a base block with a slot higher than the fork slot.
|
||||
let bad_base_block = {
|
||||
let mut bad = good_base_block.clone();
|
||||
// It's invalid to have a base state with a slot higher than the fork slot.
|
||||
let bad_base_state = {
|
||||
let mut bad = good_base_state.clone();
|
||||
*bad.slot_mut() = altair_slot;
|
||||
bad
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
BeaconState::from_ssz_bytes(&good_base_block.as_ssz_bytes(), &spec)
|
||||
.expect("good base block can be decoded"),
|
||||
good_base_block
|
||||
BeaconState::from_ssz_bytes(&good_base_state.as_ssz_bytes(), &spec)
|
||||
.expect("good base state can be decoded"),
|
||||
good_base_state
|
||||
);
|
||||
<BeaconState<MainnetEthSpec>>::from_ssz_bytes(&bad_base_block.as_ssz_bytes(), &spec)
|
||||
.expect_err("bad base block cannot be decoded");
|
||||
<BeaconState<MainnetEthSpec>>::from_ssz_bytes(&bad_base_state.as_ssz_bytes(), &spec)
|
||||
.expect_err("bad base state cannot be decoded");
|
||||
}
|
||||
|
||||
// BeaconStateAltair
|
||||
{
|
||||
let good_altair_block: BeaconState<MainnetEthSpec> =
|
||||
let good_altair_state: BeaconState<MainnetEthSpec> =
|
||||
BeaconState::Altair(BeaconStateAltair {
|
||||
slot: altair_slot,
|
||||
..<_>::random_for_test(rng)
|
||||
});
|
||||
// It's invalid to have an Altair block with a slot lower than the fork slot.
|
||||
let bad_altair_block = {
|
||||
let mut bad = good_altair_block.clone();
|
||||
// It's invalid to have an Altair state with a slot lower than the fork slot.
|
||||
let bad_altair_state = {
|
||||
let mut bad = good_altair_state.clone();
|
||||
*bad.slot_mut() = base_slot;
|
||||
bad
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
BeaconState::from_ssz_bytes(&good_altair_block.as_ssz_bytes(), &spec)
|
||||
.expect("good altair block can be decoded"),
|
||||
good_altair_block
|
||||
BeaconState::from_ssz_bytes(&good_altair_state.as_ssz_bytes(), &spec)
|
||||
.expect("good altair state can be decoded"),
|
||||
good_altair_state
|
||||
);
|
||||
<BeaconState<MainnetEthSpec>>::from_ssz_bytes(&bad_altair_block.as_ssz_bytes(), &spec)
|
||||
.expect_err("bad altair block cannot be decoded");
|
||||
<BeaconState<MainnetEthSpec>>::from_ssz_bytes(&bad_altair_state.as_ssz_bytes(), &spec)
|
||||
.expect_err("bad altair state cannot be decoded");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
70
consensus/types/src/builder_bid.rs
Normal file
70
consensus/types/src/builder_bid.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
use crate::{ChainSpec, EthSpec, ExecPayload, ExecutionPayloadHeader, SignedRoot, Uint256};
|
||||
use bls::PublicKeyBytes;
|
||||
use bls::Signature;
|
||||
use serde::{Deserialize as De, Deserializer, Serialize as Ser, Serializer};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use serde_with::{serde_as, DeserializeAs, SerializeAs};
|
||||
use std::marker::PhantomData;
|
||||
use tree_hash_derive::TreeHash;
|
||||
|
||||
#[serde_as]
|
||||
#[derive(PartialEq, Debug, Serialize, Deserialize, TreeHash, Clone)]
|
||||
#[serde(bound = "E: EthSpec, Payload: ExecPayload<E>")]
|
||||
pub struct BuilderBid<E: EthSpec, Payload: ExecPayload<E>> {
|
||||
#[serde_as(as = "BlindedPayloadAsHeader<E>")]
|
||||
pub header: Payload,
|
||||
#[serde(with = "eth2_serde_utils::quoted_u256")]
|
||||
pub value: Uint256,
|
||||
pub pubkey: PublicKeyBytes,
|
||||
#[serde(skip)]
|
||||
#[tree_hash(skip_hashing)]
|
||||
_phantom_data: PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<E: EthSpec, Payload: ExecPayload<E>> SignedRoot for BuilderBid<E, Payload> {}
|
||||
|
||||
/// Validator registration, for use in interacting with servers implementing the builder API.
|
||||
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
|
||||
#[serde(bound = "E: EthSpec, Payload: ExecPayload<E>")]
|
||||
pub struct SignedBuilderBid<E: EthSpec, Payload: ExecPayload<E>> {
|
||||
pub message: BuilderBid<E, Payload>,
|
||||
pub signature: Signature,
|
||||
}
|
||||
|
||||
struct BlindedPayloadAsHeader<E>(PhantomData<E>);
|
||||
|
||||
impl<E: EthSpec, Payload: ExecPayload<E>> SerializeAs<Payload> for BlindedPayloadAsHeader<E> {
|
||||
fn serialize_as<S>(source: &Payload, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
source.to_execution_payload_header().serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, E: EthSpec, Payload: ExecPayload<E>> DeserializeAs<'de, Payload>
|
||||
for BlindedPayloadAsHeader<E>
|
||||
{
|
||||
fn deserialize_as<D>(deserializer: D) -> Result<Payload, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let payload_header = ExecutionPayloadHeader::deserialize(deserializer)?;
|
||||
Payload::try_from(payload_header)
|
||||
.map_err(|_| serde::de::Error::custom("unable to convert payload header to payload"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec, Payload: ExecPayload<E>> SignedBuilderBid<E, Payload> {
|
||||
pub fn verify_signature(&self, spec: &ChainSpec) -> bool {
|
||||
self.message
|
||||
.pubkey
|
||||
.decompress()
|
||||
.map(|pubkey| {
|
||||
let domain = spec.get_builder_domain();
|
||||
let message = self.message.signing_root(domain);
|
||||
self.signature.verify(&pubkey, message)
|
||||
})
|
||||
.unwrap_or(false)
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::application_domain::{ApplicationDomain, APPLICATION_DOMAIN_BUILDER};
|
||||
use crate::*;
|
||||
use eth2_serde_utils::quoted_u64::MaybeQuoted;
|
||||
use int_to_bytes::int_to_bytes4;
|
||||
@@ -20,6 +21,7 @@ pub enum Domain {
|
||||
SyncCommittee,
|
||||
ContributionAndProof,
|
||||
SyncCommitteeSelectionProof,
|
||||
ApplicationMask(ApplicationDomain),
|
||||
}
|
||||
|
||||
/// Lighthouse's internal configuration struct.
|
||||
@@ -159,6 +161,11 @@ pub struct ChainSpec {
|
||||
pub attestation_subnet_count: u64,
|
||||
pub random_subnets_per_validator: u64,
|
||||
pub epochs_per_random_subnet_subscription: u64,
|
||||
|
||||
/*
|
||||
* Application params
|
||||
*/
|
||||
pub(crate) domain_application_mask: u32,
|
||||
}
|
||||
|
||||
impl ChainSpec {
|
||||
@@ -333,6 +340,7 @@ impl ChainSpec {
|
||||
Domain::SyncCommittee => self.domain_sync_committee,
|
||||
Domain::ContributionAndProof => self.domain_contribution_and_proof,
|
||||
Domain::SyncCommitteeSelectionProof => self.domain_sync_committee_selection_proof,
|
||||
Domain::ApplicationMask(application_domain) => application_domain.get_domain_constant(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,6 +368,17 @@ impl ChainSpec {
|
||||
self.compute_domain(Domain::Deposit, self.genesis_fork_version, Hash256::zero())
|
||||
}
|
||||
|
||||
// This should be updated to include the current fork and the genesis validators root, but discussion is ongoing:
|
||||
//
|
||||
// https://github.com/ethereum/builder-specs/issues/14
|
||||
pub fn get_builder_domain(&self) -> Hash256 {
|
||||
self.compute_domain(
|
||||
Domain::ApplicationMask(ApplicationDomain::Builder),
|
||||
self.genesis_fork_version,
|
||||
Hash256::zero(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Return the 32-byte fork data root for the `current_version` and `genesis_validators_root`.
|
||||
///
|
||||
/// This is used primarily in signature domains to avoid collisions across forks/chains.
|
||||
@@ -549,14 +568,9 @@ impl ChainSpec {
|
||||
.expect("pow does not overflow"),
|
||||
proportional_slashing_multiplier_bellatrix: 3,
|
||||
bellatrix_fork_version: [0x02, 0x00, 0x00, 0x00],
|
||||
bellatrix_fork_epoch: None,
|
||||
terminal_total_difficulty: Uint256::MAX
|
||||
.checked_sub(Uint256::from(2u64.pow(10)))
|
||||
.expect("subtraction does not overflow")
|
||||
// Add 1 since the spec declares `2**256 - 2**10` and we use
|
||||
// `Uint256::MAX` which is `2*256- 1`.
|
||||
.checked_add(Uint256::one())
|
||||
.expect("addition does not overflow"),
|
||||
bellatrix_fork_epoch: Some(Epoch::new(144896)),
|
||||
terminal_total_difficulty: Uint256::from_dec_str("58750000000000000000000")
|
||||
.expect("terminal_total_difficulty is a valid integer"),
|
||||
terminal_block_hash: ExecutionBlockHash::zero(),
|
||||
terminal_block_hash_activation_epoch: Epoch::new(u64::MAX),
|
||||
safe_slots_to_import_optimistically: 128u64,
|
||||
@@ -572,6 +586,11 @@ impl ChainSpec {
|
||||
maximum_gossip_clock_disparity_millis: 500,
|
||||
target_aggregators_per_committee: 16,
|
||||
epochs_per_random_subnet_subscription: 256,
|
||||
|
||||
/*
|
||||
* Application specific
|
||||
*/
|
||||
domain_application_mask: APPLICATION_DOMAIN_BUILDER,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -604,6 +623,13 @@ impl ChainSpec {
|
||||
// Merge
|
||||
bellatrix_fork_version: [0x02, 0x00, 0x00, 0x01],
|
||||
bellatrix_fork_epoch: None,
|
||||
terminal_total_difficulty: Uint256::MAX
|
||||
.checked_sub(Uint256::from(2u64.pow(10)))
|
||||
.expect("subtraction does not overflow")
|
||||
// Add 1 since the spec declares `2**256 - 2**10` and we use
|
||||
// `Uint256::MAX` which is `2*256- 1`.
|
||||
.checked_add(Uint256::one())
|
||||
.expect("addition does not overflow"),
|
||||
// Other
|
||||
network_id: 2, // lighthouse testnet network id
|
||||
deposit_chain_id: 5,
|
||||
@@ -770,6 +796,11 @@ impl ChainSpec {
|
||||
maximum_gossip_clock_disparity_millis: 500,
|
||||
target_aggregators_per_committee: 16,
|
||||
epochs_per_random_subnet_subscription: 256,
|
||||
|
||||
/*
|
||||
* Application specific
|
||||
*/
|
||||
domain_application_mask: APPLICATION_DOMAIN_BUILDER,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -781,6 +812,10 @@ impl Default for ChainSpec {
|
||||
}
|
||||
|
||||
/// Exact implementation of the *config* object from the Ethereum spec (YAML/JSON).
|
||||
///
|
||||
/// Fields relevant to hard forks after Altair should be optional so that we can continue
|
||||
/// to parse Altair configs. This default approach turns out to be much simpler than trying to
|
||||
/// make `Config` a superstruct because of the hassle of deserializing an untagged enum.
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub struct Config {
|
||||
@@ -791,17 +826,13 @@ pub struct Config {
|
||||
#[serde(default)]
|
||||
pub preset_base: String,
|
||||
|
||||
// TODO(merge): remove this default
|
||||
#[serde(default = "default_terminal_total_difficulty")]
|
||||
#[serde(with = "eth2_serde_utils::quoted_u256")]
|
||||
pub terminal_total_difficulty: Uint256,
|
||||
// TODO(merge): remove this default
|
||||
#[serde(default = "default_terminal_block_hash")]
|
||||
pub terminal_block_hash: ExecutionBlockHash,
|
||||
// TODO(merge): remove this default
|
||||
#[serde(default = "default_terminal_block_hash_activation_epoch")]
|
||||
pub terminal_block_hash_activation_epoch: Epoch,
|
||||
// TODO(merge): remove this default
|
||||
#[serde(default = "default_safe_slots_to_import_optimistically")]
|
||||
#[serde(with = "eth2_serde_utils::quoted_u64")]
|
||||
pub safe_slots_to_import_optimistically: u64,
|
||||
@@ -821,12 +852,10 @@ pub struct Config {
|
||||
#[serde(deserialize_with = "deserialize_fork_epoch")]
|
||||
pub altair_fork_epoch: Option<MaybeQuoted<Epoch>>,
|
||||
|
||||
// TODO(merge): remove this default
|
||||
#[serde(default = "default_bellatrix_fork_version")]
|
||||
#[serde(with = "eth2_serde_utils::bytes_4_hex")]
|
||||
bellatrix_fork_version: [u8; 4],
|
||||
// TODO(merge): remove this default
|
||||
#[serde(default = "default_bellatrix_fork_epoch")]
|
||||
#[serde(default)]
|
||||
#[serde(serialize_with = "serialize_fork_epoch")]
|
||||
#[serde(deserialize_with = "deserialize_fork_epoch")]
|
||||
pub bellatrix_fork_epoch: Option<MaybeQuoted<Epoch>>,
|
||||
@@ -868,10 +897,6 @@ fn default_bellatrix_fork_version() -> [u8; 4] {
|
||||
[0xff, 0xff, 0xff, 0xff]
|
||||
}
|
||||
|
||||
fn default_bellatrix_fork_epoch() -> Option<MaybeQuoted<Epoch>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Placeholder value: 2^256-2^10 (115792089237316195423570985008687907853269984665640564039457584007913129638912).
|
||||
///
|
||||
/// Taken from https://github.com/ethereum/consensus-specs/blob/d5e4828aecafaf1c57ef67a5f23c4ae7b08c5137/configs/mainnet.yaml#L15-L16
|
||||
@@ -1126,6 +1151,27 @@ mod tests {
|
||||
&spec,
|
||||
);
|
||||
test_domain(Domain::SyncCommittee, spec.domain_sync_committee, &spec);
|
||||
|
||||
// The builder domain index is zero
|
||||
let builder_domain_pre_mask = [0; 4];
|
||||
test_domain(
|
||||
Domain::ApplicationMask(ApplicationDomain::Builder),
|
||||
apply_bit_mask(builder_domain_pre_mask, &spec),
|
||||
&spec,
|
||||
);
|
||||
}
|
||||
|
||||
fn apply_bit_mask(domain_bytes: [u8; 4], spec: &ChainSpec) -> u32 {
|
||||
let mut domain = [0; 4];
|
||||
let mask_bytes = int_to_bytes4(spec.domain_application_mask);
|
||||
|
||||
// Apply application bit mask
|
||||
for (i, (domain_byte, mask_byte)) in domain_bytes.iter().zip(mask_bytes.iter()).enumerate()
|
||||
{
|
||||
domain[i] = domain_byte | mask_byte;
|
||||
}
|
||||
|
||||
u32::from_le_bytes(domain)
|
||||
}
|
||||
|
||||
// Test that `fork_name_at_epoch` and `fork_epoch` are consistent.
|
||||
@@ -1292,10 +1338,7 @@ mod yaml_tests {
|
||||
default_safe_slots_to_import_optimistically()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
chain_spec.bellatrix_fork_epoch,
|
||||
default_bellatrix_fork_epoch()
|
||||
);
|
||||
assert_eq!(chain_spec.bellatrix_fork_epoch, None);
|
||||
|
||||
assert_eq!(
|
||||
chain_spec.bellatrix_fork_version,
|
||||
@@ -1312,4 +1355,12 @@ mod yaml_tests {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_domain_builder() {
|
||||
assert_eq!(
|
||||
int_to_bytes4(ApplicationDomain::Builder.get_domain_constant()),
|
||||
[0, 0, 0, 1]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
use crate::{AltairPreset, BasePreset, BellatrixPreset, ChainSpec, Config, EthSpec};
|
||||
use crate::{
|
||||
consts::altair, AltairPreset, BasePreset, BellatrixPreset, ChainSpec, Config, EthSpec, ForkName,
|
||||
};
|
||||
use maplit::hashmap;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
use superstruct::superstruct;
|
||||
|
||||
/// Fusion of a runtime-config with the compile-time preset values.
|
||||
///
|
||||
/// Mostly useful for the API.
|
||||
#[superstruct(
|
||||
variants(Altair, Bellatrix),
|
||||
variant_attributes(derive(Serialize, Deserialize, Debug, PartialEq, Clone))
|
||||
)]
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||
#[serde(untagged)]
|
||||
pub struct ConfigAndPreset {
|
||||
#[serde(flatten)]
|
||||
pub config: Config,
|
||||
@@ -15,76 +24,75 @@ pub struct ConfigAndPreset {
|
||||
pub base_preset: BasePreset,
|
||||
#[serde(flatten)]
|
||||
pub altair_preset: AltairPreset,
|
||||
// TODO(merge): re-enable
|
||||
// #[serde(flatten)]
|
||||
// pub bellatrix_preset: BellatrixPreset,
|
||||
#[superstruct(only(Bellatrix))]
|
||||
#[serde(flatten)]
|
||||
pub bellatrix_preset: BellatrixPreset,
|
||||
/// The `extra_fields` map allows us to gracefully decode fields intended for future hard forks.
|
||||
#[serde(flatten)]
|
||||
pub extra_fields: HashMap<String, Value>,
|
||||
}
|
||||
|
||||
impl ConfigAndPreset {
|
||||
pub fn from_chain_spec<T: EthSpec>(spec: &ChainSpec) -> Self {
|
||||
pub fn from_chain_spec<T: EthSpec>(spec: &ChainSpec, fork_name: Option<ForkName>) -> Self {
|
||||
let config = Config::from_chain_spec::<T>(spec);
|
||||
let base_preset = BasePreset::from_chain_spec::<T>(spec);
|
||||
let altair_preset = AltairPreset::from_chain_spec::<T>(spec);
|
||||
// TODO(merge): re-enable
|
||||
let _bellatrix_preset = BellatrixPreset::from_chain_spec::<T>(spec);
|
||||
let extra_fields = HashMap::new();
|
||||
let extra_fields = get_extra_fields(spec);
|
||||
|
||||
Self {
|
||||
config,
|
||||
base_preset,
|
||||
altair_preset,
|
||||
extra_fields,
|
||||
if spec.bellatrix_fork_epoch.is_some()
|
||||
|| fork_name == None
|
||||
|| fork_name == Some(ForkName::Merge)
|
||||
{
|
||||
let bellatrix_preset = BellatrixPreset::from_chain_spec::<T>(spec);
|
||||
|
||||
ConfigAndPreset::Bellatrix(ConfigAndPresetBellatrix {
|
||||
config,
|
||||
base_preset,
|
||||
altair_preset,
|
||||
bellatrix_preset,
|
||||
extra_fields,
|
||||
})
|
||||
} else {
|
||||
ConfigAndPreset::Altair(ConfigAndPresetAltair {
|
||||
config,
|
||||
base_preset,
|
||||
altair_preset,
|
||||
extra_fields,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Add fields that were previously part of the config but are now constants.
|
||||
pub fn make_backwards_compat(&mut self, spec: &ChainSpec) {
|
||||
let hex_string = |value: &[u8]| format!("0x{}", hex::encode(&value));
|
||||
let u32_hex = |v: u32| hex_string(&v.to_le_bytes());
|
||||
let u8_hex = |v: u8| hex_string(&v.to_le_bytes());
|
||||
let fields = vec![
|
||||
(
|
||||
"bls_withdrawal_prefix",
|
||||
u8_hex(spec.bls_withdrawal_prefix_byte),
|
||||
),
|
||||
(
|
||||
"domain_beacon_proposer",
|
||||
u32_hex(spec.domain_beacon_proposer),
|
||||
),
|
||||
(
|
||||
"domain_beacon_attester",
|
||||
u32_hex(spec.domain_beacon_attester),
|
||||
),
|
||||
("domain_randao", u32_hex(spec.domain_randao)),
|
||||
("domain_deposit", u32_hex(spec.domain_deposit)),
|
||||
("domain_voluntary_exit", u32_hex(spec.domain_voluntary_exit)),
|
||||
(
|
||||
"domain_selection_proof",
|
||||
u32_hex(spec.domain_selection_proof),
|
||||
),
|
||||
(
|
||||
"domain_aggregate_and_proof",
|
||||
u32_hex(spec.domain_aggregate_and_proof),
|
||||
),
|
||||
(
|
||||
"target_aggregators_per_committee",
|
||||
spec.target_aggregators_per_committee.to_string(),
|
||||
),
|
||||
(
|
||||
"random_subnets_per_validator",
|
||||
spec.random_subnets_per_validator.to_string(),
|
||||
),
|
||||
(
|
||||
"epochs_per_random_subnet_subscription",
|
||||
spec.epochs_per_random_subnet_subscription.to_string(),
|
||||
),
|
||||
];
|
||||
for (key, value) in fields {
|
||||
self.extra_fields.insert(key.to_uppercase(), value.into());
|
||||
}
|
||||
/// Get a hashmap of constants to add to the `PresetAndConfig`
|
||||
pub fn get_extra_fields(spec: &ChainSpec) -> HashMap<String, Value> {
|
||||
let hex_string = |value: &[u8]| format!("0x{}", hex::encode(&value)).into();
|
||||
let u32_hex = |v: u32| hex_string(&v.to_le_bytes());
|
||||
let u8_hex = |v: u8| hex_string(&v.to_le_bytes());
|
||||
hashmap! {
|
||||
"bls_withdrawal_prefix".to_uppercase() => u8_hex(spec.bls_withdrawal_prefix_byte),
|
||||
"domain_beacon_proposer".to_uppercase() => u32_hex(spec.domain_beacon_proposer),
|
||||
"domain_beacon_attester".to_uppercase() => u32_hex(spec.domain_beacon_attester),
|
||||
"domain_randao".to_uppercase()=> u32_hex(spec.domain_randao),
|
||||
"domain_deposit".to_uppercase()=> u32_hex(spec.domain_deposit),
|
||||
"domain_voluntary_exit".to_uppercase() => u32_hex(spec.domain_voluntary_exit),
|
||||
"domain_selection_proof".to_uppercase() => u32_hex(spec.domain_selection_proof),
|
||||
"domain_aggregate_and_proof".to_uppercase() => u32_hex(spec.domain_aggregate_and_proof),
|
||||
"domain_application_mask".to_uppercase()=> u32_hex(spec.domain_application_mask),
|
||||
"target_aggregators_per_committee".to_uppercase() =>
|
||||
spec.target_aggregators_per_committee.to_string().into(),
|
||||
"random_subnets_per_validator".to_uppercase() =>
|
||||
spec.random_subnets_per_validator.to_string().into(),
|
||||
"epochs_per_random_subnet_subscription".to_uppercase() =>
|
||||
spec.epochs_per_random_subnet_subscription.to_string().into(),
|
||||
"domain_contribution_and_proof".to_uppercase() =>
|
||||
u32_hex(spec.domain_contribution_and_proof),
|
||||
"domain_sync_committee".to_uppercase() => u32_hex(spec.domain_sync_committee),
|
||||
"domain_sync_committee_selection_proof".to_uppercase() =>
|
||||
u32_hex(spec.domain_sync_committee_selection_proof),
|
||||
"sync_committee_subnet_count".to_uppercase() =>
|
||||
altair::SYNC_COMMITTEE_SUBNET_COUNT.to_string().into(),
|
||||
"target_aggregators_per_sync_subcommittee".to_uppercase() =>
|
||||
altair::TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE.to_string().into(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,15 +112,16 @@ mod test {
|
||||
.open(tmp_file.as_ref())
|
||||
.expect("error opening file");
|
||||
let mainnet_spec = ChainSpec::mainnet();
|
||||
let mut yamlconfig = ConfigAndPreset::from_chain_spec::<MainnetEthSpec>(&mainnet_spec);
|
||||
let mut yamlconfig =
|
||||
ConfigAndPreset::from_chain_spec::<MainnetEthSpec>(&mainnet_spec, None);
|
||||
let (k1, v1) = ("SAMPLE_HARDFORK_KEY1", "123456789");
|
||||
let (k2, v2) = ("SAMPLE_HARDFORK_KEY2", "987654321");
|
||||
let (k3, v3) = ("SAMPLE_HARDFORK_KEY3", 32);
|
||||
let (k4, v4) = ("SAMPLE_HARDFORK_KEY4", Value::Null);
|
||||
yamlconfig.extra_fields.insert(k1.into(), v1.into());
|
||||
yamlconfig.extra_fields.insert(k2.into(), v2.into());
|
||||
yamlconfig.extra_fields.insert(k3.into(), v3.into());
|
||||
yamlconfig.extra_fields.insert(k4.into(), v4);
|
||||
yamlconfig.extra_fields_mut().insert(k1.into(), v1.into());
|
||||
yamlconfig.extra_fields_mut().insert(k2.into(), v2.into());
|
||||
yamlconfig.extra_fields_mut().insert(k3.into(), v3.into());
|
||||
yamlconfig.extra_fields_mut().insert(k4.into(), v4);
|
||||
|
||||
serde_yaml::to_writer(writer, &yamlconfig).expect("failed to write or serialize");
|
||||
|
||||
@@ -121,8 +130,8 @@ mod test {
|
||||
.write(false)
|
||||
.open(tmp_file.as_ref())
|
||||
.expect("error while opening the file");
|
||||
let from: ConfigAndPreset =
|
||||
let from: ConfigAndPresetBellatrix =
|
||||
serde_yaml::from_reader(reader).expect("error while deserializing");
|
||||
assert_eq!(from, yamlconfig);
|
||||
assert_eq!(ConfigAndPreset::Bellatrix(from), yamlconfig);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
use crate::test_utils::TestRandom;
|
||||
use crate::Hash256;
|
||||
use derivative::Derivative;
|
||||
use rand::RngCore;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz::{Decode, DecodeError, Encode};
|
||||
use std::fmt;
|
||||
|
||||
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize, Eq, PartialEq, Hash)]
|
||||
#[derive(Default, Clone, Copy, Serialize, Deserialize, Eq, PartialEq, Hash, Derivative)]
|
||||
#[derivative(Debug = "transparent")]
|
||||
#[serde(transparent)]
|
||||
pub struct ExecutionBlockHash(Hash256);
|
||||
|
||||
|
||||
@@ -106,14 +106,14 @@ macro_rules! map_fork_name_with {
|
||||
}
|
||||
|
||||
impl FromStr for ForkName {
|
||||
type Err = ();
|
||||
type Err = String;
|
||||
|
||||
fn from_str(fork_name: &str) -> Result<Self, ()> {
|
||||
fn from_str(fork_name: &str) -> Result<Self, String> {
|
||||
Ok(match fork_name.to_lowercase().as_ref() {
|
||||
"phase0" | "base" => ForkName::Base,
|
||||
"altair" => ForkName::Altair,
|
||||
"bellatrix" | "merge" => ForkName::Merge,
|
||||
_ => return Err(()),
|
||||
_ => return Err(format!("unknown fork name: {}", fork_name)),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -138,7 +138,7 @@ impl TryFrom<String> for ForkName {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||
Self::from_str(&s).map_err(|()| format!("Invalid fork name: {}", s))
|
||||
Self::from_str(&s)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,8 +178,8 @@ mod test {
|
||||
assert_eq!(ForkName::from_str("AlTaIr"), Ok(ForkName::Altair));
|
||||
assert_eq!(ForkName::from_str("altair"), Ok(ForkName::Altair));
|
||||
|
||||
assert_eq!(ForkName::from_str("NO_NAME"), Err(()));
|
||||
assert_eq!(ForkName::from_str("no_name"), Err(()));
|
||||
assert!(ForkName::from_str("NO_NAME").is_err());
|
||||
assert!(ForkName::from_str("no_name").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -18,6 +18,7 @@ extern crate lazy_static;
|
||||
pub mod test_utils;
|
||||
|
||||
pub mod aggregate_and_proof;
|
||||
pub mod application_domain;
|
||||
pub mod attestation;
|
||||
pub mod attestation_data;
|
||||
pub mod attestation_duty;
|
||||
@@ -27,6 +28,7 @@ pub mod beacon_block_body;
|
||||
pub mod beacon_block_header;
|
||||
pub mod beacon_committee;
|
||||
pub mod beacon_state;
|
||||
pub mod builder_bid;
|
||||
pub mod chain_spec;
|
||||
pub mod checkpoint;
|
||||
pub mod consts;
|
||||
@@ -81,6 +83,7 @@ pub mod sync_committee_contribution;
|
||||
pub mod sync_committee_message;
|
||||
pub mod sync_selection_proof;
|
||||
pub mod sync_subnet_id;
|
||||
pub mod validator_registration_data;
|
||||
|
||||
pub mod slot_data;
|
||||
#[cfg(feature = "sqlite")]
|
||||
@@ -106,7 +109,9 @@ pub use crate::beacon_committee::{BeaconCommittee, OwnedBeaconCommittee};
|
||||
pub use crate::beacon_state::{Error as BeaconStateError, *};
|
||||
pub use crate::chain_spec::{ChainSpec, Config, Domain};
|
||||
pub use crate::checkpoint::Checkpoint;
|
||||
pub use crate::config_and_preset::ConfigAndPreset;
|
||||
pub use crate::config_and_preset::{
|
||||
ConfigAndPreset, ConfigAndPresetAltair, ConfigAndPresetBellatrix,
|
||||
};
|
||||
pub use crate::contribution_and_proof::ContributionAndProof;
|
||||
pub use crate::deposit::{Deposit, DEPOSIT_TREE_DEPTH};
|
||||
pub use crate::deposit_data::DepositData;
|
||||
@@ -156,6 +161,7 @@ pub use crate::sync_duty::SyncDuty;
|
||||
pub use crate::sync_selection_proof::SyncSelectionProof;
|
||||
pub use crate::sync_subnet_id::SyncSubnetId;
|
||||
pub use crate::validator::{Validator, ValidatorImmutable};
|
||||
pub use crate::validator_registration_data::*;
|
||||
pub use crate::validator_subscription::ValidatorSubscription;
|
||||
pub use crate::voluntary_exit::VoluntaryExit;
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ use std::hash::Hash;
|
||||
use test_random_derive::TestRandom;
|
||||
use tree_hash::TreeHash;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum BlockType {
|
||||
Blinded,
|
||||
Full,
|
||||
@@ -18,6 +19,7 @@ pub trait ExecPayload<T: EthSpec>:
|
||||
Debug
|
||||
+ Clone
|
||||
+ Encode
|
||||
+ Debug
|
||||
+ Decode
|
||||
+ TestRandom
|
||||
+ TreeHash
|
||||
@@ -28,6 +30,8 @@ pub trait ExecPayload<T: EthSpec>:
|
||||
+ Hash
|
||||
+ TryFrom<ExecutionPayloadHeader<T>>
|
||||
+ From<ExecutionPayload<T>>
|
||||
+ Send
|
||||
+ 'static
|
||||
{
|
||||
fn block_type() -> BlockType;
|
||||
|
||||
@@ -42,6 +46,8 @@ pub trait ExecPayload<T: EthSpec>:
|
||||
fn block_number(&self) -> u64;
|
||||
fn timestamp(&self) -> u64;
|
||||
fn block_hash(&self) -> ExecutionBlockHash;
|
||||
fn fee_recipient(&self) -> Address;
|
||||
fn gas_limit(&self) -> u64;
|
||||
}
|
||||
|
||||
impl<T: EthSpec> ExecPayload<T> for FullPayload<T> {
|
||||
@@ -72,6 +78,14 @@ impl<T: EthSpec> ExecPayload<T> for FullPayload<T> {
|
||||
fn block_hash(&self) -> ExecutionBlockHash {
|
||||
self.execution_payload.block_hash
|
||||
}
|
||||
|
||||
fn fee_recipient(&self) -> Address {
|
||||
self.execution_payload.fee_recipient
|
||||
}
|
||||
|
||||
fn gas_limit(&self) -> u64 {
|
||||
self.execution_payload.gas_limit
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EthSpec> ExecPayload<T> for BlindedPayload<T> {
|
||||
@@ -102,6 +116,14 @@ impl<T: EthSpec> ExecPayload<T> for BlindedPayload<T> {
|
||||
fn block_hash(&self) -> ExecutionBlockHash {
|
||||
self.execution_payload_header.block_hash
|
||||
}
|
||||
|
||||
fn fee_recipient(&self) -> Address {
|
||||
self.execution_payload_header.fee_recipient
|
||||
}
|
||||
|
||||
fn gas_limit(&self) -> u64 {
|
||||
self.execution_payload_header.gas_limit
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, TestRandom, Serialize, Deserialize, Derivative)]
|
||||
|
||||
@@ -18,6 +18,13 @@ pub struct ProposerSlashing {
|
||||
pub signed_header_2: SignedBeaconBlockHeader,
|
||||
}
|
||||
|
||||
impl ProposerSlashing {
|
||||
/// Get proposer index, assuming slashing validity has already been checked.
|
||||
pub fn proposer_index(&self) -> u64 {
|
||||
self.signed_header_1.message.proposer_index
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -346,6 +346,14 @@ impl<E: EthSpec> From<SignedBeaconBlock<E>> for SignedBlindedBeaconBlock<E> {
|
||||
}
|
||||
}
|
||||
|
||||
// We can blind borrowed blocks with payloads by converting the payload into a header (without
|
||||
// cloning the payload contents).
|
||||
impl<E: EthSpec> SignedBeaconBlock<E> {
|
||||
pub fn clone_as_blinded(&self) -> SignedBlindedBeaconBlock<E> {
|
||||
SignedBeaconBlock::from_block(self.message().into(), self.signature().clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
@@ -13,8 +13,8 @@ macro_rules! ssz_tests {
|
||||
($type: ty) => {
|
||||
#[test]
|
||||
pub fn test_ssz_round_trip() {
|
||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
use ssz::{ssz_encode, Decode};
|
||||
use $crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([42; 16]);
|
||||
let original = <$type>::random_for_test(&mut rng);
|
||||
@@ -33,8 +33,8 @@ macro_rules! tree_hash_tests {
|
||||
($type: ty) => {
|
||||
#[test]
|
||||
pub fn test_tree_hash_root() {
|
||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
use tree_hash::TreeHash;
|
||||
use $crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([42; 16]);
|
||||
let original = <$type>::random_for_test(&mut rng);
|
||||
|
||||
@@ -129,6 +129,7 @@ macro_rules! impl_test_random_for_u8_array {
|
||||
};
|
||||
}
|
||||
|
||||
impl_test_random_for_u8_array!(3);
|
||||
impl_test_random_for_u8_array!(4);
|
||||
impl_test_random_for_u8_array!(32);
|
||||
impl_test_random_for_u8_array!(48);
|
||||
|
||||
23
consensus/types/src/validator_registration_data.rs
Normal file
23
consensus/types/src/validator_registration_data.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use crate::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use tree_hash_derive::TreeHash;
|
||||
|
||||
/// Validator registration, for use in interacting with servers implementing the builder API.
|
||||
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct SignedValidatorRegistrationData {
|
||||
pub message: ValidatorRegistrationData,
|
||||
pub signature: Signature,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone, Encode, Decode, TreeHash)]
|
||||
pub struct ValidatorRegistrationData {
|
||||
pub fee_recipient: Address,
|
||||
#[serde(with = "eth2_serde_utils::quoted_u64")]
|
||||
pub gas_limit: u64,
|
||||
#[serde(with = "eth2_serde_utils::quoted_u64")]
|
||||
pub timestamp: u64,
|
||||
pub pubkey: PublicKeyBytes,
|
||||
}
|
||||
|
||||
impl SignedRoot for ValidatorRegistrationData {}
|
||||
Reference in New Issue
Block a user