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

This commit is contained in:
realbigsean
2024-06-20 10:43:35 -04:00
143 changed files with 1467 additions and 1519 deletions

View File

@@ -285,17 +285,17 @@ impl ForkChoiceTestDefinition {
}
}
/// Gives a root that is not the zero hash (unless i is `usize::max_value)`.
/// Gives a root that is not the zero hash (unless i is `usize::MAX)`.
fn get_root(i: u64) -> Hash256 {
Hash256::from_low_u64_be(i + 1)
}
/// Gives a hash that is not the zero hash (unless i is `usize::max_value)`.
/// Gives a hash that is not the zero hash (unless i is `usize::MAX)`.
fn get_hash(i: u64) -> ExecutionBlockHash {
ExecutionBlockHash::from_root(get_root(i))
}
/// Gives a checkpoint with a root that is not the zero hash (unless i is `usize::max_value)`.
/// Gives a checkpoint with a root that is not the zero hash (unless i is `usize::MAX)`.
/// `Epoch` will always equal `i`.
fn get_checkpoint(i: u64) -> Checkpoint {
Checkpoint {

View File

@@ -738,7 +738,7 @@ pub fn get_votes_test_definition() -> ForkChoiceTestDefinition {
// Ensure that pruning below the prune threshold does not prune.
ops.push(Operation::Prune {
finalized_root: get_root(5),
prune_threshold: usize::max_value(),
prune_threshold: usize::MAX,
expected_len: 11,
});

View File

@@ -145,24 +145,24 @@ impl TryInto<ProtoNode> for ProtoNodeV16 {
}
}
impl Into<ProtoNodeV16> for ProtoNode {
fn into(self) -> ProtoNodeV16 {
impl From<ProtoNode> for ProtoNodeV16 {
fn from(from: ProtoNode) -> ProtoNodeV16 {
ProtoNodeV16 {
slot: self.slot,
state_root: self.state_root,
target_root: self.target_root,
current_epoch_shuffling_id: self.current_epoch_shuffling_id,
next_epoch_shuffling_id: self.next_epoch_shuffling_id,
root: self.root,
parent: self.parent,
justified_checkpoint: Some(self.justified_checkpoint),
finalized_checkpoint: Some(self.finalized_checkpoint),
weight: self.weight,
best_child: self.best_child,
best_descendant: self.best_descendant,
execution_status: self.execution_status,
unrealized_justified_checkpoint: self.unrealized_justified_checkpoint,
unrealized_finalized_checkpoint: self.unrealized_finalized_checkpoint,
slot: from.slot,
state_root: from.state_root,
target_root: from.target_root,
current_epoch_shuffling_id: from.current_epoch_shuffling_id,
next_epoch_shuffling_id: from.next_epoch_shuffling_id,
root: from.root,
parent: from.parent,
justified_checkpoint: Some(from.justified_checkpoint),
finalized_checkpoint: Some(from.finalized_checkpoint),
weight: from.weight,
best_child: from.best_child,
best_descendant: from.best_descendant,
execution_status: from.execution_status,
unrealized_justified_checkpoint: from.unrealized_justified_checkpoint,
unrealized_finalized_checkpoint: from.unrealized_finalized_checkpoint,
}
}
}

View File

@@ -995,7 +995,7 @@ mod test_compute_deltas {
use super::*;
use types::MainnetEthSpec;
/// Gives a hash that is not the zero hash (unless i is `usize::max_value)`.
/// Gives a hash that is not the zero hash (unless i is `usize::MAX)`.
fn hash_from_index(i: usize) -> Hash256 {
Hash256::from_low_u64_be(i as u64 + 1)
}

View File

@@ -55,19 +55,19 @@ impl TryInto<SszContainer> for SszContainerV16 {
}
}
impl Into<SszContainerV16> for SszContainer {
fn into(self) -> SszContainerV16 {
let nodes = self.nodes.into_iter().map(Into::into).collect();
impl From<SszContainer> for SszContainerV16 {
fn from(from: SszContainer) -> SszContainerV16 {
let nodes = from.nodes.into_iter().map(Into::into).collect();
SszContainerV16 {
votes: self.votes,
balances: self.balances,
prune_threshold: self.prune_threshold,
justified_checkpoint: self.justified_checkpoint,
finalized_checkpoint: self.finalized_checkpoint,
votes: from.votes,
balances: from.balances,
prune_threshold: from.prune_threshold,
justified_checkpoint: from.justified_checkpoint,
finalized_checkpoint: from.finalized_checkpoint,
nodes,
indices: self.indices,
previous_proposer_boost: self.previous_proposer_boost,
indices: from.indices,
previous_proposer_boost: from.previous_proposer_boost,
}
}
}

View File

@@ -155,12 +155,12 @@ mod test {
#[test]
fn errors() {
assert!(u32::max_value().safe_add(1).is_err());
assert!(u32::min_value().safe_sub(1).is_err());
assert!(u32::max_value().safe_mul(2).is_err());
assert!(u32::max_value().safe_div(0).is_err());
assert!(u32::max_value().safe_rem(0).is_err());
assert!(u32::max_value().safe_shl(32).is_err());
assert!(u32::max_value().safe_shr(32).is_err());
assert!(u32::MAX.safe_add(1).is_err());
assert!(u32::MIN.safe_sub(1).is_err());
assert!(u32::MAX.safe_mul(2).is_err());
assert!(u32::MAX.safe_div(0).is_err());
assert!(u32::MAX.safe_rem(0).is_err());
assert!(u32::MAX.safe_shl(32).is_err());
assert!(u32::MAX.safe_shr(32).is_err());
}
}

View File

@@ -44,107 +44,15 @@ pub mod attesting_indices_base {
}
pub mod attesting_indices_electra {
use std::collections::{HashMap, HashSet};
use std::collections::HashSet;
use crate::per_block_processing::errors::{AttestationInvalid as Invalid, BlockOperationError};
use itertools::Itertools;
use safe_arith::SafeArith;
use types::*;
// TODO(electra) remove duplicate code
// get_indexed_attestation is almost an exact duplicate
// the only differences are the invalid selection proof
// and aggregator not in committee checks
pub fn get_indexed_attestation_from_signed_aggregate<E: EthSpec>(
committees: &[BeaconCommittee],
signed_aggregate: &SignedAggregateAndProofElectra<E>,
spec: &ChainSpec,
) -> Result<IndexedAttestation<E>, BeaconStateError> {
let mut output: HashSet<u64> = HashSet::new();
let committee_bits = &signed_aggregate.message.aggregate.committee_bits;
let aggregation_bits = &signed_aggregate.message.aggregate.aggregation_bits;
let aggregator_index = signed_aggregate.message.aggregator_index;
let attestation = &signed_aggregate.message.aggregate;
let committee_indices = get_committee_indices::<E>(committee_bits);
let mut committee_offset = 0;
let committees_map: HashMap<u64, &BeaconCommittee> = committees
.iter()
.map(|committee| (committee.index, committee))
.collect();
let committee_count_per_slot = committees.len() as u64;
let mut participant_count = 0;
// TODO(electra):
// Note: this clones the signature which is known to be a relatively slow operation.
//
// Future optimizations should remove this clone.
let selection_proof =
SelectionProof::from(signed_aggregate.message.selection_proof.clone());
for index in committee_indices {
if let Some(&beacon_committee) = committees_map.get(&index) {
if !selection_proof
.is_aggregator(beacon_committee.committee.len(), spec)
.map_err(BeaconStateError::ArithError)?
{
return Err(BeaconStateError::InvalidSelectionProof { aggregator_index });
}
if !beacon_committee
.committee
.contains(&(aggregator_index as usize))
{
return Err(BeaconStateError::AggregatorNotInCommittee { aggregator_index });
}
// This check is new to the spec's `process_attestation` in Electra.
if index >= committee_count_per_slot {
return Err(BeaconStateError::InvalidCommitteeIndex(index));
}
participant_count.safe_add_assign(beacon_committee.committee.len() as u64)?;
let committee_attesters = beacon_committee
.committee
.iter()
.enumerate()
.filter_map(|(i, &index)| {
if let Ok(aggregation_bit_index) = committee_offset.safe_add(i) {
if aggregation_bits.get(aggregation_bit_index).unwrap_or(false) {
return Some(index as u64);
}
}
None
})
.collect::<HashSet<u64>>();
output.extend(committee_attesters);
committee_offset.safe_add_assign(beacon_committee.committee.len())?;
} else {
return Err(Error::NoCommitteeFound(index));
}
}
// This check is new to the spec's `process_attestation` in Electra.
if participant_count as usize != aggregation_bits.len() {
return Err(Error::InvalidBitfield);
}
let mut indices = output.into_iter().collect_vec();
indices.sort_unstable();
Ok(IndexedAttestation::Electra(IndexedAttestationElectra {
attesting_indices: VariableList::new(indices)?,
data: attestation.data.clone(),
signature: attestation.signature.clone(),
}))
}
/// Compute an Electra IndexedAttestation given a list of committees.
///
/// Committees must be sorted by ascending order 0..committees_per_slot
pub fn get_indexed_attestation<E: EthSpec>(
committees: &[BeaconCommittee],
attestation: &AttestationElectra<E>,
@@ -167,17 +75,7 @@ pub mod attesting_indices_electra {
attestation: &AttestationElectra<E>,
) -> Result<IndexedAttestation<E>, BlockOperationError<Invalid>> {
let committees = beacon_state.get_beacon_committees_at_slot(attestation.data.slot)?;
let attesting_indices = get_attesting_indices::<E>(
&committees,
&attestation.aggregation_bits,
&attestation.committee_bits,
)?;
Ok(IndexedAttestation::Electra(IndexedAttestationElectra {
attesting_indices: VariableList::new(attesting_indices)?,
data: attestation.data.clone(),
signature: attestation.signature.clone(),
}))
get_indexed_attestation(&committees, attestation)
}
/// Shortcut for getting the attesting indices while fetching the committee from the state's cache.
@@ -190,51 +88,47 @@ pub mod attesting_indices_electra {
}
/// Returns validator indices which participated in the attestation, sorted by increasing index.
///
/// Committees must be sorted by ascending order 0..committees_per_slot
pub fn get_attesting_indices<E: EthSpec>(
committees: &[BeaconCommittee],
aggregation_bits: &BitList<E::MaxValidatorsPerSlot>,
committee_bits: &BitVector<E::MaxCommitteesPerSlot>,
) -> Result<Vec<u64>, BeaconStateError> {
let mut output: HashSet<u64> = HashSet::new();
let mut attesting_indices = vec![];
let committee_indices = get_committee_indices::<E>(committee_bits);
let mut committee_offset = 0;
let committees_map: HashMap<u64, &BeaconCommittee> = committees
.iter()
.map(|committee| (committee.index, committee))
.collect();
let committee_count_per_slot = committees.len() as u64;
let mut participant_count = 0;
for index in committee_indices {
if let Some(&beacon_committee) = committees_map.get(&index) {
// This check is new to the spec's `process_attestation` in Electra.
if index >= committee_count_per_slot {
return Err(BeaconStateError::InvalidCommitteeIndex(index));
}
participant_count.safe_add_assign(beacon_committee.committee.len() as u64)?;
let committee_attesters = beacon_committee
.committee
.iter()
.enumerate()
.filter_map(|(i, &index)| {
if let Ok(aggregation_bit_index) = committee_offset.safe_add(i) {
if aggregation_bits.get(aggregation_bit_index).unwrap_or(false) {
return Some(index as u64);
}
}
None
})
.collect::<HashSet<u64>>();
let beacon_committee = committees
.get(index as usize)
.ok_or(Error::NoCommitteeFound(index))?;
output.extend(committee_attesters);
committee_offset.safe_add_assign(beacon_committee.committee.len())?;
} else {
return Err(Error::NoCommitteeFound(index));
// This check is new to the spec's `process_attestation` in Electra.
if index >= committee_count_per_slot {
return Err(BeaconStateError::InvalidCommitteeIndex(index));
}
participant_count.safe_add_assign(beacon_committee.committee.len() as u64)?;
let committee_attesters = beacon_committee
.committee
.iter()
.enumerate()
.filter_map(|(i, &index)| {
if let Ok(aggregation_bit_index) = committee_offset.safe_add(i) {
if aggregation_bits.get(aggregation_bit_index).unwrap_or(false) {
return Some(index as u64);
}
}
None
})
.collect::<HashSet<u64>>();
attesting_indices.extend(committee_attesters);
committee_offset.safe_add_assign(beacon_committee.committee.len())?;
}
// This check is new to the spec's `process_attestation` in Electra.
@@ -242,10 +136,9 @@ pub mod attesting_indices_electra {
return Err(BeaconStateError::InvalidBitfield);
}
let mut indices = output.into_iter().collect_vec();
indices.sort_unstable();
attesting_indices.sort_unstable();
Ok(indices)
Ok(attesting_indices)
}
pub fn get_committee_indices<E: EthSpec>(

View File

@@ -129,6 +129,7 @@ pub enum BlockProcessingError {
target_address: Address,
},
InavlidConsolidationSignature,
PendingAttestationInElectra,
}
impl From<BeaconStateError> for BlockProcessingError {

View File

@@ -91,33 +91,30 @@ pub mod base {
)
.map_err(|e| e.into_with_index(i))?;
match attestation {
AttestationRef::Base(att) => {
let pending_attestation = PendingAttestation {
aggregation_bits: att.aggregation_bits.clone(),
data: att.data.clone(),
inclusion_delay: state.slot().safe_sub(att.data.slot)?.as_u64(),
proposer_index,
};
if attestation.data().target.epoch == state.current_epoch() {
state
.as_base_mut()?
.current_epoch_attestations
.push(pending_attestation)?;
} else {
state
.as_base_mut()?
.previous_epoch_attestations
.push(pending_attestation)?;
}
}
AttestationRef::Electra(_) => {
// TODO(electra) pending attestations are only phase 0
// so we should just raise a relevant error here
todo!()
}
let AttestationRef::Base(attestation) = attestation else {
// Pending attestations have been deprecated in a altair, this branch should
// never happen
return Err(BlockProcessingError::PendingAttestationInElectra);
};
let pending_attestation = PendingAttestation {
aggregation_bits: attestation.aggregation_bits.clone(),
data: attestation.data.clone(),
inclusion_delay: state.slot().safe_sub(attestation.data.slot)?.as_u64(),
proposer_index,
};
if attestation.data.target.epoch == state.current_epoch() {
state
.as_base_mut()?
.current_epoch_attestations
.push(pending_attestation)?;
} else {
state
.as_base_mut()?
.previous_epoch_attestations
.push(pending_attestation)?;
}
}
Ok(())

View File

@@ -326,7 +326,6 @@ where
genesis_validators_root,
);
// TODO(electra), signing root isnt unique in the case of electra
let message = indexed_attestation.data().signing_root(domain);
Ok(SignatureSet::multiple_pubkeys(signature, pubkeys, message))

View File

@@ -30,7 +30,7 @@ impl Default for InclusionInfo {
/// Defaults to `delay` at its maximum value and `proposer_index` at zero.
fn default() -> Self {
Self {
delay: u64::max_value(),
delay: u64::MAX,
proposer_index: 0,
}
}

View File

@@ -17,7 +17,7 @@ use std::cmp::max;
/// - `list_size == 0`
/// - `index >= list_size`
/// - `list_size > 2**24`
/// - `list_size > usize::max_value() / 2`
/// - `list_size > usize::MAX / 2`
pub fn compute_shuffled_index(
index: usize,
list_size: usize,
@@ -26,7 +26,7 @@ pub fn compute_shuffled_index(
) -> Option<usize> {
if list_size == 0
|| index >= list_size
|| list_size > usize::max_value() / 2
|| list_size > usize::MAX / 2
|| list_size > 2_usize.pow(24)
{
return None;
@@ -140,7 +140,7 @@ mod tests {
fn returns_none_for_too_large_list() {
assert_eq!(
None,
compute_shuffled_index(100, usize::max_value() / 2, &[42, 42], 90)
compute_shuffled_index(100, usize::MAX / 2, &[42, 42], 90)
);
}
}

View File

@@ -75,7 +75,7 @@ impl Buf {
/// Returns `None` under any of the following conditions:
/// - `list_size == 0`
/// - `list_size > 2**24`
/// - `list_size > usize::max_value() / 2`
/// - `list_size > usize::MAX / 2`
pub fn shuffle_list(
mut input: Vec<usize>,
rounds: u8,
@@ -84,10 +84,7 @@ pub fn shuffle_list(
) -> Option<Vec<usize>> {
let list_size = input.len();
if input.is_empty()
|| list_size > usize::max_value() / 2
|| list_size > 2_usize.pow(24)
|| rounds == 0
if input.is_empty() || list_size > usize::MAX / 2 || list_size > 2_usize.pow(24) || rounds == 0
{
return None;
}

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

@@ -117,7 +117,6 @@ impl<E: EthSpec> ExecutionPayloadHeader<E> {
#[allow(clippy::arithmetic_side_effects)]
pub fn ssz_max_var_len_for_fork(fork_name: ForkName) -> usize {
// Matching here in case variable fields are added in future forks.
// TODO(electra): review electra changes
match fork_name {
ForkName::Base
| ForkName::Altair

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,
}
}
}