Fix consensus, SSZ, tree hash & run merge EF tests (#2622)

* Update to v1.1.0-beta.4 (squash of #2548)

* SSZ, cached tree hash, EF tests
This commit is contained in:
Michael Sproul
2021-09-24 14:55:21 +10:00
committed by Paul Hauner
parent 5687c56d51
commit cce855f9ea
21 changed files with 282 additions and 186 deletions

View File

@@ -101,9 +101,13 @@ impl<T: EthSpec> BeaconBlock<T> {
/// Usually it's better to prefer `from_ssz_bytes` which will decode the correct variant based
/// on the fork slot.
pub fn any_from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
BeaconBlockAltair::from_ssz_bytes(bytes)
.map(BeaconBlock::Altair)
.or_else(|_| BeaconBlockBase::from_ssz_bytes(bytes).map(BeaconBlock::Base))
BeaconBlockMerge::from_ssz_bytes(bytes)
.map(BeaconBlock::Merge)
.or_else(|_| {
BeaconBlockAltair::from_ssz_bytes(bytes)
.map(BeaconBlock::Altair)
.or_else(|_| BeaconBlockBase::from_ssz_bytes(bytes).map(BeaconBlock::Base))
})
}
/// Convenience accessor for the `body` as a `BeaconBlockBodyRef`.

View File

@@ -1684,7 +1684,8 @@ impl<T: EthSpec> CompareFields for BeaconState<T> {
match (self, other) {
(BeaconState::Base(x), BeaconState::Base(y)) => x.compare_fields(y),
(BeaconState::Altair(x), BeaconState::Altair(y)) => x.compare_fields(y),
_ => panic!("compare_fields: mismatched state variants"),
(BeaconState::Merge(x), BeaconState::Merge(y)) => x.compare_fields(y),
_ => panic!("compare_fields: mismatched state variants",),
}
}
}

View File

@@ -341,16 +341,26 @@ impl<T: EthSpec> BeaconTreeHashCacheInner<T> {
)?;
hasher.write(state.finalized_checkpoint().tree_hash_root().as_bytes())?;
// Inactivity & light-client sync committees
if let BeaconState::Altair(ref state) = state {
// Inactivity & light-client sync committees (Altair and later).
if let Ok(inactivity_scores) = state.inactivity_scores() {
hasher.write(
self.inactivity_scores
.recalculate_tree_hash_root(&state.inactivity_scores)?
.recalculate_tree_hash_root(inactivity_scores)?
.as_bytes(),
)?;
}
hasher.write(state.current_sync_committee.tree_hash_root().as_bytes())?;
hasher.write(state.next_sync_committee.tree_hash_root().as_bytes())?;
if let Ok(current_sync_committee) = state.current_sync_committee() {
hasher.write(current_sync_committee.tree_hash_root().as_bytes())?;
}
if let Ok(next_sync_committee) = state.next_sync_committee() {
hasher.write(next_sync_committee.tree_hash_root().as_bytes())?;
}
// Execution payload (merge and later).
if let Ok(payload_header) = state.latest_execution_payload_header() {
hasher.write(payload_header.tree_hash_root().as_bytes())?;
}
let root = hasher.finish()?;

View File

@@ -8,8 +8,15 @@ use tree_hash_derive::TreeHash;
#[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> {
OpaqueTransaction(VariableList<u8, T::MaxBytesPerOpaqueTransaction>),
// 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> {
@@ -33,12 +40,13 @@ impl<T: EthSpec> From<VariableList<u8, T::MaxBytesPerOpaqueTransaction>> for Tra
#[derive(
Default, Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
)]
#[serde(bound = "T: EthSpec")]
pub struct ExecutionPayload<T: EthSpec> {
pub parent_hash: Hash256,
pub coinbase: Address,
pub state_root: Hash256,
pub receipt_root: Hash256,
#[serde(with = "serde_logs_bloom")]
#[serde(with = "ssz_types::serde_utils::hex_fixed_vec")]
pub logs_bloom: FixedVector<u8, T::BytesPerLogsBloom>,
pub random: Hash256,
#[serde(with = "eth2_serde_utils::quoted_u64")]
@@ -51,7 +59,6 @@ pub struct ExecutionPayload<T: EthSpec> {
pub timestamp: u64,
pub base_fee_per_gas: Hash256,
pub block_hash: Hash256,
#[serde(with = "serde_transactions")]
#[test_random(default)]
pub transactions: VariableList<Transaction<T>, T::MaxTransactionsPerPayload>,
}
@@ -76,99 +83,3 @@ impl<T: EthSpec> ExecutionPayload<T> {
}
}
}
/// Serializes the `logs_bloom` field.
pub mod serde_logs_bloom {
use super::*;
use eth2_serde_utils::hex::PrefixedHexVisitor;
use serde::{Deserializer, Serializer};
pub fn serialize<S, U>(bytes: &FixedVector<u8, U>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
U: Unsigned,
{
let mut hex_string: String = "0x".to_string();
hex_string.push_str(&hex::encode(&bytes[..]));
serializer.serialize_str(&hex_string)
}
pub fn deserialize<'de, D, U>(deserializer: D) -> Result<FixedVector<u8, U>, D::Error>
where
D: Deserializer<'de>,
U: Unsigned,
{
let vec = deserializer.deserialize_string(PrefixedHexVisitor)?;
FixedVector::new(vec)
.map_err(|e| serde::de::Error::custom(format!("invalid logs bloom: {:?}", e)))
}
}
/// Serializes the `transactions` field.
pub mod serde_transactions {
use super::*;
use eth2_serde_utils::hex;
use serde::ser::SerializeSeq;
use serde::{de, Deserializer, Serializer};
use std::marker::PhantomData;
pub struct ListOfBytesListVisitor<T: EthSpec> {
_t: PhantomData<T>,
}
impl<'a, T> serde::de::Visitor<'a> for ListOfBytesListVisitor<T>
where
T: EthSpec,
{
type Value = VariableList<Transaction<T>, T::MaxTransactionsPerPayload>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "a list of 0x-prefixed byte lists")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'a>,
{
let mut outer = VariableList::default();
while let Some(val) = seq.next_element::<String>()? {
let inner_vec = hex::decode(&val).map_err(de::Error::custom)?;
let inner = VariableList::new(inner_vec).map_err(|e| {
serde::de::Error::custom(format!("invalid transaction: {:?}", e))
})?;
outer.push(inner.into()).map_err(|e| {
serde::de::Error::custom(format!("too many transactions: {:?}", e))
})?;
}
Ok(outer)
}
}
pub fn serialize<S, T>(
value: &VariableList<Transaction<T>, T::MaxTransactionsPerPayload>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: EthSpec,
{
let mut seq = serializer.serialize_seq(Some(value.len()))?;
for val in value {
seq.serialize_element(&hex::encode(&val[..]))?;
}
seq.end()
}
pub fn deserialize<'de, D, T>(
deserializer: D,
) -> Result<VariableList<Transaction<T>, T::MaxTransactionsPerPayload>, D::Error>
where
D: Deserializer<'de>,
T: EthSpec,
{
deserializer.deserialize_any(ListOfBytesListVisitor { _t: PhantomData })
}
}

View File

@@ -1,4 +1,4 @@
use crate::{execution_payload::serde_logs_bloom, test_utils::TestRandom, *};
use crate::{test_utils::TestRandom, *};
use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
@@ -13,7 +13,7 @@ pub struct ExecutionPayloadHeader<T: EthSpec> {
pub coinbase: Address,
pub state_root: Hash256,
pub receipt_root: Hash256,
#[serde(with = "serde_logs_bloom")]
#[serde(with = "ssz_types::serde_utils::hex_fixed_vec")]
pub logs_bloom: FixedVector<u8, T::BytesPerLogsBloom>,
pub random: Hash256,
#[serde(with = "eth2_serde_utils::quoted_u64")]

View File

@@ -15,7 +15,7 @@ pub enum ForkName {
impl ForkName {
pub fn list_all() -> Vec<ForkName> {
vec![ForkName::Base, ForkName::Altair]
vec![ForkName::Base, ForkName::Altair, ForkName::Merge]
}
/// Set the activation slots in the given `ChainSpec` so that the fork named by `self`