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

This commit is contained in:
realbigsean
2024-06-20 10:45:31 -04:00
142 changed files with 1463 additions and 1485 deletions

View File

@@ -36,8 +36,8 @@ fn get_state<E: EthSpec>(validator_count: usize) -> BeaconState<E> {
slashed: false,
activation_eligibility_epoch: Epoch::new(0),
activation_epoch: Epoch::new(0),
exit_epoch: Epoch::from(u64::max_value()),
withdrawable_epoch: Epoch::from(u64::max_value()),
exit_epoch: Epoch::from(u64::MAX),
withdrawable_epoch: Epoch::from(u64::MAX),
})
.collect(),
)

View File

@@ -4,6 +4,7 @@ use super::{
SignedRoot,
};
use crate::test_utils::TestRandom;
use crate::Attestation;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use superstruct::superstruct;
@@ -30,7 +31,7 @@ use tree_hash_derive::TreeHash;
),
ref_attributes(
derive(Debug, PartialEq, TreeHash, Serialize,),
serde(bound = "E: EthSpec"),
serde(untagged, bound = "E: EthSpec"),
tree_hash(enum_behaviour = "transparent")
)
)]
@@ -88,28 +89,39 @@ impl<E: EthSpec> AggregateAndProof<E> {
genesis_validators_root: Hash256,
spec: &ChainSpec,
) -> Self {
let selection_proof = selection_proof
.unwrap_or_else(|| {
SelectionProof::new::<E>(
aggregate.data().slot,
secret_key,
fork,
genesis_validators_root,
spec,
)
})
.into();
let selection_proof = selection_proof.unwrap_or_else(|| {
SelectionProof::new::<E>(
aggregate.data().slot,
secret_key,
fork,
genesis_validators_root,
spec,
)
});
Self::from_attestation(
aggregator_index,
aggregate.clone_as_attestation(),
selection_proof,
)
}
/// Produces a new `AggregateAndProof` given a `selection_proof`
pub fn from_attestation(
aggregator_index: u64,
aggregate: Attestation<E>,
selection_proof: SelectionProof,
) -> Self {
match aggregate {
AttestationRef::Base(attestation) => Self::Base(AggregateAndProofBase {
Attestation::Base(aggregate) => Self::Base(AggregateAndProofBase {
aggregator_index,
aggregate: attestation.clone(),
selection_proof,
aggregate,
selection_proof: selection_proof.into(),
}),
AttestationRef::Electra(attestation) => Self::Electra(AggregateAndProofElectra {
Attestation::Electra(aggregate) => Self::Electra(AggregateAndProofElectra {
aggregator_index,
aggregate: attestation.clone(),
selection_proof,
aggregate,
selection_proof: selection_proof.into(),
}),
}
}

View File

@@ -1,7 +1,7 @@
use crate::slot_data::SlotData;
use crate::{test_utils::TestRandom, Hash256, Slot};
use crate::{Checkpoint, ForkName};
use derivative::Derivative;
use rand::RngCore;
use safe_arith::ArithError;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
@@ -22,6 +22,8 @@ pub enum Error {
AlreadySigned(usize),
SubnetCountIsZero(ArithError),
IncorrectStateVariant,
InvalidCommitteeLength,
InvalidCommitteeIndex,
}
#[superstruct(
@@ -74,23 +76,6 @@ pub struct Attestation<E: EthSpec> {
pub signature: AggregateSignature,
}
// 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::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::Electra(AttestationElectra {
aggregation_bits,
committee_bits,
data,
signature,
})
}
}
impl<E: EthSpec> Hash for Attestation<E> {
fn hash<H>(&self, state: &mut H)
where
@@ -104,6 +89,50 @@ impl<E: EthSpec> Hash for Attestation<E> {
}
impl<E: EthSpec> Attestation<E> {
/// Produces an attestation with empty signature.
pub fn empty_for_signing(
committee_index: u64,
committee_length: usize,
slot: Slot,
beacon_block_root: Hash256,
source: Checkpoint,
target: Checkpoint,
spec: &ChainSpec,
) -> Result<Self, Error> {
if spec.fork_name_at_slot::<E>(slot) >= ForkName::Electra {
let mut committee_bits: BitVector<E::MaxCommitteesPerSlot> = BitVector::default();
committee_bits
.set(committee_index as usize, true)
.map_err(|_| Error::InvalidCommitteeIndex)?;
Ok(Attestation::Electra(AttestationElectra {
aggregation_bits: BitList::with_capacity(committee_length)
.map_err(|_| Error::InvalidCommitteeLength)?,
data: AttestationData {
slot,
index: 0u64,
beacon_block_root,
source,
target,
},
committee_bits,
signature: AggregateSignature::infinity(),
}))
} else {
Ok(Attestation::Base(AttestationBase {
aggregation_bits: BitList::with_capacity(committee_length)
.map_err(|_| Error::InvalidCommitteeLength)?,
data: AttestationData {
slot,
index: committee_index,
beacon_block_root,
source,
target,
},
signature: AggregateSignature::infinity(),
}))
}
}
/// Aggregate another Attestation into this one.
///
/// The aggregation bitfields must be disjoint, and the data must be the same.

View File

@@ -820,6 +820,11 @@ impl<E: EthSpec> From<BeaconBlockBody<E, FullPayload<E>>>
}
impl<E: EthSpec> BeaconBlockBody<E> {
/// Returns the name of the fork pertaining to `self`.
pub fn fork_name(&self) -> ForkName {
self.to_ref().fork_name()
}
pub fn block_body_merkle_proof(&self, generalized_index: usize) -> Result<Vec<Hash256>, Error> {
let field_index = match generalized_index {
light_client_update::EXECUTION_PAYLOAD_INDEX => {
@@ -834,22 +839,16 @@ impl<E: EthSpec> BeaconBlockBody<E> {
_ => return Err(Error::IndexNotSupported(generalized_index)),
};
let attestations_root = match self {
BeaconBlockBody::Base(_)
| BeaconBlockBody::Altair(_)
| BeaconBlockBody::Bellatrix(_)
| BeaconBlockBody::Capella(_)
| BeaconBlockBody::Deneb(_) => self.attestations_base()?.tree_hash_root(),
BeaconBlockBody::Electra(_) => self.attestations_electra()?.tree_hash_root(),
let attestations_root = if self.fork_name() > ForkName::Electra {
self.attestations_electra()?.tree_hash_root()
} else {
self.attestations_base()?.tree_hash_root()
};
let attester_slashings_root = match self {
BeaconBlockBody::Base(_)
| BeaconBlockBody::Altair(_)
| BeaconBlockBody::Bellatrix(_)
| BeaconBlockBody::Capella(_)
| BeaconBlockBody::Deneb(_) => self.attester_slashings_base()?.tree_hash_root(),
BeaconBlockBody::Electra(_) => self.attester_slashings_electra()?.tree_hash_root(),
let attester_slashings_root = if self.fork_name() > ForkName::Electra {
self.attester_slashings_electra()?.tree_hash_root()
} else {
self.attester_slashings_base()?.tree_hash_root()
};
let mut leaves = vec![

View File

@@ -86,7 +86,7 @@ impl CommitteeCache {
}
// The use of `NonZeroUsize` reduces the maximum number of possible validators by one.
if state.validators().len() == usize::max_value() {
if state.validators().len() == usize::MAX {
return Err(Error::TooManyValidators);
}
@@ -183,6 +183,8 @@ impl CommitteeCache {
}
/// Get all the Beacon committees at a given `slot`.
///
/// Committees are sorted by ascending index order 0..committees_per_slot
pub fn get_beacon_committees_at_slot(&self, slot: Slot) -> Result<Vec<BeaconCommittee>, Error> {
if self.initialized_epoch.is_none() {
return Err(Error::CommitteeCacheUninitialized(None));

View File

@@ -2,6 +2,7 @@
use crate::test_utils::*;
use beacon_chain::test_utils::{BeaconChainHarness, EphemeralHarnessType};
use beacon_chain::types::*;
use lazy_static::lazy_static;
use swap_or_not_shuffle::shuffle_list;
pub const VALIDATOR_COUNT: usize = 16;

View File

@@ -6,6 +6,7 @@ use beacon_chain::types::{
ChainSpec, Domain, Epoch, EthSpec, Hash256, Keypair, MainnetEthSpec, MinimalEthSpec,
RelativeEpoch, Slot, Vector,
};
use lazy_static::lazy_static;
use ssz::Encode;
use std::ops::Mul;
use swap_or_not_shuffle::compute_shuffled_index;

View File

@@ -6,13 +6,10 @@ use crate::{
use crate::{KzgProofs, SignedBeaconBlock};
use bls::Signature;
use derivative::Derivative;
use kzg::{
Blob as KzgBlob, Kzg, KzgCommitment, KzgProof, BYTES_PER_BLOB, BYTES_PER_FIELD_ELEMENT,
FIELD_ELEMENTS_PER_BLOB,
};
use kzg::{Blob as KzgBlob, Kzg, KzgCommitment, KzgProof, BYTES_PER_BLOB, BYTES_PER_FIELD_ELEMENT};
use merkle_proof::{merkle_root_from_branch, verify_merkle_proof, MerkleTreeError};
use rand::Rng;
use safe_arith::{ArithError, SafeArith};
use safe_arith::ArithError;
use serde::{Deserialize, Serialize};
use ssz::Encode;
use ssz_derive::{Decode, Encode};
@@ -190,33 +187,30 @@ impl<E: EthSpec> BlobSidecar<E> {
}
/// Verifies the kzg commitment inclusion merkle proof.
pub fn verify_blob_sidecar_inclusion_proof(&self) -> Result<bool, MerkleTreeError> {
// Depth of the subtree rooted at `blob_kzg_commitments` in the `BeaconBlockBody`
// is equal to depth of the ssz List max size + 1 for the length mixin
let kzg_commitments_tree_depth = (E::max_blob_commitments_per_block()
.next_power_of_two()
.ilog2()
.safe_add(1))? as usize;
pub fn verify_blob_sidecar_inclusion_proof(&self) -> bool {
let kzg_commitments_tree_depth = E::kzg_commitments_tree_depth();
// EthSpec asserts that kzg_commitments_tree_depth is less than KzgCommitmentInclusionProofDepth
let (kzg_commitment_subtree_proof, kzg_commitments_proof) = self
.kzg_commitment_inclusion_proof
.split_at(kzg_commitments_tree_depth);
// Compute the `tree_hash_root` of the `blob_kzg_commitments` subtree using the
// inclusion proof branches
let blob_kzg_commitments_root = merkle_root_from_branch(
self.kzg_commitment.tree_hash_root(),
self.kzg_commitment_inclusion_proof
.get(0..kzg_commitments_tree_depth)
.ok_or(MerkleTreeError::PleaseNotifyTheDevs)?,
kzg_commitment_subtree_proof,
kzg_commitments_tree_depth,
self.index as usize,
);
// The remaining inclusion proof branches are for the top level `BeaconBlockBody` tree
Ok(verify_merkle_proof(
verify_merkle_proof(
blob_kzg_commitments_root,
self.kzg_commitment_inclusion_proof
.get(kzg_commitments_tree_depth..E::kzg_proof_inclusion_proof_depth())
.ok_or(MerkleTreeError::PleaseNotifyTheDevs)?,
E::kzg_proof_inclusion_proof_depth().safe_sub(kzg_commitments_tree_depth)?,
kzg_commitments_proof,
E::block_body_tree_depth(),
BLOB_KZG_COMMITMENTS_INDEX,
self.signed_block_header.message.body_root,
))
)
}
pub fn random_valid<R: Rng>(rng: &mut R, kzg: &Kzg) -> Result<Self, String> {
@@ -224,13 +218,7 @@ impl<E: EthSpec> BlobSidecar<E> {
rng.fill_bytes(&mut blob_bytes);
// Ensure that the blob is canonical by ensuring that
// each field element contained in the blob is < BLS_MODULUS
for i in 0..FIELD_ELEMENTS_PER_BLOB {
let Some(byte) = blob_bytes.get_mut(
i.checked_mul(BYTES_PER_FIELD_ELEMENT)
.ok_or("overflow".to_string())?,
) else {
return Err(format!("blob byte index out of bounds: {:?}", i));
};
for byte in blob_bytes.iter_mut().step_by(BYTES_PER_FIELD_ELEMENT) {
*byte = 0;
}

View File

@@ -376,7 +376,7 @@ impl ChainSpec {
state: &BeaconState<E>,
) -> u64 {
let fork_name = state.fork_name_unchecked();
if fork_name >= ForkName::Electra {
if fork_name.electra_enabled() {
self.min_slashing_penalty_quotient_electra
} else if fork_name >= ForkName::Bellatrix {
self.min_slashing_penalty_quotient_bellatrix

View File

@@ -294,6 +294,22 @@ pub trait EthSpec:
Self::KzgCommitmentInclusionProofDepth::to_usize()
}
fn kzg_commitments_tree_depth() -> usize {
// Depth of the subtree rooted at `blob_kzg_commitments` in the `BeaconBlockBody`
// is equal to depth of the ssz List max size + 1 for the length mixin
Self::max_blob_commitments_per_block()
.next_power_of_two()
.ilog2()
.safe_add(1)
.expect("The log of max_blob_commitments_per_block can not overflow") as usize
}
fn block_body_tree_depth() -> usize {
Self::kzg_proof_inclusion_proof_depth()
.safe_sub(Self::kzg_commitments_tree_depth())
.expect("Preset values are not configurable and never result in non-positive block body depth")
}
/// Returns the `PENDING_BALANCE_DEPOSITS_LIMIT` constant for this specification.
fn pending_balance_deposits_limit() -> usize {
Self::PendingBalanceDepositsLimit::to_usize()
@@ -525,3 +541,28 @@ impl EthSpec for GnosisEthSpec {
EthSpecId::Gnosis
}
}
#[cfg(test)]
mod test {
use crate::{EthSpec, GnosisEthSpec, MainnetEthSpec, MinimalEthSpec};
use ssz_types::typenum::Unsigned;
fn assert_valid_spec<E: EthSpec>() {
E::kzg_commitments_tree_depth();
E::block_body_tree_depth();
assert!(E::MaxValidatorsPerSlot::to_i32() >= E::MaxValidatorsPerCommittee::to_i32());
}
#[test]
fn mainnet_spec() {
assert_valid_spec::<MainnetEthSpec>();
}
#[test]
fn minimal_spec() {
assert_valid_spec::<MinimalEthSpec>();
}
#[test]
fn gnosis_spec() {
assert_valid_spec::<GnosisEthSpec>();
}
}

View File

@@ -119,6 +119,10 @@ impl ForkName {
ForkName::Electra => None,
}
}
pub fn electra_enabled(self) -> bool {
self >= ForkName::Electra
}
}
/// Map a fork name into a fork-versioned superstruct type like `BeaconBlock`.

View File

@@ -37,9 +37,9 @@ impl From<[u8; GRAFFITI_BYTES_LEN]> for Graffiti {
}
}
impl Into<[u8; GRAFFITI_BYTES_LEN]> for Graffiti {
fn into(self) -> [u8; GRAFFITI_BYTES_LEN] {
self.0
impl From<Graffiti> for [u8; GRAFFITI_BYTES_LEN] {
fn from(from: Graffiti) -> [u8; GRAFFITI_BYTES_LEN] {
from.0
}
}
@@ -77,9 +77,9 @@ impl<'de> Deserialize<'de> for GraffitiString {
}
}
impl Into<Graffiti> for GraffitiString {
fn into(self) -> Graffiti {
let graffiti_bytes = self.0.as_bytes();
impl From<GraffitiString> for Graffiti {
fn from(from: GraffitiString) -> Graffiti {
let graffiti_bytes = from.0.as_bytes();
let mut graffiti = [0; GRAFFITI_BYTES_LEN];
let graffiti_len = std::cmp::min(graffiti_bytes.len(), GRAFFITI_BYTES_LEN);

View File

@@ -1,9 +1,7 @@
use crate::{test_utils::TestRandom, AggregateSignature, AttestationData, EthSpec, VariableList};
use core::slice::Iter;
use derivative::Derivative;
use rand::RngCore;
use serde::{Deserialize, Serialize};
use ssz::Decode;
use ssz::Encode;
use ssz_derive::{Decode, Encode};
use std::hash::{Hash, Hasher};
@@ -116,11 +114,14 @@ impl<E: EthSpec> IndexedAttestation<E> {
}
}
pub fn to_electra(self) -> Result<IndexedAttestationElectra<E>, ssz_types::Error> {
Ok(match self {
pub fn to_electra(self) -> IndexedAttestationElectra<E> {
match self {
Self::Base(att) => {
let extended_attesting_indices: VariableList<u64, E::MaxValidatorsPerSlot> =
VariableList::new(att.attesting_indices.to_vec())?;
VariableList::new(att.attesting_indices.to_vec())
.expect("MaxValidatorsPerSlot must be >= MaxValidatorsPerCommittee");
// Note a unit test in consensus/types/src/eth_spec.rs asserts this invariant for
// all known specs
IndexedAttestationElectra {
attesting_indices: extended_attesting_indices,
@@ -129,7 +130,7 @@ impl<E: EthSpec> IndexedAttestation<E> {
}
}
Self::Electra(att) => att,
})
}
}
}
@@ -186,43 +187,6 @@ impl<'a, E: EthSpec> IndexedAttestationRef<'a, E> {
}
}
impl<E: EthSpec> Decode for IndexedAttestation<E> {
fn is_ssz_fixed_len() -> bool {
false
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
if let Ok(result) = IndexedAttestationBase::from_ssz_bytes(bytes) {
return Ok(IndexedAttestation::Base(result));
}
if let Ok(result) = IndexedAttestationElectra::from_ssz_bytes(bytes) {
return Ok(IndexedAttestation::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 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,
})
}
}
/// Implementation of non-crypto-secure `Hash`, for use with `HashMap` and `HashSet`.
///
/// Guarantees `att1 == att2 -> hash(att1) == hash(att2)`.
@@ -276,6 +240,38 @@ mod quoted_variable_list_u64 {
}
}
#[derive(Debug, Clone, Encode, Decode, PartialEq)]
#[ssz(enum_behaviour = "union")]
pub enum IndexedAttestationOnDisk<E: EthSpec> {
Base(IndexedAttestationBase<E>),
Electra(IndexedAttestationElectra<E>),
}
#[derive(Debug, Clone, Encode, PartialEq)]
#[ssz(enum_behaviour = "union")]
pub enum IndexedAttestationRefOnDisk<'a, E: EthSpec> {
Base(&'a IndexedAttestationBase<E>),
Electra(&'a IndexedAttestationElectra<E>),
}
impl<'a, E: EthSpec> From<&'a IndexedAttestation<E>> for IndexedAttestationRefOnDisk<'a, E> {
fn from(attestation: &'a IndexedAttestation<E>) -> Self {
match attestation {
IndexedAttestation::Base(attestation) => Self::Base(attestation),
IndexedAttestation::Electra(attestation) => Self::Electra(attestation),
}
}
}
impl<E: EthSpec> From<IndexedAttestationOnDisk<E>> for IndexedAttestation<E> {
fn from(attestation: IndexedAttestationOnDisk<E>) -> Self {
match attestation {
IndexedAttestationOnDisk::Base(attestation) => Self::Base(attestation),
IndexedAttestationOnDisk::Electra(attestation) => Self::Electra(attestation),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
@@ -331,14 +327,22 @@ mod tests {
assert!(!indexed_vote_first.is_surround_vote(&indexed_vote_second));
}
ssz_and_tree_hash_tests!(IndexedAttestation<MainnetEthSpec>);
mod base {
use super::*;
ssz_and_tree_hash_tests!(IndexedAttestationBase<MainnetEthSpec>);
}
mod electra {
use super::*;
ssz_and_tree_hash_tests!(IndexedAttestationElectra<MainnetEthSpec>);
}
fn create_indexed_attestation(
target_epoch: u64,
source_epoch: u64,
) -> IndexedAttestation<MainnetEthSpec> {
let mut rng = XorShiftRng::from_seed([42; 16]);
let mut indexed_vote = IndexedAttestation::random_for_test(&mut rng);
let mut indexed_vote =
IndexedAttestation::Base(IndexedAttestationBase::random_for_test(&mut rng));
indexed_vote.data_mut().source.epoch = Epoch::new(source_epoch);
indexed_vote.data_mut().target.epoch = Epoch::new(target_epoch);

View File

@@ -9,8 +9,6 @@
)
)]
#[macro_use]
extern crate lazy_static;
#[macro_use]
pub mod test_utils;
@@ -178,7 +176,8 @@ pub use crate::fork_versioned_response::{ForkVersionDeserialize, ForkVersionedRe
pub use crate::graffiti::{Graffiti, GRAFFITI_BYTES_LEN};
pub use crate::historical_batch::HistoricalBatch;
pub use crate::indexed_attestation::{
IndexedAttestation, IndexedAttestationBase, IndexedAttestationElectra, IndexedAttestationRef,
IndexedAttestation, IndexedAttestationBase, IndexedAttestationElectra,
IndexedAttestationOnDisk, IndexedAttestationRef, IndexedAttestationRefOnDisk,
};
pub use crate::light_client_bootstrap::{
LightClientBootstrap, LightClientBootstrapAltair, LightClientBootstrapCapella,

View File

@@ -77,9 +77,9 @@ impl SelectionProof {
}
}
impl Into<Signature> for SelectionProof {
fn into(self) -> Signature {
self.0
impl From<SelectionProof> for Signature {
fn from(from: SelectionProof) -> Signature {
from.0
}
}

View File

@@ -6,6 +6,7 @@ use super::{
Signature, SignedRoot,
};
use crate::test_utils::TestRandom;
use crate::Attestation;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use superstruct::superstruct;
@@ -83,17 +84,19 @@ impl<E: EthSpec> SignedAggregateAndProof<E> {
);
let signing_message = message.signing_root(domain);
match message {
Self::from_aggregate_and_proof(message, secret_key.sign(signing_message))
}
/// Produces a new `SignedAggregateAndProof` given a `signature` of `aggregate`
pub fn from_aggregate_and_proof(aggregate: AggregateAndProof<E>, signature: Signature) -> Self {
match aggregate {
AggregateAndProof::Base(message) => {
SignedAggregateAndProof::Base(SignedAggregateAndProofBase {
message,
signature: secret_key.sign(signing_message),
})
SignedAggregateAndProof::Base(SignedAggregateAndProofBase { message, signature })
}
AggregateAndProof::Electra(message) => {
SignedAggregateAndProof::Electra(SignedAggregateAndProofElectra {
message,
signature: secret_key.sign(signing_message),
signature,
})
}
}
@@ -107,4 +110,11 @@ impl<E: EthSpec> SignedAggregateAndProof<E> {
}
}
}
pub fn into_attestation(self) -> Attestation<E> {
match self {
Self::Base(att) => Attestation::Base(att.message.aggregate),
Self::Electra(att) => Attestation::Electra(att.message.aggregate),
}
}
}

View File

@@ -70,7 +70,7 @@ impl Slot {
}
pub fn max_value() -> Slot {
Slot(u64::max_value())
Slot(u64::MAX)
}
}
@@ -80,7 +80,7 @@ impl Epoch {
}
pub fn max_value() -> Epoch {
Epoch(u64::max_value())
Epoch(u64::MAX)
}
/// The first slot in the epoch.
@@ -176,10 +176,10 @@ mod epoch_tests {
let slots_per_epoch = 32;
// The last epoch which can be represented by u64.
let epoch = Epoch::new(u64::max_value() / slots_per_epoch);
let epoch = Epoch::new(u64::MAX / slots_per_epoch);
// A slot number on the epoch should be equal to u64::max_value.
assert_eq!(epoch.end_slot(slots_per_epoch), Slot::new(u64::max_value()));
assert_eq!(epoch.end_slot(slots_per_epoch), Slot::new(u64::MAX));
}
#[test]

View File

@@ -6,9 +6,9 @@ macro_rules! impl_from_into_u64 {
}
}
impl Into<u64> for $main {
fn into(self) -> u64 {
self.0
impl From<$main> for u64 {
fn from(from: $main) -> u64 {
from.0
}
}
@@ -28,9 +28,9 @@ macro_rules! impl_from_into_usize {
}
}
impl Into<usize> for $main {
fn into(self) -> usize {
self.0 as usize
impl From<$main> for usize {
fn from(from: $main) -> usize {
from.0 as usize
}
}
@@ -352,7 +352,7 @@ macro_rules! new_tests {
fn new() {
assert_eq!($type(0), $type::new(0));
assert_eq!($type(3), $type::new(3));
assert_eq!($type(u64::max_value()), $type::new(u64::max_value()));
assert_eq!($type(u64::MAX), $type::new(u64::MAX));
}
};
}
@@ -368,17 +368,17 @@ macro_rules! from_into_tests {
let x: $other = $type(3).into();
assert_eq!(x, 3);
let x: $other = $type(u64::max_value()).into();
let x: $other = $type(u64::MAX).into();
// Note: this will fail on 32 bit systems. This is expected as we don't have a proper
// 32-bit system strategy in place.
assert_eq!(x, $other::max_value());
assert_eq!(x, $other::MAX);
}
#[test]
fn from() {
assert_eq!($type(0), $type::from(0_u64));
assert_eq!($type(3), $type::from(3_u64));
assert_eq!($type(u64::max_value()), $type::from($other::max_value()));
assert_eq!($type(u64::MAX), $type::from($other::MAX));
}
};
}
@@ -396,8 +396,8 @@ macro_rules! math_between_tests {
assert_partial_ord(1, Ordering::Less, 2);
assert_partial_ord(2, Ordering::Greater, 1);
assert_partial_ord(0, Ordering::Less, u64::max_value());
assert_partial_ord(u64::max_value(), Ordering::Greater, 0);
assert_partial_ord(0, Ordering::Less, u64::MAX);
assert_partial_ord(u64::MAX, Ordering::Greater, 0);
}
#[test]
@@ -412,9 +412,9 @@ macro_rules! math_between_tests {
assert_partial_eq(1, 0, false);
assert_partial_eq(1, 1, true);
assert_partial_eq(u64::max_value(), u64::max_value(), true);
assert_partial_eq(0, u64::max_value(), false);
assert_partial_eq(u64::max_value(), 0, false);
assert_partial_eq(u64::MAX, u64::MAX, true);
assert_partial_eq(0, u64::MAX, false);
assert_partial_eq(u64::MAX, 0, false);
}
#[test]
@@ -436,8 +436,8 @@ macro_rules! math_between_tests {
assert_add(7, 7, 14);
// Addition should be saturating.
assert_add(u64::max_value(), 1, u64::max_value());
assert_add(u64::max_value(), u64::max_value(), u64::max_value());
assert_add(u64::MAX, 1, u64::MAX);
assert_add(u64::MAX, u64::MAX, u64::MAX);
}
#[test]
@@ -455,8 +455,8 @@ macro_rules! math_between_tests {
assert_sub(1, 0, 1);
assert_sub(2, 1, 1);
assert_sub(14, 7, 7);
assert_sub(u64::max_value(), 1, u64::max_value() - 1);
assert_sub(u64::max_value(), u64::max_value(), 0);
assert_sub(u64::MAX, 1, u64::MAX - 1);
assert_sub(u64::MAX, u64::MAX, 0);
// Subtraction should be saturating
assert_sub(0, 1, 0);
@@ -480,7 +480,7 @@ macro_rules! math_between_tests {
assert_mul(0, 2, 0);
// Multiplication should be saturating.
assert_mul(u64::max_value(), 2, u64::max_value());
assert_mul(u64::MAX, 2, u64::MAX);
}
#[test]
@@ -499,7 +499,7 @@ macro_rules! math_between_tests {
assert_div(2, 2, 1);
assert_div(100, 50, 2);
assert_div(128, 2, 64);
assert_div(u64::max_value(), 2, 2_u64.pow(63) - 1);
assert_div(u64::MAX, 2, 2_u64.pow(63) - 1);
}
#[test]
@@ -544,8 +544,8 @@ macro_rules! math_tests {
assert_saturating_sub(1, 0, 1);
assert_saturating_sub(2, 1, 1);
assert_saturating_sub(14, 7, 7);
assert_saturating_sub(u64::max_value(), 1, u64::max_value() - 1);
assert_saturating_sub(u64::max_value(), u64::max_value(), 0);
assert_saturating_sub(u64::MAX, 1, u64::MAX - 1);
assert_saturating_sub(u64::MAX, u64::MAX, 0);
// Subtraction should be saturating
assert_saturating_sub(0, 1, 0);
@@ -565,8 +565,8 @@ macro_rules! math_tests {
assert_saturating_add(7, 7, 14);
// Addition should be saturating.
assert_saturating_add(u64::max_value(), 1, u64::max_value());
assert_saturating_add(u64::max_value(), u64::max_value(), u64::max_value());
assert_saturating_add(u64::MAX, 1, u64::MAX);
assert_saturating_add(u64::MAX, u64::MAX, u64::MAX);
}
#[test]
@@ -581,11 +581,11 @@ macro_rules! math_tests {
assert_checked_div(2, 2, Some(1));
assert_checked_div(100, 50, Some(2));
assert_checked_div(128, 2, Some(64));
assert_checked_div(u64::max_value(), 2, Some(2_u64.pow(63) - 1));
assert_checked_div(u64::MAX, 2, Some(2_u64.pow(63) - 1));
assert_checked_div(2, 0, None);
assert_checked_div(0, 0, None);
assert_checked_div(u64::max_value(), 0, None);
assert_checked_div(u64::MAX, 0, None);
}
#[test]
@@ -607,7 +607,7 @@ macro_rules! math_tests {
assert_is_power_of_two(4, true);
assert_is_power_of_two(2_u64.pow(4), true);
assert_is_power_of_two(u64::max_value(), false);
assert_is_power_of_two(u64::MAX, false);
}
#[test]
@@ -619,8 +619,8 @@ macro_rules! math_tests {
assert_ord(1, Ordering::Less, 2);
assert_ord(2, Ordering::Greater, 1);
assert_ord(0, Ordering::Less, u64::max_value());
assert_ord(u64::max_value(), Ordering::Greater, 0);
assert_ord(0, Ordering::Less, u64::MAX);
assert_ord(u64::MAX, Ordering::Greater, 0);
}
};
}
@@ -647,8 +647,8 @@ macro_rules! all_tests {
let x = $type(3).as_u64();
assert_eq!(x, 3);
let x = $type(u64::max_value()).as_u64();
assert_eq!(x, u64::max_value());
let x = $type(u64::MAX).as_u64();
assert_eq!(x, u64::MAX);
}
}
@@ -665,8 +665,8 @@ macro_rules! all_tests {
let x = $type(3).as_usize();
assert_eq!(x, 3);
let x = $type(u64::max_value()).as_usize();
assert_eq!(x, usize::max_value());
let x = $type(u64::MAX).as_usize();
assert_eq!(x, usize::MAX);
}
}
};

View File

@@ -1,5 +1,6 @@
//! Identifies each shard by an integer identifier.
use crate::{AttestationRef, ChainSpec, CommitteeIndex, Epoch, EthSpec, Slot};
use lazy_static::lazy_static;
use safe_arith::{ArithError, SafeArith};
use serde::{Deserialize, Serialize};
use std::ops::{Deref, DerefMut};
@@ -151,15 +152,15 @@ impl From<u64> for SubnetId {
}
}
impl Into<u64> for SubnetId {
fn into(self) -> u64 {
self.0
impl From<SubnetId> for u64 {
fn from(from: SubnetId) -> u64 {
from.0
}
}
impl Into<u64> for &SubnetId {
fn into(self) -> u64 {
self.0
impl From<&SubnetId> for u64 {
fn from(from: &SubnetId) -> u64 {
from.0
}
}

View File

@@ -90,9 +90,9 @@ impl SyncSelectionProof {
}
}
impl Into<Signature> for SyncSelectionProof {
fn into(self) -> Signature {
self.0
impl From<SyncSelectionProof> for Signature {
fn from(from: SyncSelectionProof) -> Signature {
from.0
}
}

View File

@@ -1,6 +1,7 @@
//! Identifies each sync committee subnet by an integer identifier.
use crate::consts::altair::SYNC_COMMITTEE_SUBNET_COUNT;
use crate::EthSpec;
use lazy_static::lazy_static;
use safe_arith::{ArithError, SafeArith};
use serde::{Deserialize, Serialize};
use ssz_types::typenum::Unsigned;
@@ -77,15 +78,15 @@ impl From<u64> for SyncSubnetId {
}
}
impl Into<u64> for SyncSubnetId {
fn into(self) -> u64 {
self.0
impl From<SyncSubnetId> for u64 {
fn from(from: SyncSubnetId) -> u64 {
from.0
}
}
impl Into<u64> for &SyncSubnetId {
fn into(self) -> u64 {
self.0
impl From<&SyncSubnetId> for u64 {
fn from(from: &SyncSubnetId) -> u64 {
from.0
}
}

View File

@@ -0,0 +1,96 @@
use rand::Rng;
use kzg::{KzgCommitment, KzgProof};
use crate::beacon_block_body::KzgCommitments;
use crate::*;
use super::*;
type BlobsBundle<E> = (KzgCommitments<E>, KzgProofs<E>, BlobsList<E>);
pub fn generate_rand_block_and_blobs<E: EthSpec>(
fork_name: ForkName,
num_blobs: usize,
rng: &mut impl Rng,
) -> (SignedBeaconBlock<E, FullPayload<E>>, Vec<BlobSidecar<E>>) {
let inner = map_fork_name!(fork_name, BeaconBlock, <_>::random_for_test(rng));
let mut block = SignedBeaconBlock::from_block(inner, Signature::random_for_test(rng));
let mut blob_sidecars = vec![];
if block.fork_name_unchecked() < ForkName::Deneb {
return (block, blob_sidecars);
}
let (commitments, proofs, blobs) = generate_blobs::<E>(num_blobs).unwrap();
*block
.message_mut()
.body_mut()
.blob_kzg_commitments_mut()
.expect("kzg commitment expected from Deneb") = commitments.clone();
for (index, ((blob, kzg_commitment), kzg_proof)) in blobs
.into_iter()
.zip(commitments.into_iter())
.zip(proofs.into_iter())
.enumerate()
{
blob_sidecars.push(BlobSidecar {
index: index as u64,
blob: blob.clone(),
kzg_commitment,
kzg_proof,
signed_block_header: block.signed_block_header(),
kzg_commitment_inclusion_proof: block
.message()
.body()
.kzg_commitment_merkle_proof(index)
.unwrap(),
});
}
(block, blob_sidecars)
}
pub fn generate_blobs<E: EthSpec>(n_blobs: usize) -> Result<BlobsBundle<E>, String> {
let (mut commitments, mut proofs, mut blobs) = BlobsBundle::<E>::default();
for blob_index in 0..n_blobs {
blobs
.push(Blob::<E>::default())
.map_err(|_| format!("blobs are full, blob index: {:?}", blob_index))?;
commitments
.push(KzgCommitment::empty_for_testing())
.map_err(|_| format!("blobs are full, blob index: {:?}", blob_index))?;
proofs
.push(KzgProof::empty())
.map_err(|_| format!("blobs are full, blob index: {:?}", blob_index))?;
}
Ok((commitments, proofs, blobs))
}
#[cfg(test)]
mod test {
use super::*;
use rand::thread_rng;
#[test]
fn test_verify_blob_inclusion_proof() {
let (_block, blobs) =
generate_rand_block_and_blobs::<MainnetEthSpec>(ForkName::Deneb, 6, &mut thread_rng());
for blob in blobs {
assert!(blob.verify_blob_sidecar_inclusion_proof());
}
}
#[test]
fn test_verify_blob_inclusion_proof_invalid() {
let (_block, blobs) =
generate_rand_block_and_blobs::<MainnetEthSpec>(ForkName::Deneb, 6, &mut thread_rng());
for mut blob in blobs {
blob.kzg_commitment_inclusion_proof = FixedVector::random_for_test(&mut thread_rng());
assert!(!blob.verify_blob_sidecar_inclusion_proof());
}
}
}

View File

@@ -15,6 +15,8 @@ use tree_hash::TreeHash;
#[macro_use]
mod macros;
mod generate_deterministic_keypairs;
#[cfg(test)]
mod generate_random_block_and_blobs;
mod test_random;
pub fn test_ssz_tree_hash_pair<T, U>(v1: &T, v2: &U)

View File

@@ -63,7 +63,7 @@ impl Validator {
spec: &ChainSpec,
current_fork: ForkName,
) -> bool {
if current_fork >= ForkName::Electra {
if current_fork.electra_enabled() {
self.is_eligible_for_activation_queue_electra(spec)
} else {
self.is_eligible_for_activation_queue_base(spec)
@@ -172,7 +172,7 @@ impl Validator {
spec: &ChainSpec,
current_fork: ForkName,
) -> bool {
if current_fork >= ForkName::Electra {
if current_fork.electra_enabled() {
self.is_fully_withdrawable_at_electra(balance, epoch, spec)
} else {
self.is_fully_withdrawable_at_capella(balance, epoch, spec)
@@ -212,7 +212,7 @@ impl Validator {
spec: &ChainSpec,
current_fork: ForkName,
) -> bool {
if current_fork >= ForkName::Electra {
if current_fork.electra_enabled() {
self.is_partially_withdrawable_validator_electra(balance, spec, current_fork)
} else {
self.is_partially_withdrawable_validator_capella(balance, spec)
@@ -283,12 +283,12 @@ impl Default for Validator {
Self {
pubkey: PublicKeyBytes::empty(),
withdrawal_credentials: Hash256::default(),
activation_eligibility_epoch: Epoch::from(std::u64::MAX),
activation_epoch: Epoch::from(std::u64::MAX),
exit_epoch: Epoch::from(std::u64::MAX),
withdrawable_epoch: Epoch::from(std::u64::MAX),
activation_eligibility_epoch: Epoch::from(u64::MAX),
activation_epoch: Epoch::from(u64::MAX),
exit_epoch: Epoch::from(u64::MAX),
withdrawable_epoch: Epoch::from(u64::MAX),
slashed: false,
effective_balance: std::u64::MAX,
effective_balance: u64::MAX,
}
}
}