Merge branch 'electra-epoch-proc' of https://github.com/sigp/lighthouse into electra-engine-api

This commit is contained in:
realbigsean
2024-05-31 08:52:37 -04:00
194 changed files with 5822 additions and 3879 deletions

View File

@@ -51,7 +51,6 @@ metastruct = "0.1.0"
serde_json = { workspace = true }
smallvec = { workspace = true }
maplit = { workspace = true }
strum = { workspace = true }
milhouse = { workspace = true }
rpds = { workspace = true }

View File

@@ -4,7 +4,6 @@ use derivative::Derivative;
use rand::RngCore;
use safe_arith::ArithError;
use serde::{Deserialize, Serialize};
use ssz::Decode;
use ssz_derive::{Decode, Encode};
use ssz_types::BitVector;
use std::hash::{Hash, Hasher};
@@ -22,6 +21,7 @@ pub enum Error {
SszTypesError(ssz_types::Error),
AlreadySigned(usize),
SubnetCountIsZero(ArithError),
IncorrectStateVariant,
}
#[superstruct(
@@ -43,7 +43,9 @@ pub enum Error {
serde(bound = "E: EthSpec", deny_unknown_fields),
arbitrary(bound = "E: EthSpec"),
),
ref_attributes(derive(TreeHash), tree_hash(enum_behaviour = "transparent"))
ref_attributes(derive(TreeHash), tree_hash(enum_behaviour = "transparent")),
cast_error(ty = "Error", expr = "Error::IncorrectStateVariant"),
partial_getter_error(ty = "Error", expr = "Error::IncorrectStateVariant")
)]
#[derive(
Debug,
@@ -72,37 +74,17 @@ pub struct Attestation<E: EthSpec> {
pub signature: AggregateSignature,
}
impl<E: EthSpec> Decode for Attestation<E> {
fn is_ssz_fixed_len() -> bool {
false
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
if let Ok(result) = AttestationBase::from_ssz_bytes(bytes) {
return Ok(Attestation::Base(result));
}
if let Ok(result) = AttestationElectra::from_ssz_bytes(bytes) {
return Ok(Attestation::Electra(result));
}
Err(ssz::DecodeError::BytesInvalid(String::from(
"bytes not valid for any fork variant",
)))
}
}
// TODO(electra): think about how to handle fork variants here
impl<E: EthSpec> TestRandom for Attestation<E> {
fn random_for_test(rng: &mut impl RngCore) -> Self {
let aggregation_bits: BitList<E::MaxValidatorsPerCommittee> = BitList::random_for_test(rng);
// let committee_bits: BitList<E::MaxCommitteesPerSlot> = BitList::random_for_test(rng);
let aggregation_bits = BitList::random_for_test(rng);
let data = AttestationData::random_for_test(rng);
let signature = AggregateSignature::random_for_test(rng);
let committee_bits = BitVector::random_for_test(rng);
Self::Base(AttestationBase {
Self::Electra(AttestationElectra {
aggregation_bits,
// committee_bits,
committee_bits,
data,
signature,
})
@@ -187,9 +169,9 @@ impl<E: EthSpec> Attestation<E> {
}
}
pub fn committee_index(&self) -> u64 {
pub fn committee_index(&self) -> Option<u64> {
match self {
Attestation::Base(att) => att.data.index,
Attestation::Base(att) => Some(att.data.index),
Attestation::Electra(att) => att.committee_index(),
}
}
@@ -238,12 +220,31 @@ impl<'a, E: EthSpec> AttestationRef<'a, E> {
}
}
pub fn committee_index(&self) -> u64 {
pub fn committee_index(&self) -> Option<u64> {
match self {
AttestationRef::Base(att) => att.data.index,
AttestationRef::Base(att) => Some(att.data.index),
AttestationRef::Electra(att) => att.committee_index(),
}
}
pub fn set_aggregation_bits(&self) -> Vec<usize> {
match self {
Self::Base(att) => att
.aggregation_bits
.iter()
.enumerate()
.filter(|(_i, bit)| *bit)
.map(|(i, _bit)| i)
.collect::<Vec<_>>(),
Self::Electra(att) => att
.aggregation_bits
.iter()
.enumerate()
.filter(|(_i, bit)| *bit)
.map(|(i, _bit)| i)
.collect::<Vec<_>>(),
}
}
}
impl<E: EthSpec> AttestationElectra<E> {
@@ -257,8 +258,8 @@ impl<E: EthSpec> AttestationElectra<E> {
.is_zero()
}
pub fn committee_index(&self) -> u64 {
*self.get_committee_indices().first().unwrap_or(&0u64)
pub fn committee_index(&self) -> Option<u64> {
self.get_committee_indices().first().cloned()
}
pub fn get_committee_indices(&self) -> Vec<u64> {
@@ -417,6 +418,65 @@ impl<'a, E: EthSpec> SlotData for AttestationRef<'a, E> {
}
}
#[derive(Debug, Clone, Encode, Decode, PartialEq)]
#[ssz(enum_behaviour = "union")]
pub enum AttestationOnDisk<E: EthSpec> {
Base(AttestationBase<E>),
Electra(AttestationElectra<E>),
}
impl<E: EthSpec> AttestationOnDisk<E> {
pub fn to_ref(&self) -> AttestationRefOnDisk<E> {
match self {
AttestationOnDisk::Base(att) => AttestationRefOnDisk::Base(att),
AttestationOnDisk::Electra(att) => AttestationRefOnDisk::Electra(att),
}
}
}
#[derive(Debug, Clone, Encode)]
#[ssz(enum_behaviour = "union")]
pub enum AttestationRefOnDisk<'a, E: EthSpec> {
Base(&'a AttestationBase<E>),
Electra(&'a AttestationElectra<E>),
}
impl<E: EthSpec> From<Attestation<E>> for AttestationOnDisk<E> {
fn from(attestation: Attestation<E>) -> Self {
match attestation {
Attestation::Base(attestation) => Self::Base(attestation),
Attestation::Electra(attestation) => Self::Electra(attestation),
}
}
}
impl<E: EthSpec> From<AttestationOnDisk<E>> for Attestation<E> {
fn from(attestation: AttestationOnDisk<E>) -> Self {
match attestation {
AttestationOnDisk::Base(attestation) => Self::Base(attestation),
AttestationOnDisk::Electra(attestation) => Self::Electra(attestation),
}
}
}
impl<'a, E: EthSpec> From<AttestationRef<'a, E>> for AttestationRefOnDisk<'a, E> {
fn from(attestation: AttestationRef<'a, E>) -> Self {
match attestation {
AttestationRef::Base(attestation) => Self::Base(attestation),
AttestationRef::Electra(attestation) => Self::Electra(attestation),
}
}
}
impl<'a, E: EthSpec> From<AttestationRefOnDisk<'a, E>> for AttestationRef<'a, E> {
fn from(attestation: AttestationRefOnDisk<'a, E>) -> Self {
match attestation {
AttestationRefOnDisk::Base(attestation) => Self::Base(attestation),
AttestationRefOnDisk::Electra(attestation) => Self::Electra(attestation),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
@@ -428,7 +488,7 @@ mod tests {
// This test will only pass with `blst`, if we run these tests with another
// BLS library in future we will have to make it generic.
#[test]
fn size_of() {
fn size_of_base() {
use std::mem::size_of;
let aggregation_bits =
@@ -441,16 +501,43 @@ mod tests {
assert_eq!(signature, 288 + 16);
let attestation_expected = aggregation_bits + attestation_data + signature;
// TODO(electra) since we've removed attestation aggregation for electra variant
// i've updated the attestation value expected from 488 544
// assert_eq!(attestation_expected, 488);
assert_eq!(attestation_expected, 488);
assert_eq!(
size_of::<Attestation<MainnetEthSpec>>(),
size_of::<AttestationBase<MainnetEthSpec>>(),
attestation_expected
);
}
// TODO(electra): can we do this with both variants or should we?
ssz_and_tree_hash_tests!(AttestationBase<MainnetEthSpec>);
#[test]
fn size_of_electra() {
use std::mem::size_of;
let aggregation_bits =
size_of::<BitList<<MainnetEthSpec as EthSpec>::MaxValidatorsPerSlot>>();
let attestation_data = size_of::<AttestationData>();
let committee_bits =
size_of::<BitList<<MainnetEthSpec as EthSpec>::MaxCommitteesPerSlot>>();
let signature = size_of::<AggregateSignature>();
assert_eq!(aggregation_bits, 56);
assert_eq!(committee_bits, 56);
assert_eq!(attestation_data, 128);
assert_eq!(signature, 288 + 16);
let attestation_expected = aggregation_bits + committee_bits + attestation_data + signature;
assert_eq!(attestation_expected, 544);
assert_eq!(
size_of::<AttestationElectra<MainnetEthSpec>>(),
attestation_expected
);
}
mod base {
use super::*;
ssz_and_tree_hash_tests!(AttestationBase<MainnetEthSpec>);
}
mod electra {
use super::*;
ssz_and_tree_hash_tests!(AttestationElectra<MainnetEthSpec>);
}
}

View File

@@ -164,7 +164,12 @@ impl<E: EthSpec> AttesterSlashing<E> {
mod tests {
use super::*;
use crate::*;
// TODO(electra): should this be done for both variants?
ssz_and_tree_hash_tests!(AttesterSlashingBase<MainnetEthSpec>);
mod base {
use super::*;
ssz_and_tree_hash_tests!(AttesterSlashingBase<MainnetEthSpec>);
}
mod electra {
use super::*;
ssz_and_tree_hash_tests!(AttesterSlashingElectra<MainnetEthSpec>);
}
}

View File

@@ -607,14 +607,12 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockElectra<E, Payload>
/// Return a Electra block where the block has maximum size.
pub fn full(spec: &ChainSpec) -> Self {
let base_block: BeaconBlockBase<_, Payload> = BeaconBlockBase::full(spec);
// TODO(electra): check this
let indexed_attestation: IndexedAttestationElectra<E> = IndexedAttestationElectra {
attesting_indices: VariableList::new(vec![0_u64; E::MaxValidatorsPerSlot::to_usize()])
.unwrap(),
data: AttestationData::default(),
signature: AggregateSignature::empty(),
};
// TODO(electra): fix this so we calculate this size correctly
let attester_slashings = vec![
AttesterSlashingElectra {
attestation_1: indexed_attestation.clone(),
@@ -627,7 +625,6 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockElectra<E, Payload>
aggregation_bits: BitList::with_capacity(E::MaxValidatorsPerSlot::to_usize()).unwrap(),
data: AttestationData::default(),
signature: AggregateSignature::empty(),
// TODO(electra): does this actually allocate the size correctly?
committee_bits: BitVector::new(),
};
let mut attestations_electra = vec![];

View File

@@ -251,6 +251,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBodyRef<'a, E,
body.execution_payload.tree_hash_root(),
body.bls_to_execution_changes.tree_hash_root(),
body.blob_kzg_commitments.tree_hash_root(),
body.consolidations.tree_hash_root(),
];
let beacon_block_body_depth = leaves.len().next_power_of_two().ilog2() as usize;
let tree = MerkleTree::create(&leaves, beacon_block_body_depth);

View File

@@ -163,6 +163,12 @@ pub enum Error {
NonExecutionAddresWithdrawalCredential,
NoCommitteeFound(CommitteeIndex),
InvalidCommitteeIndex(CommitteeIndex),
InvalidSelectionProof {
aggregator_index: u64,
},
AggregatorNotInCommittee {
aggregator_index: u64,
},
}
/// Control whether an epoch-indexed field can be indexed at the next epoch or not.

View File

@@ -206,14 +206,17 @@ impl<E: EthSpec> Decode for IndexedAttestation<E> {
}
}
// TODO(electra): think about how to handle fork variants here
impl<E: EthSpec> TestRandom for IndexedAttestation<E> {
fn random_for_test(rng: &mut impl RngCore) -> Self {
let attesting_indices = VariableList::random_for_test(rng);
// let committee_bits: BitList<E::MaxCommitteesPerSlot> = BitList::random_for_test(rng);
let data = AttestationData::random_for_test(rng);
let signature = AggregateSignature::random_for_test(rng);
Self::Base(IndexedAttestationBase {
attesting_indices,
// committee_bits,
data,
signature,
})

View File

@@ -74,7 +74,6 @@ impl<E: EthSpec> SignedAggregateAndProof<E> {
genesis_validators_root,
spec,
);
let target_epoch = message.aggregate().data().slot.epoch(E::slots_per_epoch());
let domain = spec.get_domain(
target_epoch,

View File

@@ -40,13 +40,15 @@ impl SubnetId {
/// Compute the subnet for an attestation where each slot in the
/// attestation epoch contains `committee_count_per_slot` committees.
pub fn compute_subnet_for_attestation<E: EthSpec>(
attestation: &AttestationRef<E>,
attestation: AttestationRef<E>,
committee_count_per_slot: u64,
spec: &ChainSpec,
) -> Result<SubnetId, ArithError> {
let committee_index = attestation.committee_index().ok_or(ArithError::Overflow)?;
Self::compute_subnet::<E>(
attestation.data().slot,
attestation.committee_index(),
committee_index,
committee_count_per_slot,
spec,
)

View File

@@ -26,6 +26,15 @@ impl<N: Unsigned + Clone> TestRandom for BitVector<N> {
fn random_for_test(rng: &mut impl RngCore) -> Self {
let mut raw_bytes = smallvec![0; std::cmp::max(1, (N::to_usize() + 7) / 8)];
rng.fill_bytes(&mut raw_bytes);
// If N isn't divisible by 8
// zero out bits greater than N
if let Some(last_byte) = raw_bytes.last_mut() {
let mut mask = 0;
for i in 0..N::to_usize() % 8 {
mask |= 1 << i;
}
*last_byte &= mask;
}
Self::from_bytes(raw_bytes).expect("we generate a valid BitVector")
}
}