Merge remote-tracking branch 'origin/unstable' into max-blobs-preset

This commit is contained in:
Michael Sproul
2025-01-06 14:02:50 +11:00
430 changed files with 14304 additions and 9470 deletions

View File

@@ -146,4 +146,4 @@ impl<E: EthSpec> AggregateAndProof<E> {
}
impl<E: EthSpec> SignedRoot for AggregateAndProof<E> {}
impl<'a, E: EthSpec> SignedRoot for AggregateAndProofRef<'a, E> {}
impl<E: EthSpec> SignedRoot for AggregateAndProofRef<'_, E> {}

View File

@@ -233,7 +233,7 @@ impl<E: EthSpec> Attestation<E> {
}
}
impl<'a, E: EthSpec> AttestationRef<'a, E> {
impl<E: EthSpec> AttestationRef<'_, E> {
pub fn clone_as_attestation(self) -> Attestation<E> {
match self {
Self::Base(att) => Attestation::Base(att.clone()),
@@ -422,7 +422,7 @@ impl<E: EthSpec> SlotData for Attestation<E> {
}
}
impl<'a, E: EthSpec> SlotData for AttestationRef<'a, E> {
impl<E: EthSpec> SlotData for AttestationRef<'_, E> {
fn get_slot(&self) -> Slot {
self.data().slot
}

View File

@@ -80,10 +80,7 @@ pub struct BeaconBlock<E: EthSpec, Payload: AbstractExecPayload<E> = FullPayload
pub type BlindedBeaconBlock<E> = BeaconBlock<E, BlindedPayload<E>>;
impl<E: EthSpec, Payload: AbstractExecPayload<E>> SignedRoot for BeaconBlock<E, Payload> {}
impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> SignedRoot
for BeaconBlockRef<'a, E, Payload>
{
}
impl<E: EthSpec, Payload: AbstractExecPayload<E>> SignedRoot for BeaconBlockRef<'_, E, Payload> {}
/// Empty block trait for each block variant to implement.
pub trait EmptyBlock {

View File

@@ -145,7 +145,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBodyRef<'a, E,
}
}
fn body_merkle_leaves(&self) -> Vec<Hash256> {
pub(crate) fn body_merkle_leaves(&self) -> Vec<Hash256> {
let mut leaves = vec![];
match self {
Self::Base(body) => {
@@ -176,57 +176,71 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBodyRef<'a, E,
leaves
}
/// Produces the proof of inclusion for a `KzgCommitment` in `self.blob_kzg_commitments`
/// at `index`.
/// Calculate a KZG commitment merkle proof.
///
/// Prefer to use `complete_kzg_commitment_merkle_proof` with a reused proof for the
/// `blob_kzg_commitments` field.
pub fn kzg_commitment_merkle_proof(
&self,
index: usize,
) -> Result<FixedVector<Hash256, E::KzgCommitmentInclusionProofDepth>, Error> {
// We compute the branches by generating 2 merkle trees:
// 1. Merkle tree for the `blob_kzg_commitments` List object
// 2. Merkle tree for the `BeaconBlockBody` container
// We then merge the branches for both the trees all the way up to the root.
let kzg_commitments_proof = self.kzg_commitments_merkle_proof()?;
let proof = self.complete_kzg_commitment_merkle_proof(index, &kzg_commitments_proof)?;
Ok(proof)
}
// Part1 (Branches for the subtree rooted at `blob_kzg_commitments`)
//
// Branches for `blob_kzg_commitments` without length mix-in
let blob_leaves = self
.blob_kzg_commitments()?
.iter()
.map(|commitment| commitment.tree_hash_root())
.collect::<Vec<_>>();
let depth = E::max_blob_commitments_per_block()
.next_power_of_two()
.ilog2();
let tree = MerkleTree::create(&blob_leaves, depth as usize);
let (_, mut proof) = tree
.generate_proof(index, depth as usize)
.map_err(Error::MerkleTreeError)?;
/// Produces the proof of inclusion for a `KzgCommitment` in `self.blob_kzg_commitments`
/// at `index` using an existing proof for the `blob_kzg_commitments` field.
pub fn complete_kzg_commitment_merkle_proof(
&self,
index: usize,
kzg_commitments_proof: &[Hash256],
) -> Result<FixedVector<Hash256, E::KzgCommitmentInclusionProofDepth>, Error> {
match self {
Self::Base(_) | Self::Altair(_) | Self::Bellatrix(_) | Self::Capella(_) => {
Err(Error::IncorrectStateVariant)
}
Self::Deneb(_) | Self::Electra(_) => {
// We compute the branches by generating 2 merkle trees:
// 1. Merkle tree for the `blob_kzg_commitments` List object
// 2. Merkle tree for the `BeaconBlockBody` container
// We then merge the branches for both the trees all the way up to the root.
// Add the branch corresponding to the length mix-in.
let length = blob_leaves.len();
let usize_len = std::mem::size_of::<usize>();
let mut length_bytes = [0; BYTES_PER_CHUNK];
length_bytes
.get_mut(0..usize_len)
.ok_or(Error::MerkleTreeError(MerkleTreeError::PleaseNotifyTheDevs))?
.copy_from_slice(&length.to_le_bytes());
let length_root = Hash256::from_slice(length_bytes.as_slice());
proof.push(length_root);
// Part1 (Branches for the subtree rooted at `blob_kzg_commitments`)
//
// Branches for `blob_kzg_commitments` without length mix-in
let blob_leaves = self
.blob_kzg_commitments()?
.iter()
.map(|commitment| commitment.tree_hash_root())
.collect::<Vec<_>>();
let depth = E::max_blob_commitments_per_block()
.next_power_of_two()
.ilog2();
let tree = MerkleTree::create(&blob_leaves, depth as usize);
let (_, mut proof) = tree
.generate_proof(index, depth as usize)
.map_err(Error::MerkleTreeError)?;
// Part 2
// Branches for `BeaconBlockBody` container
let body_leaves = self.body_merkle_leaves();
let beacon_block_body_depth = body_leaves.len().next_power_of_two().ilog2() as usize;
let tree = MerkleTree::create(&body_leaves, beacon_block_body_depth);
let (_, mut proof_body) = tree
.generate_proof(BLOB_KZG_COMMITMENTS_INDEX, beacon_block_body_depth)
.map_err(Error::MerkleTreeError)?;
// Join the proofs for the subtree and the main tree
proof.append(&mut proof_body);
debug_assert_eq!(proof.len(), E::kzg_proof_inclusion_proof_depth());
// Add the branch corresponding to the length mix-in.
let length = blob_leaves.len();
let usize_len = std::mem::size_of::<usize>();
let mut length_bytes = [0; BYTES_PER_CHUNK];
length_bytes
.get_mut(0..usize_len)
.ok_or(Error::MerkleTreeError(MerkleTreeError::PleaseNotifyTheDevs))?
.copy_from_slice(&length.to_le_bytes());
let length_root = Hash256::from_slice(length_bytes.as_slice());
proof.push(length_root);
Ok(proof.into())
// Part 2
// Branches for `BeaconBlockBody` container
// Join the proofs for the subtree and the main tree
proof.extend_from_slice(kzg_commitments_proof);
Ok(FixedVector::new(proof)?)
}
}
}
/// Produces the proof of inclusion for `self.blob_kzg_commitments`.
@@ -239,7 +253,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBodyRef<'a, E,
let (_, proof) = tree
.generate_proof(BLOB_KZG_COMMITMENTS_INDEX, beacon_block_body_depth)
.map_err(Error::MerkleTreeError)?;
Ok(proof.into())
Ok(FixedVector::new(proof)?)
}
pub fn block_body_merkle_proof(&self, generalized_index: usize) -> Result<Vec<Hash256>, Error> {
@@ -364,7 +378,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBodyRefMut<'a,
}
}
impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBodyRef<'a, E, Payload> {
impl<E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBodyRef<'_, E, Payload> {
/// Get the fork_name of this object
pub fn fork_name(self) -> ForkName {
match self {

View File

@@ -7,7 +7,7 @@ pub struct BeaconCommittee<'a> {
pub committee: &'a [usize],
}
impl<'a> BeaconCommittee<'a> {
impl BeaconCommittee<'_> {
pub fn into_owned(self) -> OwnedBeaconCommittee {
OwnedBeaconCommittee {
slot: self.slot,

View File

@@ -59,6 +59,7 @@ pub enum Error {
UnknownValidator(usize),
UnableToDetermineProducer,
InvalidBitfield,
EmptyCommittee,
ValidatorIsWithdrawable,
ValidatorIsInactive {
val_index: usize,
@@ -155,7 +156,6 @@ pub enum Error {
current_fork: ForkName,
},
TotalActiveBalanceDiffUninitialized,
MissingImmutableValidator(usize),
IndexNotSupported(usize),
InvalidFlagIndex(usize),
MerkleTreeError(merkle_proof::MerkleTreeError),
@@ -510,7 +510,7 @@ where
#[compare_fields(as_iter)]
#[test_random(default)]
#[superstruct(only(Electra))]
pub pending_balance_deposits: List<PendingBalanceDeposit, E::PendingBalanceDepositsLimit>,
pub pending_deposits: List<PendingDeposit, E::PendingDepositsLimit>,
#[compare_fields(as_iter)]
#[test_random(default)]
#[superstruct(only(Electra))]
@@ -1548,19 +1548,23 @@ impl<E: EthSpec> BeaconState<E> {
.ok_or(Error::UnknownValidator(validator_index))
}
/// Add a validator to the registry and return the validator index that was allocated for it.
pub fn add_validator_to_registry(
&mut self,
deposit_data: &DepositData,
pubkey: PublicKeyBytes,
withdrawal_credentials: Hash256,
amount: u64,
spec: &ChainSpec,
) -> Result<(), Error> {
let fork = self.fork_name_unchecked();
let amount = if fork.electra_enabled() {
0
} else {
deposit_data.amount
};
self.validators_mut()
.push(Validator::from_deposit(deposit_data, amount, fork, spec))?;
) -> Result<usize, Error> {
let index = self.validators().len();
let fork_name = self.fork_name_unchecked();
self.validators_mut().push(Validator::from_deposit(
pubkey,
withdrawal_credentials,
amount,
fork_name,
spec,
))?;
self.balances_mut().push(amount)?;
// Altair or later initializations.
@@ -1574,7 +1578,20 @@ impl<E: EthSpec> BeaconState<E> {
inactivity_scores.push(0)?;
}
Ok(())
// Keep the pubkey cache up to date if it was up to date prior to this call.
//
// Doing this here while we know the pubkey and index is marginally quicker than doing it in
// a call to `update_pubkey_cache` later because we don't need to index into the validators
// tree again.
let pubkey_cache = self.pubkey_cache_mut();
if pubkey_cache.len() == index {
let success = pubkey_cache.insert(pubkey, index);
if !success {
return Err(Error::PubkeyCacheInconsistent);
}
}
Ok(index)
}
/// Safe copy-on-write accessor for the `validators` list.
@@ -1781,19 +1798,6 @@ impl<E: EthSpec> BeaconState<E> {
}
}
/// Get the number of outstanding deposits.
///
/// Returns `Err` if the state is invalid.
pub fn get_outstanding_deposit_len(&self) -> Result<u64, Error> {
self.eth1_data()
.deposit_count
.checked_sub(self.eth1_deposit_index())
.ok_or(Error::InvalidDepositState {
deposit_count: self.eth1_data().deposit_count,
deposit_index: self.eth1_deposit_index(),
})
}
/// Build all caches (except the tree hash cache), if they need to be built.
pub fn build_caches(&mut self, spec: &ChainSpec) -> Result<(), Error> {
self.build_all_committee_caches(spec)?;
@@ -2150,27 +2154,6 @@ impl<E: EthSpec> BeaconState<E> {
.map_err(Into::into)
}
/// Get active balance for the given `validator_index`.
pub fn get_active_balance(
&self,
validator_index: usize,
spec: &ChainSpec,
current_fork: ForkName,
) -> Result<u64, Error> {
let max_effective_balance = self
.validators()
.get(validator_index)
.map(|validator| validator.get_max_effective_balance(spec, current_fork))
.ok_or(Error::UnknownValidator(validator_index))?;
Ok(std::cmp::min(
*self
.balances()
.get(validator_index)
.ok_or(Error::UnknownValidator(validator_index))?,
max_effective_balance,
))
}
pub fn get_pending_balance_to_withdraw(&self, validator_index: usize) -> Result<u64, Error> {
let mut pending_balance = 0;
for withdrawal in self
@@ -2197,42 +2180,18 @@ impl<E: EthSpec> BeaconState<E> {
if *balance > spec.min_activation_balance {
let excess_balance = balance.safe_sub(spec.min_activation_balance)?;
*balance = spec.min_activation_balance;
self.pending_balance_deposits_mut()?
.push(PendingBalanceDeposit {
index: validator_index as u64,
amount: excess_balance,
})?;
let validator = self.get_validator(validator_index)?.clone();
self.pending_deposits_mut()?.push(PendingDeposit {
pubkey: validator.pubkey,
withdrawal_credentials: validator.withdrawal_credentials,
amount: excess_balance,
signature: Signature::infinity()?.into(),
slot: spec.genesis_slot,
})?;
}
Ok(())
}
pub fn queue_entire_balance_and_reset_validator(
&mut self,
validator_index: usize,
spec: &ChainSpec,
) -> Result<(), Error> {
let balance = self
.balances_mut()
.get_mut(validator_index)
.ok_or(Error::UnknownValidator(validator_index))?;
let balance_copy = *balance;
*balance = 0_u64;
let validator = self
.validators_mut()
.get_mut(validator_index)
.ok_or(Error::UnknownValidator(validator_index))?;
validator.effective_balance = 0;
validator.activation_eligibility_epoch = spec.far_future_epoch;
self.pending_balance_deposits_mut()?
.push(PendingBalanceDeposit {
index: validator_index as u64,
amount: balance_copy,
})
.map_err(Into::into)
}
/// Change the withdrawal prefix of the given `validator_index` to the compounding withdrawal validator prefix.
pub fn switch_to_compounding_validator(
&mut self,
@@ -2243,12 +2202,10 @@ impl<E: EthSpec> BeaconState<E> {
.validators_mut()
.get_mut(validator_index)
.ok_or(Error::UnknownValidator(validator_index))?;
if validator.has_eth1_withdrawal_credential(spec) {
AsMut::<[u8; 32]>::as_mut(&mut validator.withdrawal_credentials)[0] =
spec.compounding_withdrawal_prefix_byte;
AsMut::<[u8; 32]>::as_mut(&mut validator.withdrawal_credentials)[0] =
spec.compounding_withdrawal_prefix_byte;
self.queue_excess_active_balance(validator_index, spec)?;
}
self.queue_excess_active_balance(validator_index, spec)?;
Ok(())
}
@@ -2506,33 +2463,64 @@ impl<E: EthSpec> BeaconState<E> {
Ok(())
}
pub fn compute_merkle_proof(&self, generalized_index: usize) -> Result<Vec<Hash256>, Error> {
// 1. Convert generalized index to field index.
let field_index = match generalized_index {
pub fn compute_current_sync_committee_proof(&self) -> Result<Vec<Hash256>, Error> {
// Sync committees are top-level fields, subtract off the generalized indices
// for the internal nodes. Result should be 22 or 23, the field offset of the committee
// in the `BeaconState`:
// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#beaconstate
let field_index = if self.fork_name_unchecked().electra_enabled() {
light_client_update::CURRENT_SYNC_COMMITTEE_INDEX_ELECTRA
} else {
light_client_update::CURRENT_SYNC_COMMITTEE_INDEX
| light_client_update::NEXT_SYNC_COMMITTEE_INDEX => {
// Sync committees are top-level fields, subtract off the generalized indices
// for the internal nodes. Result should be 22 or 23, the field offset of the committee
// in the `BeaconState`:
// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#beaconstate
generalized_index
.checked_sub(self.num_fields_pow2())
.ok_or(Error::IndexNotSupported(generalized_index))?
}
light_client_update::FINALIZED_ROOT_INDEX => {
// Finalized root is the right child of `finalized_checkpoint`, divide by two to get
// the generalized index of `state.finalized_checkpoint`.
let finalized_checkpoint_generalized_index = generalized_index / 2;
// Subtract off the internal nodes. Result should be 105/2 - 32 = 20 which matches
// position of `finalized_checkpoint` in `BeaconState`.
finalized_checkpoint_generalized_index
.checked_sub(self.num_fields_pow2())
.ok_or(Error::IndexNotSupported(generalized_index))?
}
_ => return Err(Error::IndexNotSupported(generalized_index)),
};
let leaves = self.get_beacon_state_leaves();
self.generate_proof(field_index, &leaves)
}
// 2. Get all `BeaconState` leaves.
pub fn compute_next_sync_committee_proof(&self) -> Result<Vec<Hash256>, Error> {
// Sync committees are top-level fields, subtract off the generalized indices
// for the internal nodes. Result should be 22 or 23, the field offset of the committee
// in the `BeaconState`:
// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#beaconstate
let field_index = if self.fork_name_unchecked().electra_enabled() {
light_client_update::NEXT_SYNC_COMMITTEE_INDEX_ELECTRA
} else {
light_client_update::NEXT_SYNC_COMMITTEE_INDEX
};
let leaves = self.get_beacon_state_leaves();
self.generate_proof(field_index, &leaves)
}
pub fn compute_finalized_root_proof(&self) -> Result<Vec<Hash256>, Error> {
// Finalized root is the right child of `finalized_checkpoint`, divide by two to get
// the generalized index of `state.finalized_checkpoint`.
let field_index = if self.fork_name_unchecked().electra_enabled() {
// Index should be 169/2 - 64 = 20 which matches the position
// of `finalized_checkpoint` in `BeaconState`
light_client_update::FINALIZED_ROOT_INDEX_ELECTRA
} else {
// Index should be 105/2 - 32 = 20 which matches the position
// of `finalized_checkpoint` in `BeaconState`
light_client_update::FINALIZED_ROOT_INDEX
};
let leaves = self.get_beacon_state_leaves();
let mut proof = self.generate_proof(field_index, &leaves)?;
proof.insert(0, self.finalized_checkpoint().epoch.tree_hash_root());
Ok(proof)
}
fn generate_proof(
&self,
field_index: usize,
leaves: &[Hash256],
) -> Result<Vec<Hash256>, Error> {
let depth = self.num_fields_pow2().ilog2() as usize;
let tree = merkle_proof::MerkleTree::create(leaves, depth);
let (_, proof) = tree.generate_proof(field_index, depth)?;
Ok(proof)
}
fn get_beacon_state_leaves(&self) -> Vec<Hash256> {
let mut leaves = vec![];
#[allow(clippy::arithmetic_side_effects)]
match self {
@@ -2568,18 +2556,7 @@ impl<E: EthSpec> BeaconState<E> {
}
};
// 3. Make deposit tree.
// Use the depth of the `BeaconState` fields (i.e. `log2(32) = 5`).
let depth = light_client_update::CURRENT_SYNC_COMMITTEE_PROOF_LEN;
let tree = merkle_proof::MerkleTree::create(&leaves, depth);
let (_, mut proof) = tree.generate_proof(field_index, depth)?;
// 4. If we're proving the finalized root, patch in the finalized epoch to complete the proof.
if generalized_index == light_client_update::FINALIZED_ROOT_INDEX {
proof.insert(0, self.finalized_checkpoint().epoch.tree_hash_root());
}
Ok(proof)
leaves
}
}

View File

@@ -27,7 +27,7 @@ impl<'a, E: EthSpec> BlockRootsIter<'a, E> {
}
}
impl<'a, E: EthSpec> Iterator for BlockRootsIter<'a, E> {
impl<E: EthSpec> Iterator for BlockRootsIter<'_, E> {
type Item = Result<(Slot, Hash256), Error>;
fn next(&mut self) -> Option<Self::Item> {

View File

@@ -307,43 +307,6 @@ mod committees {
}
}
mod get_outstanding_deposit_len {
use super::*;
async fn state() -> BeaconState<MinimalEthSpec> {
get_harness(16, Slot::new(0))
.await
.chain
.head_beacon_state_cloned()
}
#[tokio::test]
async fn returns_ok() {
let mut state = state().await;
assert_eq!(state.get_outstanding_deposit_len(), Ok(0));
state.eth1_data_mut().deposit_count = 17;
*state.eth1_deposit_index_mut() = 16;
assert_eq!(state.get_outstanding_deposit_len(), Ok(1));
}
#[tokio::test]
async fn returns_err_if_the_state_is_invalid() {
let mut state = state().await;
// The state is invalid, deposit count is lower than deposit index.
state.eth1_data_mut().deposit_count = 16;
*state.eth1_deposit_index_mut() = 17;
assert_eq!(
state.get_outstanding_deposit_len(),
Err(BeaconStateError::InvalidDepositState {
deposit_count: 16,
deposit_index: 17,
})
);
}
}
#[test]
fn decode_base_and_altair() {
type E = MainnetEthSpec;

View File

@@ -140,6 +140,37 @@ impl<E: EthSpec> BlobSidecar<E> {
})
}
pub fn new_with_existing_proof(
index: usize,
blob: Blob<E>,
signed_block: &SignedBeaconBlock<E>,
signed_block_header: SignedBeaconBlockHeader,
kzg_commitments_inclusion_proof: &[Hash256],
kzg_proof: KzgProof,
) -> Result<Self, BlobSidecarError> {
let expected_kzg_commitments = signed_block
.message()
.body()
.blob_kzg_commitments()
.map_err(|_e| BlobSidecarError::PreDeneb)?;
let kzg_commitment = *expected_kzg_commitments
.get(index)
.ok_or(BlobSidecarError::MissingKzgCommitment)?;
let kzg_commitment_inclusion_proof = signed_block
.message()
.body()
.complete_kzg_commitment_merkle_proof(index, kzg_commitments_inclusion_proof)?;
Ok(Self {
index: index as u64,
blob,
kzg_commitment,
kzg_proof,
signed_block_header,
kzg_commitment_inclusion_proof,
})
}
pub fn id(&self) -> BlobIdentifier {
BlobIdentifier {
block_root: self.block_root(),

View File

@@ -127,6 +127,11 @@ pub struct ChainSpec {
pub deposit_network_id: u64,
pub deposit_contract_address: Address,
/*
* Execution Specs
*/
pub gas_limit_adjustment_factor: u64,
/*
* Altair hard fork params
*/
@@ -204,7 +209,6 @@ pub struct ChainSpec {
pub target_aggregators_per_committee: u64,
pub gossip_max_size: u64,
pub max_request_blocks: u64,
pub epochs_per_subnet_subscription: u64,
pub min_epochs_for_block_requests: u64,
pub max_chunk_size: u64,
pub ttfb_timeout: u64,
@@ -215,9 +219,7 @@ pub struct ChainSpec {
pub message_domain_valid_snappy: [u8; 4],
pub subnets_per_node: u8,
pub attestation_subnet_count: u64,
pub attestation_subnet_extra_bits: u8,
pub attestation_subnet_prefix_bits: u8,
pub attestation_subnet_shuffling_prefix_bits: u8,
/*
* Networking Deneb
@@ -729,6 +731,11 @@ impl ChainSpec {
.parse()
.expect("chain spec deposit contract address"),
/*
* Execution Specs
*/
gas_limit_adjustment_factor: 1024,
/*
* Altair hard fork params
*/
@@ -827,7 +834,6 @@ impl ChainSpec {
subnets_per_node: 2,
maximum_gossip_clock_disparity_millis: default_maximum_gossip_clock_disparity_millis(),
target_aggregators_per_committee: 16,
epochs_per_subnet_subscription: default_epochs_per_subnet_subscription(),
gossip_max_size: default_gossip_max_size(),
min_epochs_for_block_requests: default_min_epochs_for_block_requests(),
max_chunk_size: default_max_chunk_size(),
@@ -835,10 +841,7 @@ impl ChainSpec {
resp_timeout: default_resp_timeout(),
message_domain_invalid_snappy: default_message_domain_invalid_snappy(),
message_domain_valid_snappy: default_message_domain_valid_snappy(),
attestation_subnet_extra_bits: default_attestation_subnet_extra_bits(),
attestation_subnet_prefix_bits: default_attestation_subnet_prefix_bits(),
attestation_subnet_shuffling_prefix_bits:
default_attestation_subnet_shuffling_prefix_bits(),
max_request_blocks: default_max_request_blocks(),
/*
@@ -1048,6 +1051,11 @@ impl ChainSpec {
.parse()
.expect("chain spec deposit contract address"),
/*
* Execution Specs
*/
gas_limit_adjustment_factor: 1024,
/*
* Altair hard fork params
*/
@@ -1145,7 +1153,6 @@ impl ChainSpec {
subnets_per_node: 4, // Make this larger than usual to avoid network damage
maximum_gossip_clock_disparity_millis: default_maximum_gossip_clock_disparity_millis(),
target_aggregators_per_committee: 16,
epochs_per_subnet_subscription: default_epochs_per_subnet_subscription(),
gossip_max_size: default_gossip_max_size(),
min_epochs_for_block_requests: 33024,
max_chunk_size: default_max_chunk_size(),
@@ -1153,11 +1160,8 @@ impl ChainSpec {
resp_timeout: default_resp_timeout(),
message_domain_invalid_snappy: default_message_domain_invalid_snappy(),
message_domain_valid_snappy: default_message_domain_valid_snappy(),
attestation_subnet_extra_bits: default_attestation_subnet_extra_bits(),
attestation_subnet_prefix_bits: default_attestation_subnet_prefix_bits(),
attestation_subnet_shuffling_prefix_bits:
default_attestation_subnet_shuffling_prefix_bits(),
max_request_blocks: default_max_request_blocks(),
attestation_subnet_prefix_bits: default_attestation_subnet_prefix_bits(),
/*
* Networking Deneb Specific
@@ -1309,15 +1313,16 @@ pub struct Config {
#[serde(with = "serde_utils::address_hex")]
deposit_contract_address: Address,
#[serde(default = "default_gas_limit_adjustment_factor")]
#[serde(with = "serde_utils::quoted_u64")]
gas_limit_adjustment_factor: u64,
#[serde(default = "default_gossip_max_size")]
#[serde(with = "serde_utils::quoted_u64")]
gossip_max_size: u64,
#[serde(default = "default_max_request_blocks")]
#[serde(with = "serde_utils::quoted_u64")]
max_request_blocks: u64,
#[serde(default = "default_epochs_per_subnet_subscription")]
#[serde(with = "serde_utils::quoted_u64")]
epochs_per_subnet_subscription: u64,
#[serde(default = "default_min_epochs_for_block_requests")]
#[serde(with = "serde_utils::quoted_u64")]
min_epochs_for_block_requests: u64,
@@ -1342,15 +1347,9 @@ pub struct Config {
#[serde(default = "default_message_domain_valid_snappy")]
#[serde(with = "serde_utils::bytes_4_hex")]
message_domain_valid_snappy: [u8; 4],
#[serde(default = "default_attestation_subnet_extra_bits")]
#[serde(with = "serde_utils::quoted_u8")]
attestation_subnet_extra_bits: u8,
#[serde(default = "default_attestation_subnet_prefix_bits")]
#[serde(with = "serde_utils::quoted_u8")]
attestation_subnet_prefix_bits: u8,
#[serde(default = "default_attestation_subnet_shuffling_prefix_bits")]
#[serde(with = "serde_utils::quoted_u8")]
attestation_subnet_shuffling_prefix_bits: u8,
#[serde(default = "default_max_request_blocks_deneb")]
#[serde(with = "serde_utils::quoted_u64")]
max_request_blocks_deneb: u64,
@@ -1435,10 +1434,18 @@ fn default_subnets_per_node() -> u8 {
2u8
}
fn default_attestation_subnet_prefix_bits() -> u8 {
6
}
const fn default_max_per_epoch_activation_churn_limit() -> u64 {
8
}
const fn default_gas_limit_adjustment_factor() -> u64 {
1024
}
const fn default_gossip_max_size() -> u64 {
10485760
}
@@ -1467,18 +1474,6 @@ const fn default_message_domain_valid_snappy() -> [u8; 4] {
[1, 0, 0, 0]
}
const fn default_attestation_subnet_extra_bits() -> u8 {
0
}
const fn default_attestation_subnet_prefix_bits() -> u8 {
6
}
const fn default_attestation_subnet_shuffling_prefix_bits() -> u8 {
3
}
const fn default_max_request_blocks() -> u64 {
1024
}
@@ -1517,10 +1512,6 @@ const fn default_max_per_epoch_activation_exit_churn_limit() -> u64 {
256_000_000_000
}
const fn default_epochs_per_subnet_subscription() -> u64 {
256
}
const fn default_attestation_propagation_slot_range() -> u64 {
32
}
@@ -1698,6 +1689,7 @@ impl Config {
shard_committee_period: spec.shard_committee_period,
eth1_follow_distance: spec.eth1_follow_distance,
subnets_per_node: spec.subnets_per_node,
attestation_subnet_prefix_bits: spec.attestation_subnet_prefix_bits,
inactivity_score_bias: spec.inactivity_score_bias,
inactivity_score_recovery_rate: spec.inactivity_score_recovery_rate,
@@ -1712,9 +1704,10 @@ impl Config {
deposit_network_id: spec.deposit_network_id,
deposit_contract_address: spec.deposit_contract_address,
gas_limit_adjustment_factor: spec.gas_limit_adjustment_factor,
gossip_max_size: spec.gossip_max_size,
max_request_blocks: spec.max_request_blocks,
epochs_per_subnet_subscription: spec.epochs_per_subnet_subscription,
min_epochs_for_block_requests: spec.min_epochs_for_block_requests,
max_chunk_size: spec.max_chunk_size,
ttfb_timeout: spec.ttfb_timeout,
@@ -1723,9 +1716,6 @@ impl Config {
maximum_gossip_clock_disparity_millis: spec.maximum_gossip_clock_disparity_millis,
message_domain_invalid_snappy: spec.message_domain_invalid_snappy,
message_domain_valid_snappy: spec.message_domain_valid_snappy,
attestation_subnet_extra_bits: spec.attestation_subnet_extra_bits,
attestation_subnet_prefix_bits: spec.attestation_subnet_prefix_bits,
attestation_subnet_shuffling_prefix_bits: spec.attestation_subnet_shuffling_prefix_bits,
max_request_blocks_deneb: spec.max_request_blocks_deneb,
max_request_blob_sidecars: spec.max_request_blob_sidecars,
max_request_data_column_sidecars: spec.max_request_data_column_sidecars,
@@ -1780,6 +1770,7 @@ impl Config {
shard_committee_period,
eth1_follow_distance,
subnets_per_node,
attestation_subnet_prefix_bits,
inactivity_score_bias,
inactivity_score_recovery_rate,
ejection_balance,
@@ -1790,6 +1781,7 @@ impl Config {
deposit_chain_id,
deposit_network_id,
deposit_contract_address,
gas_limit_adjustment_factor,
gossip_max_size,
min_epochs_for_block_requests,
max_chunk_size,
@@ -1797,11 +1789,7 @@ impl Config {
resp_timeout,
message_domain_invalid_snappy,
message_domain_valid_snappy,
attestation_subnet_extra_bits,
attestation_subnet_prefix_bits,
attestation_subnet_shuffling_prefix_bits,
max_request_blocks,
epochs_per_subnet_subscription,
attestation_propagation_slot_range,
maximum_gossip_clock_disparity_millis,
max_request_blocks_deneb,
@@ -1856,6 +1844,7 @@ impl Config {
deposit_chain_id,
deposit_network_id,
deposit_contract_address,
gas_limit_adjustment_factor,
terminal_total_difficulty,
terminal_block_hash,
terminal_block_hash_activation_epoch,
@@ -1866,11 +1855,8 @@ impl Config {
resp_timeout,
message_domain_invalid_snappy,
message_domain_valid_snappy,
attestation_subnet_extra_bits,
attestation_subnet_prefix_bits,
attestation_subnet_shuffling_prefix_bits,
max_request_blocks,
epochs_per_subnet_subscription,
attestation_propagation_slot_range,
maximum_gossip_clock_disparity_millis,
max_request_blocks_deneb,
@@ -2167,9 +2153,7 @@ mod yaml_tests {
check_default!(resp_timeout);
check_default!(message_domain_invalid_snappy);
check_default!(message_domain_valid_snappy);
check_default!(attestation_subnet_extra_bits);
check_default!(attestation_subnet_prefix_bits);
check_default!(attestation_subnet_shuffling_prefix_bits);
assert_eq!(chain_spec.bellatrix_fork_epoch, None);
}

View File

@@ -1,7 +1,7 @@
use crate::beacon_block_body::{KzgCommitments, BLOB_KZG_COMMITMENTS_INDEX};
use crate::test_utils::TestRandom;
use crate::BeaconStateError;
use crate::{BeaconBlockHeader, EthSpec, Hash256, KzgProofs, SignedBeaconBlockHeader, Slot};
use crate::{BeaconBlockHeader, Epoch, EthSpec, Hash256, KzgProofs, SignedBeaconBlockHeader, Slot};
use bls::Signature;
use derivative::Derivative;
use kzg::Error as KzgError;
@@ -67,6 +67,10 @@ impl<E: EthSpec> DataColumnSidecar<E> {
self.signed_block_header.message.slot
}
pub fn epoch(&self) -> Epoch {
self.slot().epoch(E::slots_per_epoch())
}
pub fn block_root(&self) -> Hash256 {
self.signed_block_header.message.tree_hash_root()
}

View File

@@ -1,5 +1,6 @@
use crate::test_utils::TestRandom;
use crate::{Hash256, PublicKeyBytes, Signature};
use crate::{Hash256, PublicKeyBytes};
use bls::SignatureBytes;
use serde::{Deserialize, Serialize};
use ssz::Encode;
use ssz_derive::{Decode, Encode};
@@ -10,7 +11,6 @@ use tree_hash_derive::TreeHash;
arbitrary::Arbitrary,
Debug,
PartialEq,
Eq,
Hash,
Clone,
Serialize,
@@ -25,7 +25,7 @@ pub struct DepositRequest {
pub withdrawal_credentials: Hash256,
#[serde(with = "serde_utils::quoted_u64")]
pub amount: u64,
pub signature: Signature,
pub signature: SignatureBytes,
#[serde(with = "serde_utils::quoted_u64")]
pub index: u64,
}
@@ -36,7 +36,7 @@ impl DepositRequest {
pubkey: PublicKeyBytes::empty(),
withdrawal_credentials: Hash256::ZERO,
amount: 0,
signature: Signature::empty(),
signature: SignatureBytes::empty(),
index: 0,
}
.as_ssz_bytes()

View File

@@ -149,7 +149,7 @@ pub trait EthSpec:
/*
* New in Electra
*/
type PendingBalanceDepositsLimit: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type PendingDepositsLimit: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type PendingPartialWithdrawalsLimit: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type PendingConsolidationsLimit: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type MaxConsolidationRequestsPerPayload: Unsigned + Clone + Sync + Send + Debug + PartialEq;
@@ -157,6 +157,7 @@ pub trait EthSpec:
type MaxAttesterSlashingsElectra: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type MaxAttestationsElectra: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type MaxWithdrawalRequestsPerPayload: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type MaxPendingDepositsPerEpoch: Unsigned + Clone + Sync + Send + Debug + PartialEq;
fn default_spec() -> ChainSpec;
@@ -324,9 +325,9 @@ pub trait EthSpec:
.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()
/// Returns the `PENDING_DEPOSITS_LIMIT` constant for this specification.
fn pending_deposits_limit() -> usize {
Self::PendingDepositsLimit::to_usize()
}
/// Returns the `PENDING_PARTIAL_WITHDRAWALS_LIMIT` constant for this specification.
@@ -364,6 +365,11 @@ pub trait EthSpec:
Self::MaxWithdrawalRequestsPerPayload::to_usize()
}
/// Returns the `MAX_PENDING_DEPOSITS_PER_EPOCH` constant for this specification.
fn max_pending_deposits_per_epoch() -> usize {
Self::MaxPendingDepositsPerEpoch::to_usize()
}
fn kzg_commitments_inclusion_proof_depth() -> usize {
Self::KzgCommitmentsInclusionProofDepth::to_usize()
}
@@ -422,7 +428,7 @@ impl EthSpec for MainnetEthSpec {
type SlotsPerEth1VotingPeriod = U2048; // 64 epochs * 32 slots per epoch
type MaxBlsToExecutionChanges = U16;
type MaxWithdrawalsPerPayload = U16;
type PendingBalanceDepositsLimit = U134217728;
type PendingDepositsLimit = U134217728;
type PendingPartialWithdrawalsLimit = U134217728;
type PendingConsolidationsLimit = U262144;
type MaxConsolidationRequestsPerPayload = U1;
@@ -430,6 +436,7 @@ impl EthSpec for MainnetEthSpec {
type MaxAttesterSlashingsElectra = U1;
type MaxAttestationsElectra = U8;
type MaxWithdrawalRequestsPerPayload = U16;
type MaxPendingDepositsPerEpoch = U16;
fn default_spec() -> ChainSpec {
ChainSpec::mainnet()
@@ -491,7 +498,8 @@ impl EthSpec for MinimalEthSpec {
MaxExtraDataBytes,
MaxBlsToExecutionChanges,
BytesPerFieldElement,
PendingBalanceDepositsLimit,
PendingDepositsLimit,
MaxPendingDepositsPerEpoch,
MaxConsolidationRequestsPerPayload,
MaxAttesterSlashingsElectra,
MaxAttestationsElectra
@@ -547,7 +555,7 @@ impl EthSpec for GnosisEthSpec {
type BytesPerFieldElement = U32;
type BytesPerBlob = U131072;
type KzgCommitmentInclusionProofDepth = U17;
type PendingBalanceDepositsLimit = U134217728;
type PendingDepositsLimit = U134217728;
type PendingPartialWithdrawalsLimit = U134217728;
type PendingConsolidationsLimit = U262144;
type MaxConsolidationRequestsPerPayload = U1;
@@ -555,6 +563,7 @@ impl EthSpec for GnosisEthSpec {
type MaxAttesterSlashingsElectra = U1;
type MaxAttestationsElectra = U8;
type MaxWithdrawalRequestsPerPayload = U16;
type MaxPendingDepositsPerEpoch = U16;
type FieldElementsPerCell = U64;
type FieldElementsPerExtBlob = U8192;
type BytesPerCell = U2048;

View File

@@ -52,9 +52,11 @@ pub struct ExecutionBlockHeader {
pub blob_gas_used: Option<u64>,
pub excess_blob_gas: Option<u64>,
pub parent_beacon_block_root: Option<Hash256>,
pub requests_root: Option<Hash256>,
}
impl ExecutionBlockHeader {
#[allow(clippy::too_many_arguments)]
pub fn from_payload<E: EthSpec>(
payload: ExecutionPayloadRef<E>,
rlp_empty_list_root: Hash256,
@@ -63,6 +65,7 @@ impl ExecutionBlockHeader {
rlp_blob_gas_used: Option<u64>,
rlp_excess_blob_gas: Option<u64>,
rlp_parent_beacon_block_root: Option<Hash256>,
rlp_requests_root: Option<Hash256>,
) -> Self {
// Most of these field mappings are defined in EIP-3675 except for `mixHash`, which is
// defined in EIP-4399.
@@ -87,6 +90,7 @@ impl ExecutionBlockHeader {
blob_gas_used: rlp_blob_gas_used,
excess_blob_gas: rlp_excess_blob_gas,
parent_beacon_block_root: rlp_parent_beacon_block_root,
requests_root: rlp_requests_root,
}
}
}
@@ -114,6 +118,7 @@ pub struct EncodableExecutionBlockHeader<'a> {
pub blob_gas_used: Option<u64>,
pub excess_blob_gas: Option<u64>,
pub parent_beacon_block_root: Option<&'a [u8]>,
pub requests_root: Option<&'a [u8]>,
}
impl<'a> From<&'a ExecutionBlockHeader> for EncodableExecutionBlockHeader<'a> {
@@ -139,6 +144,7 @@ impl<'a> From<&'a ExecutionBlockHeader> for EncodableExecutionBlockHeader<'a> {
blob_gas_used: header.blob_gas_used,
excess_blob_gas: header.excess_blob_gas,
parent_beacon_block_root: None,
requests_root: None,
};
if let Some(withdrawals_root) = &header.withdrawals_root {
encodable.withdrawals_root = Some(withdrawals_root.as_slice());
@@ -146,6 +152,9 @@ impl<'a> From<&'a ExecutionBlockHeader> for EncodableExecutionBlockHeader<'a> {
if let Some(parent_beacon_block_root) = &header.parent_beacon_block_root {
encodable.parent_beacon_block_root = Some(parent_beacon_block_root.as_slice())
}
if let Some(requests_root) = &header.requests_root {
encodable.requests_root = Some(requests_root.as_slice())
}
encodable
}
}

View File

@@ -371,7 +371,7 @@ impl<E: EthSpec> TryFrom<ExecutionPayloadHeader<E>> for ExecutionPayloadHeaderDe
}
}
impl<'a, E: EthSpec> ExecutionPayloadHeaderRefMut<'a, E> {
impl<E: EthSpec> ExecutionPayloadHeaderRefMut<'_, E> {
/// Mutate through
pub fn replace(self, header: ExecutionPayloadHeader<E>) -> Result<(), BeaconStateError> {
match self {

View File

@@ -1,7 +1,8 @@
use crate::test_utils::TestRandom;
use crate::{ConsolidationRequest, DepositRequest, EthSpec, WithdrawalRequest};
use crate::{ConsolidationRequest, DepositRequest, EthSpec, Hash256, WithdrawalRequest};
use alloy_primitives::Bytes;
use derivative::Derivative;
use ethereum_hashing::{DynamicContext, Sha256Context};
use serde::{Deserialize, Serialize};
use ssz::Encode;
use ssz_derive::{Decode, Encode};
@@ -47,6 +48,43 @@ impl<E: EthSpec> ExecutionRequests<E> {
let consolidation_bytes = Bytes::from(self.consolidations.as_ssz_bytes());
vec![deposit_bytes, withdrawal_bytes, consolidation_bytes]
}
/// Generate the execution layer `requests_hash` based on EIP-7685.
///
/// `sha256(sha256(requests_0) ++ sha256(requests_1) ++ ...)`
pub fn requests_hash(&self) -> Hash256 {
let mut hasher = DynamicContext::new();
for (i, request) in self.get_execution_requests_list().iter().enumerate() {
let mut request_hasher = DynamicContext::new();
request_hasher.update(&[i as u8]);
request_hasher.update(request);
let request_hash = request_hasher.finalize();
hasher.update(&request_hash);
}
hasher.finalize().into()
}
}
/// This is used to index into the `execution_requests` array.
#[derive(Debug, Copy, Clone)]
pub enum RequestPrefix {
Deposit,
Withdrawal,
Consolidation,
}
impl RequestPrefix {
pub fn from_prefix(prefix: u8) -> Option<Self> {
match prefix {
0 => Some(Self::Deposit),
1 => Some(Self::Withdrawal),
2 => Some(Self::Consolidation),
_ => None,
}
}
}
#[cfg(test)]

View File

@@ -15,6 +15,7 @@ use tree_hash_derive::TreeHash;
#[derive(
Debug,
PartialEq,
Eq,
Serialize,
Deserialize,
Encode,

View File

@@ -134,7 +134,7 @@ impl<E: EthSpec> IndexedAttestation<E> {
}
}
impl<'a, E: EthSpec> IndexedAttestationRef<'a, E> {
impl<E: EthSpec> IndexedAttestationRef<'_, E> {
pub fn is_double_vote(&self, other: Self) -> bool {
self.data().target.epoch == other.data().target.epoch && self.data() != other.data()
}

View File

@@ -54,8 +54,8 @@ pub mod light_client_finality_update;
pub mod light_client_optimistic_update;
pub mod light_client_update;
pub mod pending_attestation;
pub mod pending_balance_deposit;
pub mod pending_consolidation;
pub mod pending_deposit;
pub mod pending_partial_withdrawal;
pub mod proposer_preparation_data;
pub mod proposer_slashing;
@@ -170,7 +170,7 @@ pub use crate::execution_payload_header::{
ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderRef,
ExecutionPayloadHeaderRefMut,
};
pub use crate::execution_requests::ExecutionRequests;
pub use crate::execution_requests::{ExecutionRequests, RequestPrefix};
pub use crate::fork::Fork;
pub use crate::fork_context::ForkContext;
pub use crate::fork_data::ForkData;
@@ -200,7 +200,7 @@ pub use crate::light_client_optimistic_update::{
};
pub use crate::light_client_update::{
Error as LightClientUpdateError, LightClientUpdate, LightClientUpdateAltair,
LightClientUpdateCapella, LightClientUpdateDeneb, LightClientUpdateElectra,
LightClientUpdateCapella, LightClientUpdateDeneb, LightClientUpdateElectra, MerkleProof,
};
pub use crate::participation_flags::ParticipationFlags;
pub use crate::payload::{
@@ -210,8 +210,8 @@ pub use crate::payload::{
FullPayloadRef, OwnedExecPayload,
};
pub use crate::pending_attestation::PendingAttestation;
pub use crate::pending_balance_deposit::PendingBalanceDeposit;
pub use crate::pending_consolidation::PendingConsolidation;
pub use crate::pending_deposit::PendingDeposit;
pub use crate::pending_partial_withdrawal::PendingPartialWithdrawal;
pub use crate::preset::{
AltairPreset, BasePreset, BellatrixPreset, CapellaPreset, DenebPreset, ElectraPreset,

View File

@@ -57,7 +57,16 @@ pub struct LightClientBootstrap<E: EthSpec> {
/// The `SyncCommittee` used in the requested period.
pub current_sync_committee: Arc<SyncCommittee<E>>,
/// Merkle proof for sync committee
#[superstruct(
only(Altair, Capella, Deneb),
partial_getter(rename = "current_sync_committee_branch_altair")
)]
pub current_sync_committee_branch: FixedVector<Hash256, CurrentSyncCommitteeProofLen>,
#[superstruct(
only(Electra),
partial_getter(rename = "current_sync_committee_branch_electra")
)]
pub current_sync_committee_branch: FixedVector<Hash256, CurrentSyncCommitteeProofLenElectra>,
}
impl<E: EthSpec> LightClientBootstrap<E> {
@@ -115,7 +124,7 @@ impl<E: EthSpec> LightClientBootstrap<E> {
pub fn new(
block: &SignedBlindedBeaconBlock<E>,
current_sync_committee: Arc<SyncCommittee<E>>,
current_sync_committee_branch: FixedVector<Hash256, CurrentSyncCommitteeProofLen>,
current_sync_committee_branch: Vec<Hash256>,
chain_spec: &ChainSpec,
) -> Result<Self, Error> {
let light_client_bootstrap = match block
@@ -126,22 +135,22 @@ impl<E: EthSpec> LightClientBootstrap<E> {
ForkName::Altair | ForkName::Bellatrix => Self::Altair(LightClientBootstrapAltair {
header: LightClientHeaderAltair::block_to_light_client_header(block)?,
current_sync_committee,
current_sync_committee_branch,
current_sync_committee_branch: current_sync_committee_branch.into(),
}),
ForkName::Capella => Self::Capella(LightClientBootstrapCapella {
header: LightClientHeaderCapella::block_to_light_client_header(block)?,
current_sync_committee,
current_sync_committee_branch,
current_sync_committee_branch: current_sync_committee_branch.into(),
}),
ForkName::Deneb => Self::Deneb(LightClientBootstrapDeneb {
header: LightClientHeaderDeneb::block_to_light_client_header(block)?,
current_sync_committee,
current_sync_committee_branch,
current_sync_committee_branch: current_sync_committee_branch.into(),
}),
ForkName::Electra => Self::Electra(LightClientBootstrapElectra {
header: LightClientHeaderElectra::block_to_light_client_header(block)?,
current_sync_committee,
current_sync_committee_branch,
current_sync_committee_branch: current_sync_committee_branch.into(),
}),
};
@@ -155,9 +164,7 @@ impl<E: EthSpec> LightClientBootstrap<E> {
) -> Result<Self, Error> {
let mut header = beacon_state.latest_block_header().clone();
header.state_root = beacon_state.update_tree_hash_cache()?;
let current_sync_committee_branch =
FixedVector::new(beacon_state.compute_merkle_proof(CURRENT_SYNC_COMMITTEE_INDEX)?)?;
let current_sync_committee_branch = beacon_state.compute_current_sync_committee_proof()?;
let current_sync_committee = beacon_state.current_sync_committee()?.clone();
let light_client_bootstrap = match block
@@ -168,22 +175,22 @@ impl<E: EthSpec> LightClientBootstrap<E> {
ForkName::Altair | ForkName::Bellatrix => Self::Altair(LightClientBootstrapAltair {
header: LightClientHeaderAltair::block_to_light_client_header(block)?,
current_sync_committee,
current_sync_committee_branch,
current_sync_committee_branch: current_sync_committee_branch.into(),
}),
ForkName::Capella => Self::Capella(LightClientBootstrapCapella {
header: LightClientHeaderCapella::block_to_light_client_header(block)?,
current_sync_committee,
current_sync_committee_branch,
current_sync_committee_branch: current_sync_committee_branch.into(),
}),
ForkName::Deneb => Self::Deneb(LightClientBootstrapDeneb {
header: LightClientHeaderDeneb::block_to_light_client_header(block)?,
current_sync_committee,
current_sync_committee_branch,
current_sync_committee_branch: current_sync_committee_branch.into(),
}),
ForkName::Electra => Self::Electra(LightClientBootstrapElectra {
header: LightClientHeaderElectra::block_to_light_client_header(block)?,
current_sync_committee,
current_sync_committee_branch,
current_sync_committee_branch: current_sync_committee_branch.into(),
}),
};
@@ -210,8 +217,28 @@ impl<E: EthSpec> ForkVersionDeserialize for LightClientBootstrap<E> {
#[cfg(test)]
mod tests {
use super::*;
use crate::MainnetEthSpec;
// `ssz_tests!` can only be defined once per namespace
#[cfg(test)]
mod altair {
use crate::{LightClientBootstrapAltair, MainnetEthSpec};
ssz_tests!(LightClientBootstrapAltair<MainnetEthSpec>);
}
ssz_tests!(LightClientBootstrapDeneb<MainnetEthSpec>);
#[cfg(test)]
mod capella {
use crate::{LightClientBootstrapCapella, MainnetEthSpec};
ssz_tests!(LightClientBootstrapCapella<MainnetEthSpec>);
}
#[cfg(test)]
mod deneb {
use crate::{LightClientBootstrapDeneb, MainnetEthSpec};
ssz_tests!(LightClientBootstrapDeneb<MainnetEthSpec>);
}
#[cfg(test)]
mod electra {
use crate::{LightClientBootstrapElectra, MainnetEthSpec};
ssz_tests!(LightClientBootstrapElectra<MainnetEthSpec>);
}
}

View File

@@ -63,8 +63,13 @@ pub struct LightClientFinalityUpdate<E: EthSpec> {
#[superstruct(only(Electra), partial_getter(rename = "finalized_header_electra"))]
pub finalized_header: LightClientHeaderElectra<E>,
/// Merkle proof attesting finalized header.
#[test_random(default)]
#[superstruct(
only(Altair, Capella, Deneb),
partial_getter(rename = "finality_branch_altair")
)]
pub finality_branch: FixedVector<Hash256, FinalizedRootProofLen>,
#[superstruct(only(Electra), partial_getter(rename = "finality_branch_electra"))]
pub finality_branch: FixedVector<Hash256, FinalizedRootProofLenElectra>,
/// current sync aggregate
pub sync_aggregate: SyncAggregate<E>,
/// Slot of the sync aggregated signature
@@ -75,7 +80,7 @@ impl<E: EthSpec> LightClientFinalityUpdate<E> {
pub fn new(
attested_block: &SignedBlindedBeaconBlock<E>,
finalized_block: &SignedBlindedBeaconBlock<E>,
finality_branch: FixedVector<Hash256, FinalizedRootProofLen>,
finality_branch: Vec<Hash256>,
sync_aggregate: SyncAggregate<E>,
signature_slot: Slot,
chain_spec: &ChainSpec,
@@ -92,7 +97,7 @@ impl<E: EthSpec> LightClientFinalityUpdate<E> {
finalized_header: LightClientHeaderAltair::block_to_light_client_header(
finalized_block,
)?,
finality_branch,
finality_branch: finality_branch.into(),
sync_aggregate,
signature_slot,
})
@@ -104,7 +109,7 @@ impl<E: EthSpec> LightClientFinalityUpdate<E> {
finalized_header: LightClientHeaderCapella::block_to_light_client_header(
finalized_block,
)?,
finality_branch,
finality_branch: finality_branch.into(),
sync_aggregate,
signature_slot,
}),
@@ -115,7 +120,7 @@ impl<E: EthSpec> LightClientFinalityUpdate<E> {
finalized_header: LightClientHeaderDeneb::block_to_light_client_header(
finalized_block,
)?,
finality_branch,
finality_branch: finality_branch.into(),
sync_aggregate,
signature_slot,
}),
@@ -126,7 +131,7 @@ impl<E: EthSpec> LightClientFinalityUpdate<E> {
finalized_header: LightClientHeaderElectra::block_to_light_client_header(
finalized_block,
)?,
finality_branch,
finality_branch: finality_branch.into(),
sync_aggregate,
signature_slot,
}),
@@ -226,8 +231,28 @@ impl<E: EthSpec> ForkVersionDeserialize for LightClientFinalityUpdate<E> {
#[cfg(test)]
mod tests {
use super::*;
use crate::MainnetEthSpec;
// `ssz_tests!` can only be defined once per namespace
#[cfg(test)]
mod altair {
use crate::{LightClientFinalityUpdateAltair, MainnetEthSpec};
ssz_tests!(LightClientFinalityUpdateAltair<MainnetEthSpec>);
}
ssz_tests!(LightClientFinalityUpdateDeneb<MainnetEthSpec>);
#[cfg(test)]
mod capella {
use crate::{LightClientFinalityUpdateCapella, MainnetEthSpec};
ssz_tests!(LightClientFinalityUpdateCapella<MainnetEthSpec>);
}
#[cfg(test)]
mod deneb {
use crate::{LightClientFinalityUpdateDeneb, MainnetEthSpec};
ssz_tests!(LightClientFinalityUpdateDeneb<MainnetEthSpec>);
}
#[cfg(test)]
mod electra {
use crate::{LightClientFinalityUpdateElectra, MainnetEthSpec};
ssz_tests!(LightClientFinalityUpdateElectra<MainnetEthSpec>);
}
}

View File

@@ -179,12 +179,12 @@ impl<E: EthSpec> LightClientHeaderCapella<E> {
.to_ref()
.block_body_merkle_proof(EXECUTION_PAYLOAD_INDEX)?;
return Ok(LightClientHeaderCapella {
Ok(LightClientHeaderCapella {
beacon: block.message().block_header(),
execution: header,
execution_branch: FixedVector::new(execution_branch)?,
_phantom_data: PhantomData,
});
})
}
}
@@ -307,3 +307,31 @@ impl<E: EthSpec> ForkVersionDeserialize for LightClientHeader<E> {
}
}
}
#[cfg(test)]
mod tests {
// `ssz_tests!` can only be defined once per namespace
#[cfg(test)]
mod altair {
use crate::{LightClientHeaderAltair, MainnetEthSpec};
ssz_tests!(LightClientHeaderAltair<MainnetEthSpec>);
}
#[cfg(test)]
mod capella {
use crate::{LightClientHeaderCapella, MainnetEthSpec};
ssz_tests!(LightClientHeaderCapella<MainnetEthSpec>);
}
#[cfg(test)]
mod deneb {
use crate::{LightClientHeaderDeneb, MainnetEthSpec};
ssz_tests!(LightClientHeaderDeneb<MainnetEthSpec>);
}
#[cfg(test)]
mod electra {
use crate::{LightClientHeaderElectra, MainnetEthSpec};
ssz_tests!(LightClientHeaderElectra<MainnetEthSpec>);
}
}

View File

@@ -214,8 +214,28 @@ impl<E: EthSpec> ForkVersionDeserialize for LightClientOptimisticUpdate<E> {
#[cfg(test)]
mod tests {
use super::*;
use crate::MainnetEthSpec;
// `ssz_tests!` can only be defined once per namespace
#[cfg(test)]
mod altair {
use crate::{LightClientOptimisticUpdateAltair, MainnetEthSpec};
ssz_tests!(LightClientOptimisticUpdateAltair<MainnetEthSpec>);
}
ssz_tests!(LightClientOptimisticUpdateDeneb<MainnetEthSpec>);
#[cfg(test)]
mod capella {
use crate::{LightClientOptimisticUpdateCapella, MainnetEthSpec};
ssz_tests!(LightClientOptimisticUpdateCapella<MainnetEthSpec>);
}
#[cfg(test)]
mod deneb {
use crate::{LightClientOptimisticUpdateDeneb, MainnetEthSpec};
ssz_tests!(LightClientOptimisticUpdateDeneb<MainnetEthSpec>);
}
#[cfg(test)]
mod electra {
use crate::{LightClientOptimisticUpdateElectra, MainnetEthSpec};
ssz_tests!(LightClientOptimisticUpdateElectra<MainnetEthSpec>);
}
}

View File

@@ -14,7 +14,7 @@ use serde_json::Value;
use ssz::{Decode, Encode};
use ssz_derive::Decode;
use ssz_derive::Encode;
use ssz_types::typenum::{U4, U5, U6};
use ssz_types::typenum::{U4, U5, U6, U7};
use std::sync::Arc;
use superstruct::superstruct;
use test_random_derive::TestRandom;
@@ -25,24 +25,39 @@ pub const CURRENT_SYNC_COMMITTEE_INDEX: usize = 54;
pub const NEXT_SYNC_COMMITTEE_INDEX: usize = 55;
pub const EXECUTION_PAYLOAD_INDEX: usize = 25;
pub const FINALIZED_ROOT_INDEX_ELECTRA: usize = 169;
pub const CURRENT_SYNC_COMMITTEE_INDEX_ELECTRA: usize = 86;
pub const NEXT_SYNC_COMMITTEE_INDEX_ELECTRA: usize = 87;
pub type FinalizedRootProofLen = U6;
pub type CurrentSyncCommitteeProofLen = U5;
pub type ExecutionPayloadProofLen = U4;
pub type NextSyncCommitteeProofLen = U5;
pub type FinalizedRootProofLenElectra = U7;
pub type CurrentSyncCommitteeProofLenElectra = U6;
pub type NextSyncCommitteeProofLenElectra = U6;
pub const FINALIZED_ROOT_PROOF_LEN: usize = 6;
pub const CURRENT_SYNC_COMMITTEE_PROOF_LEN: usize = 5;
pub const NEXT_SYNC_COMMITTEE_PROOF_LEN: usize = 5;
pub const EXECUTION_PAYLOAD_PROOF_LEN: usize = 4;
pub const FINALIZED_ROOT_PROOF_LEN_ELECTRA: usize = 7;
pub const NEXT_SYNC_COMMITTEE_PROOF_LEN_ELECTRA: usize = 6;
pub const CURRENT_SYNC_COMMITTEE_PROOF_LEN_ELECTRA: usize = 6;
pub type MerkleProof = Vec<Hash256>;
// Max light client updates by range request limits
// spec: https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/p2p-interface.md#configuration
pub const MAX_REQUEST_LIGHT_CLIENT_UPDATES: u64 = 128;
type FinalityBranch = FixedVector<Hash256, FinalizedRootProofLen>;
type FinalityBranchElectra = FixedVector<Hash256, FinalizedRootProofLenElectra>;
type NextSyncCommitteeBranch = FixedVector<Hash256, NextSyncCommitteeProofLen>;
type NextSyncCommitteeBranchElectra = FixedVector<Hash256, NextSyncCommitteeProofLenElectra>;
#[derive(Debug, PartialEq, Clone)]
pub enum Error {
SszTypesError(ssz_types::Error),
@@ -124,8 +139,17 @@ pub struct LightClientUpdate<E: EthSpec> {
pub attested_header: LightClientHeaderElectra<E>,
/// The `SyncCommittee` used in the next period.
pub next_sync_committee: Arc<SyncCommittee<E>>,
/// Merkle proof for next sync committee
// Merkle proof for next sync committee
#[superstruct(
only(Altair, Capella, Deneb),
partial_getter(rename = "next_sync_committee_branch_altair")
)]
pub next_sync_committee_branch: NextSyncCommitteeBranch,
#[superstruct(
only(Electra),
partial_getter(rename = "next_sync_committee_branch_electra")
)]
pub next_sync_committee_branch: NextSyncCommitteeBranchElectra,
/// The last `BeaconBlockHeader` from the last attested finalized block (end of epoch).
#[superstruct(only(Altair), partial_getter(rename = "finalized_header_altair"))]
pub finalized_header: LightClientHeaderAltair<E>,
@@ -136,7 +160,13 @@ pub struct LightClientUpdate<E: EthSpec> {
#[superstruct(only(Electra), partial_getter(rename = "finalized_header_electra"))]
pub finalized_header: LightClientHeaderElectra<E>,
/// Merkle proof attesting finalized header.
#[superstruct(
only(Altair, Capella, Deneb),
partial_getter(rename = "finality_branch_altair")
)]
pub finality_branch: FinalityBranch,
#[superstruct(only(Electra), partial_getter(rename = "finality_branch_electra"))]
pub finality_branch: FinalityBranchElectra,
/// current sync aggreggate
pub sync_aggregate: SyncAggregate<E>,
/// Slot of the sync aggregated signature
@@ -165,8 +195,8 @@ impl<E: EthSpec> LightClientUpdate<E> {
sync_aggregate: &SyncAggregate<E>,
block_slot: Slot,
next_sync_committee: Arc<SyncCommittee<E>>,
next_sync_committee_branch: FixedVector<Hash256, NextSyncCommitteeProofLen>,
finality_branch: FixedVector<Hash256, FinalizedRootProofLen>,
next_sync_committee_branch: Vec<Hash256>,
finality_branch: Vec<Hash256>,
attested_block: &SignedBlindedBeaconBlock<E>,
finalized_block: Option<&SignedBlindedBeaconBlock<E>>,
chain_spec: &ChainSpec,
@@ -189,9 +219,9 @@ impl<E: EthSpec> LightClientUpdate<E> {
Self::Altair(LightClientUpdateAltair {
attested_header,
next_sync_committee,
next_sync_committee_branch,
next_sync_committee_branch: next_sync_committee_branch.into(),
finalized_header,
finality_branch,
finality_branch: finality_branch.into(),
sync_aggregate: sync_aggregate.clone(),
signature_slot: block_slot,
})
@@ -209,9 +239,9 @@ impl<E: EthSpec> LightClientUpdate<E> {
Self::Capella(LightClientUpdateCapella {
attested_header,
next_sync_committee,
next_sync_committee_branch,
next_sync_committee_branch: next_sync_committee_branch.into(),
finalized_header,
finality_branch,
finality_branch: finality_branch.into(),
sync_aggregate: sync_aggregate.clone(),
signature_slot: block_slot,
})
@@ -229,9 +259,9 @@ impl<E: EthSpec> LightClientUpdate<E> {
Self::Deneb(LightClientUpdateDeneb {
attested_header,
next_sync_committee,
next_sync_committee_branch,
next_sync_committee_branch: next_sync_committee_branch.into(),
finalized_header,
finality_branch,
finality_branch: finality_branch.into(),
sync_aggregate: sync_aggregate.clone(),
signature_slot: block_slot,
})
@@ -249,9 +279,9 @@ impl<E: EthSpec> LightClientUpdate<E> {
Self::Electra(LightClientUpdateElectra {
attested_header,
next_sync_committee,
next_sync_committee_branch,
next_sync_committee_branch: next_sync_committee_branch.into(),
finalized_header,
finality_branch,
finality_branch: finality_branch.into(),
sync_aggregate: sync_aggregate.clone(),
signature_slot: block_slot,
})
@@ -388,25 +418,21 @@ impl<E: EthSpec> LightClientUpdate<E> {
return Ok(new_attested_header_slot < prev_attested_header_slot);
}
return Ok(new.signature_slot() < self.signature_slot());
Ok(new.signature_slot() < self.signature_slot())
}
fn is_next_sync_committee_branch_empty(&self) -> bool {
for index in self.next_sync_committee_branch().iter() {
if *index != Hash256::default() {
return false;
}
}
true
fn is_next_sync_committee_branch_empty<'a>(&'a self) -> bool {
map_light_client_update_ref!(&'a _, self.to_ref(), |update, cons| {
cons(update);
is_empty_branch(update.next_sync_committee_branch.as_ref())
})
}
pub fn is_finality_branch_empty(&self) -> bool {
for index in self.finality_branch().iter() {
if *index != Hash256::default() {
return false;
}
}
true
pub fn is_finality_branch_empty<'a>(&'a self) -> bool {
map_light_client_update_ref!(&'a _, self.to_ref(), |update, cons| {
cons(update);
is_empty_branch(update.finality_branch.as_ref())
})
}
// A `LightClientUpdate` has two `LightClientHeader`s
@@ -436,6 +462,15 @@ impl<E: EthSpec> LightClientUpdate<E> {
}
}
fn is_empty_branch(branch: &[Hash256]) -> bool {
for index in branch.iter() {
if *index != Hash256::default() {
return false;
}
}
true
}
fn compute_sync_committee_period_at_slot<E: EthSpec>(
slot: Slot,
chain_spec: &ChainSpec,
@@ -447,16 +482,53 @@ fn compute_sync_committee_period_at_slot<E: EthSpec>(
#[cfg(test)]
mod tests {
use super::*;
use crate::MainnetEthSpec;
use ssz_types::typenum::Unsigned;
ssz_tests!(LightClientUpdateDeneb<MainnetEthSpec>);
// `ssz_tests!` can only be defined once per namespace
#[cfg(test)]
mod altair {
use super::*;
use crate::MainnetEthSpec;
ssz_tests!(LightClientUpdateAltair<MainnetEthSpec>);
}
#[cfg(test)]
mod capella {
use super::*;
use crate::MainnetEthSpec;
ssz_tests!(LightClientUpdateCapella<MainnetEthSpec>);
}
#[cfg(test)]
mod deneb {
use super::*;
use crate::MainnetEthSpec;
ssz_tests!(LightClientUpdateDeneb<MainnetEthSpec>);
}
#[cfg(test)]
mod electra {
use super::*;
use crate::MainnetEthSpec;
ssz_tests!(LightClientUpdateElectra<MainnetEthSpec>);
}
#[test]
fn finalized_root_params() {
assert!(2usize.pow(FINALIZED_ROOT_PROOF_LEN as u32) <= FINALIZED_ROOT_INDEX);
assert!(2usize.pow(FINALIZED_ROOT_PROOF_LEN as u32 + 1) > FINALIZED_ROOT_INDEX);
assert_eq!(FinalizedRootProofLen::to_usize(), FINALIZED_ROOT_PROOF_LEN);
assert!(
2usize.pow(FINALIZED_ROOT_PROOF_LEN_ELECTRA as u32) <= FINALIZED_ROOT_INDEX_ELECTRA
);
assert!(
2usize.pow(FINALIZED_ROOT_PROOF_LEN_ELECTRA as u32 + 1) > FINALIZED_ROOT_INDEX_ELECTRA
);
assert_eq!(
FinalizedRootProofLenElectra::to_usize(),
FINALIZED_ROOT_PROOF_LEN_ELECTRA
);
}
#[test]
@@ -471,6 +543,19 @@ mod tests {
CurrentSyncCommitteeProofLen::to_usize(),
CURRENT_SYNC_COMMITTEE_PROOF_LEN
);
assert!(
2usize.pow(CURRENT_SYNC_COMMITTEE_PROOF_LEN_ELECTRA as u32)
<= CURRENT_SYNC_COMMITTEE_INDEX_ELECTRA
);
assert!(
2usize.pow(CURRENT_SYNC_COMMITTEE_PROOF_LEN_ELECTRA as u32 + 1)
> CURRENT_SYNC_COMMITTEE_INDEX_ELECTRA
);
assert_eq!(
CurrentSyncCommitteeProofLenElectra::to_usize(),
CURRENT_SYNC_COMMITTEE_PROOF_LEN_ELECTRA
);
}
#[test]
@@ -481,5 +566,18 @@ mod tests {
NextSyncCommitteeProofLen::to_usize(),
NEXT_SYNC_COMMITTEE_PROOF_LEN
);
assert!(
2usize.pow(NEXT_SYNC_COMMITTEE_PROOF_LEN_ELECTRA as u32)
<= NEXT_SYNC_COMMITTEE_INDEX_ELECTRA
);
assert!(
2usize.pow(NEXT_SYNC_COMMITTEE_PROOF_LEN_ELECTRA as u32 + 1)
> NEXT_SYNC_COMMITTEE_INDEX_ELECTRA
);
assert_eq!(
NextSyncCommitteeProofLenElectra::to_usize(),
NEXT_SYNC_COMMITTEE_PROOF_LEN_ELECTRA
);
}
}

View File

@@ -32,6 +32,7 @@ pub trait ExecPayload<E: EthSpec>: Debug + Clone + PartialEq + Hash + TreeHash +
fn prev_randao(&self) -> Hash256;
fn block_number(&self) -> u64;
fn timestamp(&self) -> u64;
fn extra_data(&self) -> VariableList<u8, E::MaxExtraDataBytes>;
fn block_hash(&self) -> ExecutionBlockHash;
fn fee_recipient(&self) -> Address;
fn gas_limit(&self) -> u64;
@@ -225,6 +226,13 @@ impl<E: EthSpec> ExecPayload<E> for FullPayload<E> {
})
}
fn extra_data<'a>(&'a self) -> VariableList<u8, E::MaxExtraDataBytes> {
map_full_payload_ref!(&'a _, self.to_ref(), move |payload, cons| {
cons(payload);
payload.execution_payload.extra_data.clone()
})
}
fn block_hash<'a>(&'a self) -> ExecutionBlockHash {
map_full_payload_ref!(&'a _, self.to_ref(), move |payload, cons| {
cons(payload);
@@ -317,7 +325,7 @@ impl<'a, E: EthSpec> FullPayloadRef<'a, E> {
}
}
impl<'b, E: EthSpec> ExecPayload<E> for FullPayloadRef<'b, E> {
impl<E: EthSpec> ExecPayload<E> for FullPayloadRef<'_, E> {
fn block_type() -> BlockType {
BlockType::Full
}
@@ -357,6 +365,13 @@ impl<'b, E: EthSpec> ExecPayload<E> for FullPayloadRef<'b, E> {
})
}
fn extra_data<'a>(&'a self) -> VariableList<u8, E::MaxExtraDataBytes> {
map_full_payload_ref!(&'a _, self, move |payload, cons| {
cons(payload);
payload.execution_payload.extra_data.clone()
})
}
fn block_hash<'a>(&'a self) -> ExecutionBlockHash {
map_full_payload_ref!(&'a _, self, move |payload, cons| {
cons(payload);
@@ -542,6 +557,13 @@ impl<E: EthSpec> ExecPayload<E> for BlindedPayload<E> {
})
}
fn extra_data<'a>(&'a self) -> VariableList<u8, <E as EthSpec>::MaxExtraDataBytes> {
map_blinded_payload_ref!(&'a _, self.to_ref(), move |payload, cons| {
cons(payload);
payload.execution_payload_header.extra_data.clone()
})
}
fn block_hash<'a>(&'a self) -> ExecutionBlockHash {
map_blinded_payload_ref!(&'a _, self.to_ref(), move |payload, cons| {
cons(payload);
@@ -643,6 +665,13 @@ impl<'b, E: EthSpec> ExecPayload<E> for BlindedPayloadRef<'b, E> {
})
}
fn extra_data<'a>(&'a self) -> VariableList<u8, <E as EthSpec>::MaxExtraDataBytes> {
map_blinded_payload_ref!(&'a _, self, move |payload, cons| {
cons(payload);
payload.execution_payload_header.extra_data.clone()
})
}
fn block_hash<'a>(&'a self) -> ExecutionBlockHash {
map_blinded_payload_ref!(&'a _, self, move |payload, cons| {
cons(payload);
@@ -745,6 +774,10 @@ macro_rules! impl_exec_payload_common {
self.$wrapped_field.timestamp
}
fn extra_data(&self) -> VariableList<u8, E::MaxExtraDataBytes> {
self.$wrapped_field.extra_data.clone()
}
fn block_hash(&self) -> ExecutionBlockHash {
self.$wrapped_field.block_hash
}

View File

@@ -1,4 +1,5 @@
use crate::test_utils::TestRandom;
use crate::*;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
@@ -8,7 +9,6 @@ use tree_hash_derive::TreeHash;
arbitrary::Arbitrary,
Debug,
PartialEq,
Eq,
Hash,
Clone,
Serialize,
@@ -18,16 +18,18 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
)]
pub struct PendingBalanceDeposit {
#[serde(with = "serde_utils::quoted_u64")]
pub index: u64,
pub struct PendingDeposit {
pub pubkey: PublicKeyBytes,
pub withdrawal_credentials: Hash256,
#[serde(with = "serde_utils::quoted_u64")]
pub amount: u64,
pub signature: SignatureBytes,
pub slot: Slot,
}
#[cfg(test)]
mod tests {
use super::*;
ssz_and_tree_hash_tests!(PendingBalanceDeposit);
ssz_and_tree_hash_tests!(PendingDeposit);
}

View File

@@ -260,7 +260,7 @@ impl ElectraPreset {
whistleblower_reward_quotient_electra: spec.whistleblower_reward_quotient_electra,
max_pending_partials_per_withdrawals_sweep: spec
.max_pending_partials_per_withdrawals_sweep,
pending_balance_deposits_limit: E::pending_balance_deposits_limit() as u64,
pending_balance_deposits_limit: E::pending_deposits_limit() as u64,
pending_partial_withdrawals_limit: E::pending_partial_withdrawals_limit() as u64,
pending_consolidations_limit: E::pending_consolidations_limit() as u64,
max_consolidation_requests_per_payload: E::max_consolidation_requests_per_payload()

View File

@@ -18,7 +18,7 @@ use std::slice::SliceIndex;
/// ## Example
///
/// ```
/// use ssz_types::{RuntimeVariableList};
/// use types::{RuntimeVariableList};
///
/// let base: Vec<u64> = vec![1, 2, 3, 4];
///

View File

@@ -1,6 +1,7 @@
use crate::beacon_block_body::format_kzg_commitments;
use crate::beacon_block_body::{format_kzg_commitments, BLOB_KZG_COMMITMENTS_INDEX};
use crate::*;
use derivative::Derivative;
use merkle_proof::MerkleTree;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use std::fmt;
@@ -239,6 +240,45 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> SignedBeaconBlock<E, Payload>
}
}
/// Produce a signed beacon block header AND a merkle proof for the KZG commitments.
///
/// This method is more efficient than generating each part separately as it reuses hashing.
pub fn signed_block_header_and_kzg_commitments_proof(
&self,
) -> Result<
(
SignedBeaconBlockHeader,
FixedVector<Hash256, E::KzgCommitmentsInclusionProofDepth>,
),
Error,
> {
// Create the block body merkle tree
let body_leaves = self.message().body().body_merkle_leaves();
let beacon_block_body_depth = body_leaves.len().next_power_of_two().ilog2() as usize;
let body_merkle_tree = MerkleTree::create(&body_leaves, beacon_block_body_depth);
// Compute the KZG commitments inclusion proof
let (_, proof) = body_merkle_tree
.generate_proof(BLOB_KZG_COMMITMENTS_INDEX, beacon_block_body_depth)
.map_err(Error::MerkleTreeError)?;
let kzg_commitments_inclusion_proof = FixedVector::new(proof)?;
let block_header = BeaconBlockHeader {
slot: self.slot(),
proposer_index: self.message().proposer_index(),
parent_root: self.parent_root(),
state_root: self.state_root(),
body_root: body_merkle_tree.hash(),
};
let signed_header = SignedBeaconBlockHeader {
message: block_header,
signature: self.signature().clone(),
};
Ok((signed_header, kzg_commitments_inclusion_proof))
}
/// Convenience accessor for the block's slot.
pub fn slot(&self) -> Slot {
self.message().slot()

View File

@@ -133,7 +133,7 @@ pub struct SlotIter<'a> {
slots_per_epoch: u64,
}
impl<'a> Iterator for SlotIter<'a> {
impl Iterator for SlotIter<'_> {
type Item = Slot;
fn next(&mut self) -> Option<Slot> {

View File

@@ -1,14 +1,17 @@
//! Identifies each shard by an integer identifier.
use crate::{AttestationRef, ChainSpec, CommitteeIndex, Epoch, EthSpec, Slot};
use crate::{AttestationRef, ChainSpec, CommitteeIndex, EthSpec, Slot};
use alloy_primitives::{bytes::Buf, U256};
use safe_arith::{ArithError, SafeArith};
use serde::{Deserialize, Serialize};
use std::ops::{Deref, DerefMut};
use std::sync::LazyLock;
use swap_or_not_shuffle::compute_shuffled_index;
const MAX_SUBNET_ID: usize = 64;
/// The number of bits in a Discovery `NodeId`. This is used for binary operations on the node-id
/// data.
const NODE_ID_BITS: u64 = 256;
static SUBNET_ID_TO_STRING: LazyLock<Vec<String>> = LazyLock::new(|| {
let mut v = Vec::with_capacity(MAX_SUBNET_ID);
@@ -74,52 +77,22 @@ impl SubnetId {
.into())
}
/// Computes the set of subnets the node should be subscribed to during the current epoch,
/// along with the first epoch in which these subscriptions are no longer valid.
/// Computes the set of subnets the node should be subscribed to. We subscribe to these subnets
/// for the duration of the node's runtime.
#[allow(clippy::arithmetic_side_effects)]
pub fn compute_subnets_for_epoch<E: EthSpec>(
pub fn compute_attestation_subnets(
raw_node_id: [u8; 32],
epoch: Epoch,
spec: &ChainSpec,
) -> Result<(impl Iterator<Item = SubnetId>, Epoch), &'static str> {
// simplify variable naming
let subscription_duration = spec.epochs_per_subnet_subscription;
) -> impl Iterator<Item = SubnetId> {
// The bits of the node-id we are using to define the subnets.
let prefix_bits = spec.attestation_subnet_prefix_bits as u64;
let shuffling_prefix_bits = spec.attestation_subnet_shuffling_prefix_bits as u64;
let node_id = U256::from_be_slice(&raw_node_id);
let node_id = U256::from_be_slice(&raw_node_id);
// calculate the prefixes used to compute the subnet and shuffling
let node_id_prefix = (node_id >> (256 - prefix_bits)).as_le_slice().get_u64_le();
let shuffling_prefix = (node_id >> (256 - (prefix_bits + shuffling_prefix_bits)))
let node_id_prefix = (node_id >> (NODE_ID_BITS - prefix_bits))
.as_le_slice()
.get_u64_le();
// number of groups the shuffling creates
let shuffling_groups = 1 << shuffling_prefix_bits;
// shuffling group for this node
let shuffling_bits = shuffling_prefix % shuffling_groups;
let epoch_transition = (node_id_prefix
+ (shuffling_bits * (subscription_duration >> shuffling_prefix_bits)))
% subscription_duration;
// Calculate at which epoch this node needs to re-evaluate
let valid_until_epoch = epoch.as_u64()
+ subscription_duration
.saturating_sub((epoch.as_u64() + epoch_transition) % subscription_duration);
let subscription_event_idx = (epoch.as_u64() + epoch_transition) / subscription_duration;
let permutation_seed =
ethereum_hashing::hash(&int_to_bytes::int_to_bytes8(subscription_event_idx));
let num_subnets = 1 << spec.attestation_subnet_prefix_bits;
let permutated_prefix = compute_shuffled_index(
node_id_prefix as usize,
num_subnets,
&permutation_seed,
spec.shuffle_round_count,
)
.ok_or("Unable to shuffle")? as u64;
// Get the constants we need to avoid holding a reference to the spec
let &ChainSpec {
subnets_per_node,
@@ -127,10 +100,8 @@ impl SubnetId {
..
} = spec;
let subnet_set_generator = (0..subnets_per_node).map(move |idx| {
SubnetId::new((permutated_prefix + idx as u64) % attestation_subnet_count)
});
Ok((subnet_set_generator, valid_until_epoch.into()))
(0..subnets_per_node)
.map(move |idx| SubnetId::new((node_id_prefix + idx as u64) % attestation_subnet_count))
}
}
@@ -180,7 +151,7 @@ mod tests {
/// A set of tests compared to the python specification
#[test]
fn compute_subnets_for_epoch_unit_test() {
fn compute_attestation_subnets_test() {
// Randomized variables used generated with the python specification
let node_ids = [
"0",
@@ -189,59 +160,34 @@ mod tests {
"27726842142488109545414954493849224833670205008410190955613662332153332462900",
"39755236029158558527862903296867805548949739810920318269566095185775868999998",
"31899136003441886988955119620035330314647133604576220223892254902004850516297",
"58579998103852084482416614330746509727562027284701078483890722833654510444626",
"28248042035542126088870192155378394518950310811868093527036637864276176517397",
"60930578857433095740782970114409273483106482059893286066493409689627770333527",
"103822458477361691467064888613019442068586830412598673713899771287914656699997",
]
.map(|v| Uint256::from_str_radix(v, 10).unwrap().to_be_bytes::<32>());
let epochs = [
54321u64, 1017090249, 1827566880, 846255942, 766597383, 1204990115, 1616209495,
1774367616, 1484598751, 3525502229,
]
.map(Epoch::from);
let expected_subnets = [
vec![0, 1],
vec![49u64, 50u64],
vec![10, 11],
vec![15, 16],
vec![21, 22],
vec![17, 18],
];
// Test mainnet
let spec = ChainSpec::mainnet();
// Calculated by hand
let expected_valid_time = [
54528u64, 1017090255, 1827567030, 846256049, 766597387, 1204990287, 1616209536,
1774367857, 1484598847, 3525502311,
];
// Calculated from pyspec
let expected_subnets = [
vec![4u64, 5u64],
vec![31, 32],
vec![39, 40],
vec![38, 39],
vec![53, 54],
vec![57, 58],
vec![48, 49],
vec![1, 2],
vec![34, 35],
vec![37, 38],
];
for x in 0..node_ids.len() {
println!("Test: {}", x);
println!(
"NodeId: {:?}\n Epoch: {}\n, expected_update_time: {}\n, expected_subnets: {:?}",
node_ids[x], epochs[x], expected_valid_time[x], expected_subnets[x]
"NodeId: {:?}\nExpected_subnets: {:?}",
node_ids[x], expected_subnets[x]
);
let (computed_subnets, valid_time) = SubnetId::compute_subnets_for_epoch::<
crate::MainnetEthSpec,
>(node_ids[x], epochs[x], &spec)
.unwrap();
let computed_subnets = SubnetId::compute_attestation_subnets(node_ids[x], &spec);
assert_eq!(
expected_subnets[x],
computed_subnets.map(SubnetId::into).collect::<Vec<u64>>()
);
assert_eq!(Epoch::from(expected_valid_time[x]), valid_time);
}
}
}

View File

@@ -83,6 +83,35 @@ mod test {
}
}
#[test]
fn test_verify_blob_inclusion_proof_from_existing_proof() {
let (block, mut blob_sidecars) =
generate_rand_block_and_blobs::<MainnetEthSpec>(ForkName::Deneb, 1, &mut thread_rng());
let BlobSidecar {
index,
blob,
kzg_proof,
..
} = blob_sidecars.pop().unwrap();
// Compute the commitments inclusion proof and use it for building blob sidecar.
let (signed_block_header, kzg_commitments_inclusion_proof) = block
.signed_block_header_and_kzg_commitments_proof()
.unwrap();
let blob_sidecar = BlobSidecar::new_with_existing_proof(
index as usize,
blob,
&block,
signed_block_header,
&kzg_commitments_inclusion_proof,
kzg_proof,
)
.unwrap();
assert!(blob_sidecar.verify_blob_sidecar_inclusion_proof());
}
#[test]
fn test_verify_blob_inclusion_proof_invalid() {
let (_block, blobs) =

View File

@@ -1,6 +1,6 @@
use crate::{
test_utils::TestRandom, Address, BeaconState, ChainSpec, Checkpoint, DepositData, Epoch,
EthSpec, FixedBytesExtended, ForkName, Hash256, PublicKeyBytes,
test_utils::TestRandom, Address, BeaconState, ChainSpec, Checkpoint, Epoch, EthSpec,
FixedBytesExtended, ForkName, Hash256, PublicKeyBytes,
};
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
@@ -15,6 +15,7 @@ use tree_hash_derive::TreeHash;
Debug,
Clone,
PartialEq,
Eq,
Serialize,
Deserialize,
Encode,
@@ -37,14 +38,15 @@ pub struct Validator {
impl Validator {
#[allow(clippy::arithmetic_side_effects)]
pub fn from_deposit(
deposit_data: &DepositData,
pubkey: PublicKeyBytes,
withdrawal_credentials: Hash256,
amount: u64,
fork_name: ForkName,
spec: &ChainSpec,
) -> Self {
let mut validator = Validator {
pubkey: deposit_data.pubkey,
withdrawal_credentials: deposit_data.withdrawal_credentials,
pubkey,
withdrawal_credentials,
activation_eligibility_epoch: spec.far_future_epoch,
activation_epoch: spec.far_future_epoch,
exit_epoch: spec.far_future_epoch,
@@ -290,16 +292,6 @@ impl Validator {
spec.max_effective_balance
}
}
pub fn get_active_balance(
&self,
validator_balance: u64,
spec: &ChainSpec,
current_fork: ForkName,
) -> u64 {
let max_effective_balance = self.get_max_effective_balance(spec, current_fork);
std::cmp::min(validator_balance, max_effective_balance)
}
}
impl Default for Validator {