1.1.5 merge spec tests (#2781)

* Fix arbitrary check kintsugi

* Add merge chain spec fields, and a function to determine which constant to use based on the state variant

* increment spec test version

* Remove `Transaction` enum wrapper

* Remove Transaction new-type

* Remove gas validations

* Add `--terminal-block-hash-epoch-override` flag

* Increment spec tests version to 1.1.5

* Remove extraneous gossip verification https://github.com/ethereum/consensus-specs/pull/2687

* - Remove unused Error variants
- Require both "terminal-block-hash-epoch-override" and "terminal-block-hash-override" when either flag is used

* - Remove a couple more unused Error variants

Co-authored-by: Paul Hauner <paul@paulhauner.com>
This commit is contained in:
realbigsean
2021-11-10 11:35:07 -05:00
committed by Paul Hauner
parent cdbe603adf
commit de49c7ddaa
20 changed files with 210 additions and 208 deletions

View File

@@ -0,0 +1,77 @@
//! Serialize `VaraibleList<VariableList<u8, M>, N>` as list of 0x-prefixed hex string.
use crate::VariableList;
use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer};
use std::marker::PhantomData;
use typenum::Unsigned;
#[derive(Deserialize)]
#[serde(transparent)]
pub struct WrappedListOwned<N: Unsigned>(
#[serde(with = "crate::serde_utils::hex_var_list")] VariableList<u8, N>,
);
#[derive(Serialize)]
#[serde(transparent)]
pub struct WrappedListRef<'a, N: Unsigned>(
#[serde(with = "crate::serde_utils::hex_var_list")] &'a VariableList<u8, N>,
);
pub fn serialize<S, M, N>(
list: &VariableList<VariableList<u8, M>, N>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
M: Unsigned,
N: Unsigned,
{
let mut seq = serializer.serialize_seq(Some(list.len()))?;
for bytes in list {
seq.serialize_element(&WrappedListRef(bytes))?;
}
seq.end()
}
#[derive(Default)]
pub struct Visitor<M, N> {
_phantom_m: PhantomData<M>,
_phantom_n: PhantomData<N>,
}
impl<'a, M, N> serde::de::Visitor<'a> for Visitor<M, N>
where
M: Unsigned,
N: Unsigned,
{
type Value = VariableList<VariableList<u8, M>, N>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "a list of 0x-prefixed hex bytes")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'a>,
{
let mut list: VariableList<VariableList<u8, M>, N> = <_>::default();
while let Some(val) = seq.next_element::<WrappedListOwned<M>>()? {
list.push(val.0).map_err(|e| {
serde::de::Error::custom(format!("failed to push value to list: {:?}.", e))
})?;
}
Ok(list)
}
}
pub fn deserialize<'de, D, M, N>(
deserializer: D,
) -> Result<VariableList<VariableList<u8, M>, N>, D::Error>
where
D: Deserializer<'de>,
M: Unsigned,
N: Unsigned,
{
deserializer.deserialize_seq(Visitor::default())
}

View File

@@ -1,4 +1,5 @@
pub mod hex_fixed_vec;
pub mod hex_var_list;
pub mod list_of_hex_var_list;
pub mod quoted_u64_fixed_vec;
pub mod quoted_u64_var_list;

View File

@@ -31,14 +31,11 @@ pub fn slash_validator<T: EthSpec>(
.safe_add(validator_effective_balance)?,
)?;
let min_slashing_penalty_quotient = match state {
BeaconState::Base(_) => spec.min_slashing_penalty_quotient,
BeaconState::Altair(_) | BeaconState::Merge(_) => spec.min_slashing_penalty_quotient_altair,
};
decrease_balance(
state,
slashed_index,
validator_effective_balance.safe_div(min_slashing_penalty_quotient)?,
validator_effective_balance
.safe_div(spec.min_slashing_penalty_quotient_for_state(state))?,
)?;
// Apply proposer and whistleblower rewards

View File

@@ -295,50 +295,6 @@ pub fn get_new_eth1_data<T: EthSpec>(
}
}
/// https://github.com/ethereum/consensus-specs/blob/dev/specs/merge/beacon-chain.md#is_valid_gas_limit
pub fn verify_is_valid_gas_limit<T: EthSpec>(
payload: &ExecutionPayload<T>,
parent: &ExecutionPayloadHeader<T>,
) -> Result<(), BlockProcessingError> {
// check if payload used too much gas
if payload.gas_used > payload.gas_limit {
return Err(BlockProcessingError::ExecutionInvalidGasLimit {
used: payload.gas_used,
limit: payload.gas_limit,
});
}
// check if payload changed the gas limit too much
if payload.gas_limit
>= parent
.gas_limit
.safe_add(parent.gas_limit.safe_div(T::gas_limit_denominator())?)?
{
return Err(BlockProcessingError::ExecutionInvalidGasLimitIncrease {
limit: payload.gas_limit,
parent_limit: parent.gas_limit,
});
}
if payload.gas_limit
<= parent
.gas_limit
.safe_sub(parent.gas_limit.safe_div(T::gas_limit_denominator())?)?
{
return Err(BlockProcessingError::ExecutionInvalidGasLimitDecrease {
limit: payload.gas_limit,
parent_limit: parent.gas_limit,
});
}
// check if the gas limit is at least the minimum gas limit
if payload.gas_limit < T::min_gas_limit() {
return Err(BlockProcessingError::ExecutionInvalidGasLimitTooSmall {
limit: payload.gas_limit,
min: T::min_gas_limit(),
});
}
Ok(())
}
/// https://github.com/ethereum/consensus-specs/blob/dev/specs/merge/beacon-chain.md#process_execution_payload
pub fn process_execution_payload<T: EthSpec>(
state: &mut BeaconState<T>,
@@ -353,21 +309,6 @@ pub fn process_execution_payload<T: EthSpec>(
found: payload.parent_hash,
}
);
block_verify!(
payload.block_number
== state
.latest_execution_payload_header()?
.block_number
.safe_add(1)?,
BlockProcessingError::ExecutionBlockNumberIncontiguous {
expected: state
.latest_execution_payload_header()?
.block_number
.safe_add(1)?,
found: payload.block_number,
}
);
verify_is_valid_gas_limit(payload, state.latest_execution_payload_header()?)?;
}
block_verify!(
payload.random == *state.get_randao_mix(state.current_epoch())?,

View File

@@ -61,30 +61,10 @@ pub enum BlockProcessingError {
expected: Hash256,
found: Hash256,
},
ExecutionBlockNumberIncontiguous {
expected: u64,
found: u64,
},
ExecutionRandaoMismatch {
expected: Hash256,
found: Hash256,
},
ExecutionInvalidGasLimit {
used: u64,
limit: u64,
},
ExecutionInvalidGasLimitIncrease {
limit: u64,
parent_limit: u64,
},
ExecutionInvalidGasLimitDecrease {
limit: u64,
parent_limit: u64,
},
ExecutionInvalidGasLimitTooSmall {
limit: u64,
min: u64,
},
ExecutionInvalidTimestamp {
expected: u64,
found: u64,

View File

@@ -47,7 +47,6 @@ pub fn process_epoch<T: EthSpec>(
process_slashings(
state,
participation_cache.current_epoch_total_active_balance(),
spec.proportional_slashing_multiplier_altair,
spec,
)?;

View File

@@ -119,7 +119,7 @@ pub fn get_inactivity_penalty_deltas<T: EthSpec>(
.safe_mul(state.get_inactivity_score(index)?)?;
let penalty_denominator = spec
.inactivity_score_bias
.safe_mul(spec.inactivity_penalty_quotient_altair)?;
.safe_mul(spec.inactivity_penalty_quotient_for_state(state))?;
delta.penalize(penalty_numerator.safe_div(penalty_denominator)?)?;
}
deltas

View File

@@ -43,7 +43,6 @@ pub fn process_epoch<T: EthSpec>(
process_slashings(
state,
validator_statuses.total_balances.current_epoch(),
spec.proportional_slashing_multiplier,
spec,
)?;

View File

@@ -6,14 +6,15 @@ use types::{BeaconState, BeaconStateError, ChainSpec, EthSpec, Unsigned};
pub fn process_slashings<T: EthSpec>(
state: &mut BeaconState<T>,
total_balance: u64,
slashing_multiplier: u64,
spec: &ChainSpec,
) -> Result<(), Error> {
let epoch = state.current_epoch();
let sum_slashings = state.get_all_slashings().iter().copied().safe_sum()?;
let adjusted_total_slashing_balance =
std::cmp::min(sum_slashings.safe_mul(slashing_multiplier)?, total_balance);
let adjusted_total_slashing_balance = std::cmp::min(
sum_slashings.safe_mul(spec.proportional_slashing_multiplier_for_state(state))?,
total_balance,
);
let (validators, balances) = state.validators_and_balances_mut();
for (index, validator) in validators.iter().enumerate() {

View File

@@ -127,11 +127,19 @@ pub struct ChainSpec {
pub altair_fork_version: [u8; 4],
/// The Altair fork epoch is optional, with `None` representing "Altair never happens".
pub altair_fork_epoch: Option<Epoch>,
/*
* Merge hard fork params
*/
pub inactivity_penalty_quotient_merge: u64,
pub min_slashing_penalty_quotient_merge: u64,
pub proportional_slashing_multiplier_merge: u64,
pub merge_fork_version: [u8; 4],
/// The Merge fork epoch is optional, with `None` representing "Merge never happens".
pub merge_fork_epoch: Option<Epoch>,
pub terminal_total_difficulty: Uint256,
pub terminal_block_hash: Hash256,
pub terminal_block_hash_activation_epoch: Epoch,
/*
* Networking
@@ -235,6 +243,39 @@ impl ChainSpec {
}
}
/// For a given `BeaconState`, return the inactivity penalty quotient associated with its variant.
pub fn inactivity_penalty_quotient_for_state<T: EthSpec>(&self, state: &BeaconState<T>) -> u64 {
match state {
BeaconState::Base(_) => self.inactivity_penalty_quotient,
BeaconState::Altair(_) => self.inactivity_penalty_quotient_altair,
BeaconState::Merge(_) => self.inactivity_penalty_quotient_merge,
}
}
/// For a given `BeaconState`, return the proportional slashing multiplier associated with its variant.
pub fn proportional_slashing_multiplier_for_state<T: EthSpec>(
&self,
state: &BeaconState<T>,
) -> u64 {
match state {
BeaconState::Base(_) => self.proportional_slashing_multiplier,
BeaconState::Altair(_) => self.proportional_slashing_multiplier_altair,
BeaconState::Merge(_) => self.proportional_slashing_multiplier_merge,
}
}
/// For a given `BeaconState`, return the minimum slashing penalty quotient associated with its variant.
pub fn min_slashing_penalty_quotient_for_state<T: EthSpec>(
&self,
state: &BeaconState<T>,
) -> u64 {
match state {
BeaconState::Base(_) => self.min_slashing_penalty_quotient,
BeaconState::Altair(_) => self.min_slashing_penalty_quotient_altair,
BeaconState::Merge(_) => self.min_slashing_penalty_quotient_merge,
}
}
/// Returns a full `Fork` struct for a given epoch.
pub fn fork_at_epoch(&self, epoch: Epoch) -> Fork {
let current_fork_name = self.fork_name_at_epoch(epoch);
@@ -367,7 +408,7 @@ impl ChainSpec {
* Constants
*/
genesis_slot: Slot::new(0),
far_future_epoch: Epoch::new(u64::max_value()),
far_future_epoch: Epoch::new(u64::MAX),
base_rewards_per_epoch: 4,
deposit_contract_tree_depth: 32,
@@ -479,12 +520,22 @@ impl ChainSpec {
domain_contribution_and_proof: 9,
altair_fork_version: [0x01, 0x00, 0x00, 0x00],
altair_fork_epoch: Some(Epoch::new(74240)),
/*
* Merge hard fork params
*/
inactivity_penalty_quotient_merge: u64::checked_pow(2, 24)
.expect("pow does not overflow"),
min_slashing_penalty_quotient_merge: u64::checked_pow(2, 5)
.expect("pow does not overflow"),
proportional_slashing_multiplier_merge: 3,
merge_fork_version: [0x02, 0x00, 0x00, 0x00],
merge_fork_epoch: None,
terminal_total_difficulty: Uint256::MAX
.checked_sub(Uint256::from(2u64.pow(10)))
.expect("calculation does not overflow"),
terminal_block_hash: Hash256::zero(),
terminal_block_hash_activation_epoch: Epoch::new(u64::MAX),
/*
* Network specific

View File

@@ -3,13 +3,13 @@ use crate::*;
use safe_arith::SafeArith;
use serde_derive::{Deserialize, Serialize};
use ssz_types::typenum::{
Unsigned, U0, U1024, U1099511627776, U128, U16, U16777216, U2, U2048, U32, U4, U4096, U512,
U64, U65536, U8, U8192,
Unsigned, U0, U1024, U1073741824, U1099511627776, U128, U16, U16777216, U2, U2048, U32, U4,
U4096, U512, U64, U65536, U8, U8192,
};
use std::fmt::{self, Debug};
use std::str::FromStr;
use ssz_types::typenum::{bit::B0, UInt, U1048576, U16384, U256, U625};
use ssz_types::typenum::{bit::B0, UInt, U1048576, U256, U625};
pub type U5000 = UInt<UInt<UInt<U625, B0>, B0>, B0>; // 625 * 8 = 5000
const MAINNET: &str = "mainnet";
@@ -86,7 +86,7 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq +
/*
* New in Merge
*/
type MaxBytesPerOpaqueTransaction: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type MaxBytesPerTransaction: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type MaxTransactionsPerPayload: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type BytesPerLogsBloom: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type GasLimitDenominator: Unsigned + Clone + Sync + Send + Debug + PartialEq;
@@ -200,9 +200,9 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq +
Self::SyncSubcommitteeSize::to_usize()
}
/// Returns the `MAX_BYTES_PER_OPAQUE_TRANSACTION` constant for this specification.
fn max_bytes_per_opaque_transaction() -> usize {
Self::MaxBytesPerOpaqueTransaction::to_usize()
/// Returns the `MAX_BYTES_PER_TRANSACTION` constant for this specification.
fn max_bytes_per_transaction() -> usize {
Self::MaxBytesPerTransaction::to_usize()
}
/// Returns the `MAX_TRANSACTIONS_PER_PAYLOAD` constant for this specification.
@@ -214,16 +214,6 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq +
fn bytes_per_logs_bloom() -> usize {
Self::BytesPerLogsBloom::to_usize()
}
/// Returns the `GAS_LIMIT_DENOMINATOR` constant for this specification.
fn gas_limit_denominator() -> u64 {
Self::GasLimitDenominator::to_u64()
}
/// Returns the `MIN_GAS_LIMIT` constant for this specification.
fn min_gas_limit() -> u64 {
Self::MinGasLimit::to_u64()
}
}
/// Macro to inherit some type values from another EthSpec.
@@ -258,8 +248,8 @@ impl EthSpec for MainnetEthSpec {
type MaxVoluntaryExits = U16;
type SyncCommitteeSize = U512;
type SyncCommitteeSubnetCount = U4;
type MaxBytesPerOpaqueTransaction = U1048576;
type MaxTransactionsPerPayload = U16384;
type MaxBytesPerTransaction = U1073741824; // 1,073,741,824
type MaxTransactionsPerPayload = U1048576; // 1,048,576
type BytesPerLogsBloom = U256;
type GasLimitDenominator = U1024;
type MinGasLimit = U5000;
@@ -306,7 +296,7 @@ impl EthSpec for MinimalEthSpec {
MaxAttestations,
MaxDeposits,
MaxVoluntaryExits,
MaxBytesPerOpaqueTransaction,
MaxBytesPerTransaction,
MaxTransactionsPerPayload,
BytesPerLogsBloom,
GasLimitDenominator,

View File

@@ -1,41 +1,10 @@
use crate::{test_utils::TestRandom, *};
use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use std::{ops::Index, slice::SliceIndex};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash)]
#[ssz(enum_behaviour = "union")]
#[tree_hash(enum_behaviour = "union")]
#[serde(tag = "selector", content = "value")]
#[serde(bound = "T: EthSpec")]
pub enum Transaction<T: EthSpec> {
// FIXME(merge): renaming this enum variant to 0 is a bit of a hack...
#[serde(rename = "0")]
OpaqueTransaction(
#[serde(with = "ssz_types::serde_utils::hex_var_list")]
VariableList<u8, T::MaxBytesPerOpaqueTransaction>,
),
}
impl<T: EthSpec, I: SliceIndex<[u8]>> Index<I> for Transaction<T> {
type Output = I::Output;
#[inline]
fn index(&self, index: I) -> &Self::Output {
match self {
Self::OpaqueTransaction(v) => Index::index(v, index),
}
}
}
impl<T: EthSpec> From<VariableList<u8, T::MaxBytesPerOpaqueTransaction>> for Transaction<T> {
fn from(list: VariableList<u8, <T as EthSpec>::MaxBytesPerOpaqueTransaction>) -> Self {
Self::OpaqueTransaction(list)
}
}
pub type Transaction<T> = VariableList<u8, T>;
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
@@ -62,8 +31,9 @@ pub struct ExecutionPayload<T: EthSpec> {
pub extra_data: VariableList<u8, T::MaxExtraDataBytes>,
pub base_fee_per_gas: Hash256,
pub block_hash: Hash256,
#[test_random(default)]
pub transactions: VariableList<Transaction<T>, T::MaxTransactionsPerPayload>,
#[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")]
pub transactions:
VariableList<Transaction<T::MaxBytesPerTransaction>, T::MaxTransactionsPerPayload>,
}
impl<T: EthSpec> ExecutionPayload<T> {