resolve merge conflicts

This commit is contained in:
Eitan Seri-Levi
2025-01-07 13:57:22 +07:00
49 changed files with 545 additions and 261 deletions

View File

@@ -391,10 +391,12 @@ pub fn partially_verify_execution_payload<E: EthSpec, Payload: AbstractExecPaylo
if let Ok(blob_commitments) = body.blob_kzg_commitments() {
// Verify commitments are under the limit.
let max_blobs_per_block =
spec.max_blobs_per_block(block_slot.epoch(E::slots_per_epoch())) as usize;
block_verify!(
blob_commitments.len() <= E::max_blobs_per_block(),
blob_commitments.len() <= max_blobs_per_block,
BlockProcessingError::ExecutionInvalidBlobsLen {
max: E::max_blobs_per_block(),
max: max_blobs_per_block,
actual: blob_commitments.len(),
}
);

View File

@@ -8,7 +8,5 @@
FIELD_ELEMENTS_PER_BLOB: 4096
# `uint64(2**12)` (= 4096)
MAX_BLOB_COMMITMENTS_PER_BLOCK: 4096
# `uint64(6)`
MAX_BLOBS_PER_BLOCK: 6
# `floorlog2(BLOB_KZG_COMMITMENTS_GINDEX) + 1 + ceillog2(MAX_BLOB_COMMITMENTS_PER_BLOCK)` = 4 + 1 + 12 = 17
KZG_COMMITMENT_INCLUSION_PROOF_DEPTH: 17

View File

@@ -6,7 +6,5 @@
FIELD_ELEMENTS_PER_BLOB: 4096
# `uint64(2**12)` (= 4096)
MAX_BLOB_COMMITMENTS_PER_BLOCK: 4096
# `uint64(6)`
MAX_BLOBS_PER_BLOCK: 6
# `floorlog2(get_generalized_index(BeaconBlockBody, 'blob_kzg_commitments')) + 1 + ceillog2(MAX_BLOB_COMMITMENTS_PER_BLOCK)` = 4 + 1 + 12 = 17
KZG_COMMITMENT_INCLUSION_PROOF_DEPTH: 17

View File

@@ -13,8 +13,6 @@ use tree_hash_derive::TreeHash;
pub type KzgCommitments<E> =
VariableList<KzgCommitment, <E as EthSpec>::MaxBlobCommitmentsPerBlock>;
pub type KzgCommitmentOpts<E> =
FixedVector<Option<KzgCommitment>, <E as EthSpec>::MaxBlobsPerBlock>;
/// The number of leaves (including padding) on the `BeaconBlockBody` Merkle tree.
///

View File

@@ -1,10 +1,10 @@
use crate::test_utils::TestRandom;
use crate::ForkName;
use crate::{
beacon_block_body::BLOB_KZG_COMMITMENTS_INDEX, BeaconBlockHeader, BeaconStateError, Blob,
Epoch, EthSpec, FixedVector, Hash256, SignedBeaconBlockHeader, Slot, VariableList,
Epoch, EthSpec, FixedVector, ForkVersionDeserialize, Hash256, KzgProofs, RuntimeFixedVector,
RuntimeVariableList, SignedBeaconBlock, SignedBeaconBlockHeader, Slot, VariableList,
};
use crate::{ForkVersionDeserialize, KzgProofs, SignedBeaconBlock};
use crate::{ChainSpec, ForkName};
use bls::Signature;
use derivative::Derivative;
use kzg::{Blob as KzgBlob, Kzg, KzgCommitment, KzgProof, BYTES_PER_BLOB, BYTES_PER_FIELD_ELEMENT};
@@ -30,19 +30,6 @@ pub struct BlobIdentifier {
pub index: u64,
}
impl BlobIdentifier {
pub fn get_all_blob_ids<E: EthSpec>(block_root: Hash256) -> Vec<BlobIdentifier> {
let mut blob_ids = Vec::with_capacity(E::max_blobs_per_block());
for i in 0..E::max_blobs_per_block() {
blob_ids.push(BlobIdentifier {
block_root,
index: i as u64,
});
}
blob_ids
}
}
impl PartialOrd for BlobIdentifier {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
@@ -291,19 +278,23 @@ impl<E: EthSpec> BlobSidecar<E> {
blobs: BlobsList<E>,
block: &SignedBeaconBlock<E>,
kzg_proofs: KzgProofs<E>,
spec: &ChainSpec,
) -> Result<BlobSidecarList<E>, BlobSidecarError> {
let mut blob_sidecars = vec![];
for (i, (kzg_proof, blob)) in kzg_proofs.iter().zip(blobs).enumerate() {
let blob_sidecar = BlobSidecar::new(i, blob, block, *kzg_proof)?;
blob_sidecars.push(Arc::new(blob_sidecar));
}
Ok(VariableList::from(blob_sidecars))
Ok(RuntimeVariableList::from_vec(
blob_sidecars,
spec.max_blobs_per_block(block.epoch()) as usize,
))
}
}
pub type BlobSidecarList<E> = VariableList<Arc<BlobSidecar<E>>, <E as EthSpec>::MaxBlobsPerBlock>;
pub type FixedBlobSidecarList<E> =
FixedVector<Option<Arc<BlobSidecar<E>>>, <E as EthSpec>::MaxBlobsPerBlock>;
pub type BlobSidecarList<E> = RuntimeVariableList<Arc<BlobSidecar<E>>>;
/// Alias for a non length-constrained list of `BlobSidecar`s.
pub type FixedBlobSidecarList<E> = RuntimeFixedVector<Option<Arc<BlobSidecar<E>>>>;
pub type BlobsList<E> = VariableList<Blob<E>, <E as EthSpec>::MaxBlobCommitmentsPerBlock>;
impl<E: EthSpec> ForkVersionDeserialize for BlobSidecarList<E> {

View File

@@ -229,6 +229,7 @@ pub struct ChainSpec {
pub max_request_data_column_sidecars: u64,
pub min_epochs_for_blob_sidecars_requests: u64,
pub blob_sidecar_subnet_count: u64,
max_blobs_per_block: u64,
/*
* Networking Derived
@@ -605,6 +606,17 @@ impl ChainSpec {
}
}
/// Return the value of `MAX_BLOBS_PER_BLOCK` appropriate for the fork at `epoch`.
pub fn max_blobs_per_block(&self, epoch: Epoch) -> u64 {
self.max_blobs_per_block_by_fork(self.fork_name_at_epoch(epoch))
}
/// Return the value of `MAX_BLOBS_PER_BLOCK` appropriate for `fork`.
pub fn max_blobs_per_block_by_fork(&self, _fork_name: ForkName) -> u64 {
// TODO(electra): add Electra blobs per block change here
self.max_blobs_per_block
}
pub fn data_columns_per_subnet(&self) -> usize {
self.number_of_columns
.safe_div(self.data_column_sidecar_subnet_count as usize)
@@ -841,6 +853,7 @@ impl ChainSpec {
max_request_data_column_sidecars: default_max_request_data_column_sidecars(),
min_epochs_for_blob_sidecars_requests: default_min_epochs_for_blob_sidecars_requests(),
blob_sidecar_subnet_count: default_blob_sidecar_subnet_count(),
max_blobs_per_block: default_max_blobs_per_block(),
/*
* Derived Deneb Specific
@@ -1159,6 +1172,7 @@ impl ChainSpec {
max_request_data_column_sidecars: default_max_request_data_column_sidecars(),
min_epochs_for_blob_sidecars_requests: 16384,
blob_sidecar_subnet_count: default_blob_sidecar_subnet_count(),
max_blobs_per_block: default_max_blobs_per_block(),
/*
* Derived Deneb Specific
@@ -1352,6 +1366,9 @@ pub struct Config {
#[serde(default = "default_blob_sidecar_subnet_count")]
#[serde(with = "serde_utils::quoted_u64")]
blob_sidecar_subnet_count: u64,
#[serde(default = "default_max_blobs_per_block")]
#[serde(with = "serde_utils::quoted_u64")]
max_blobs_per_block: u64,
#[serde(default = "default_min_per_epoch_churn_limit_electra")]
#[serde(with = "serde_utils::quoted_u64")]
@@ -1482,6 +1499,12 @@ const fn default_blob_sidecar_subnet_count() -> u64 {
6
}
/// Its important to keep this consistent with the deneb preset value for
/// `MAX_BLOBS_PER_BLOCK` else we might run into consensus issues.
const fn default_max_blobs_per_block() -> u64 {
6
}
const fn default_min_per_epoch_churn_limit_electra() -> u64 {
128_000_000_000
}
@@ -1699,6 +1722,7 @@ impl Config {
max_request_data_column_sidecars: spec.max_request_data_column_sidecars,
min_epochs_for_blob_sidecars_requests: spec.min_epochs_for_blob_sidecars_requests,
blob_sidecar_subnet_count: spec.blob_sidecar_subnet_count,
max_blobs_per_block: spec.max_blobs_per_block,
min_per_epoch_churn_limit_electra: spec.min_per_epoch_churn_limit_electra,
max_per_epoch_activation_exit_churn_limit: spec
@@ -1774,6 +1798,7 @@ impl Config {
max_request_data_column_sidecars,
min_epochs_for_blob_sidecars_requests,
blob_sidecar_subnet_count,
max_blobs_per_block,
min_per_epoch_churn_limit_electra,
max_per_epoch_activation_exit_churn_limit,
@@ -1840,6 +1865,7 @@ impl Config {
max_request_data_column_sidecars,
min_epochs_for_blob_sidecars_requests,
blob_sidecar_subnet_count,
max_blobs_per_block,
min_per_epoch_churn_limit_electra,
max_per_epoch_activation_exit_churn_limit,

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;
@@ -11,7 +11,6 @@ use safe_arith::ArithError;
use serde::{Deserialize, Serialize};
use ssz::Encode;
use ssz_derive::{Decode, Encode};
use ssz_types::typenum::Unsigned;
use ssz_types::Error as SszError;
use ssz_types::{FixedVector, VariableList};
use std::hash::Hash;
@@ -68,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()
}
@@ -110,18 +113,16 @@ impl<E: EthSpec> DataColumnSidecar<E> {
.len()
}
pub fn max_size() -> usize {
pub fn max_size(max_blobs_per_block: usize) -> usize {
Self {
index: 0,
column: VariableList::new(vec![Cell::<E>::default(); E::MaxBlobsPerBlock::to_usize()])
.unwrap(),
column: VariableList::new(vec![Cell::<E>::default(); max_blobs_per_block]).unwrap(),
kzg_commitments: VariableList::new(vec![
KzgCommitment::empty_for_testing();
E::MaxBlobsPerBlock::to_usize()
max_blobs_per_block
])
.unwrap(),
kzg_proofs: VariableList::new(vec![KzgProof::empty(); E::MaxBlobsPerBlock::to_usize()])
.unwrap(),
kzg_proofs: VariableList::new(vec![KzgProof::empty(); max_blobs_per_block]).unwrap(),
signed_block_header: SignedBeaconBlockHeader {
message: BeaconBlockHeader::empty(),
signature: Signature::empty(),

View File

@@ -4,7 +4,7 @@ use safe_arith::SafeArith;
use serde::{Deserialize, Serialize};
use ssz_types::typenum::{
bit::B0, UInt, U0, U1, U10, U1024, U1048576, U1073741824, U1099511627776, U128, U131072,
U134217728, U16, U16777216, U17, U2, U2048, U256, U262144, U32, U4, U4096, U512, U6, U625, U64,
U134217728, U16, U16777216, U17, U2, U2048, U256, U262144, U32, U4, U4096, U512, U625, U64,
U65536, U8, U8192,
};
use std::fmt::{self, Debug};
@@ -108,7 +108,6 @@ pub trait EthSpec:
/*
* New in Deneb
*/
type MaxBlobsPerBlock: Unsigned + Clone + Sync + Send + Debug + PartialEq + Unpin;
type MaxBlobCommitmentsPerBlock: Unsigned + Clone + Sync + Send + Debug + PartialEq + Unpin;
type FieldElementsPerBlob: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type BytesPerFieldElement: Unsigned + Clone + Sync + Send + Debug + PartialEq;
@@ -280,11 +279,6 @@ pub trait EthSpec:
Self::MaxWithdrawalsPerPayload::to_usize()
}
/// Returns the `MAX_BLOBS_PER_BLOCK` constant for this specification.
fn max_blobs_per_block() -> usize {
Self::MaxBlobsPerBlock::to_usize()
}
/// Returns the `MAX_BLOB_COMMITMENTS_PER_BLOCK` constant for this specification.
fn max_blob_commitments_per_block() -> usize {
Self::MaxBlobCommitmentsPerBlock::to_usize()
@@ -420,7 +414,6 @@ impl EthSpec for MainnetEthSpec {
type GasLimitDenominator = U1024;
type MinGasLimit = U5000;
type MaxExtraDataBytes = U32;
type MaxBlobsPerBlock = U6;
type MaxBlobCommitmentsPerBlock = U4096;
type BytesPerFieldElement = U32;
type FieldElementsPerBlob = U4096;
@@ -504,7 +497,6 @@ impl EthSpec for MinimalEthSpec {
MinGasLimit,
MaxExtraDataBytes,
MaxBlsToExecutionChanges,
MaxBlobsPerBlock,
BytesPerFieldElement,
PendingDepositsLimit,
MaxPendingDepositsPerEpoch,
@@ -558,7 +550,6 @@ impl EthSpec for GnosisEthSpec {
type SlotsPerEth1VotingPeriod = U1024; // 64 epochs * 16 slots per epoch
type MaxBlsToExecutionChanges = U16;
type MaxWithdrawalsPerPayload = U8;
type MaxBlobsPerBlock = U6;
type MaxBlobCommitmentsPerBlock = U4096;
type FieldElementsPerBlob = U4096;
type BytesPerFieldElement = U32;

View File

@@ -108,6 +108,7 @@ pub mod data_column_sidecar;
pub mod data_column_subnet_id;
pub mod light_client_header;
pub mod non_zero_usize;
pub mod runtime_fixed_vector;
pub mod runtime_var_list;
pub use crate::activation_queue::ActivationQueue;
@@ -219,6 +220,7 @@ pub use crate::preset::{
pub use crate::proposer_preparation_data::ProposerPreparationData;
pub use crate::proposer_slashing::ProposerSlashing;
pub use crate::relative_epoch::{Error as RelativeEpochError, RelativeEpoch};
pub use crate::runtime_fixed_vector::RuntimeFixedVector;
pub use crate::runtime_var_list::RuntimeVariableList;
pub use crate::selection_proof::SelectionProof;
pub use crate::shuffling_id::AttestationShufflingId;

View File

@@ -205,8 +205,6 @@ impl CapellaPreset {
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub struct DenebPreset {
#[serde(with = "serde_utils::quoted_u64")]
pub max_blobs_per_block: u64,
#[serde(with = "serde_utils::quoted_u64")]
pub max_blob_commitments_per_block: u64,
#[serde(with = "serde_utils::quoted_u64")]
@@ -216,7 +214,6 @@ pub struct DenebPreset {
impl DenebPreset {
pub fn from_chain_spec<E: EthSpec>(_spec: &ChainSpec) -> Self {
Self {
max_blobs_per_block: E::max_blobs_per_block() as u64,
max_blob_commitments_per_block: E::max_blob_commitments_per_block() as u64,
field_elements_per_blob: E::field_elements_per_blob() as u64,
}

View File

@@ -0,0 +1,78 @@
/// Emulates a SSZ `Vector`.
#[derive(Clone, Debug)]
pub struct RuntimeFixedVector<T> {
vec: Vec<T>,
len: usize,
}
impl<T: Clone + Default> RuntimeFixedVector<T> {
pub fn new(vec: Vec<T>) -> Self {
let len = vec.len();
Self { vec, len }
}
pub fn to_vec(&self) -> Vec<T> {
self.vec.clone()
}
pub fn as_slice(&self) -> &[T] {
self.vec.as_slice()
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.len
}
pub fn into_vec(self) -> Vec<T> {
self.vec
}
pub fn default(max_len: usize) -> Self {
Self {
vec: vec![T::default(); max_len],
len: max_len,
}
}
pub fn take(&mut self) -> Self {
let new = std::mem::take(&mut self.vec);
*self = Self::new(vec![T::default(); self.len]);
Self {
vec: new,
len: self.len,
}
}
}
impl<T> std::ops::Deref for RuntimeFixedVector<T> {
type Target = [T];
fn deref(&self) -> &[T] {
&self.vec[..]
}
}
impl<T> std::ops::DerefMut for RuntimeFixedVector<T> {
fn deref_mut(&mut self) -> &mut [T] {
&mut self.vec[..]
}
}
impl<T> IntoIterator for RuntimeFixedVector<T> {
type Item = T;
type IntoIter = std::vec::IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
self.vec.into_iter()
}
}
impl<'a, T> IntoIterator for &'a RuntimeFixedVector<T> {
type Item = &'a T;
type IntoIter = std::slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.vec.iter()
}
}

View File

@@ -2,7 +2,7 @@ use derivative::Derivative;
use serde::{Deserialize, Serialize};
use ssz::Decode;
use ssz_types::Error;
use std::ops::{Deref, DerefMut, Index, IndexMut};
use std::ops::{Deref, Index, IndexMut};
use std::slice::SliceIndex;
/// Emulates a SSZ `List`.
@@ -10,6 +10,11 @@ use std::slice::SliceIndex;
/// An ordered, heap-allocated, variable-length, homogeneous collection of `T`, with no more than
/// `max_len` values.
///
/// In cases where the `max_length` of the container is unknown at time of initialization, we provide
/// a `Self::empty_uninitialized` constructor that initializes a runtime list without setting the max_len.
///
/// To ensure there are no inconsistent states, we do not allow any mutating operation if `max_len` is not set.
///
/// ## Example
///
/// ```
@@ -35,14 +40,26 @@ use std::slice::SliceIndex;
///
/// // Push a value to if it _does_ exceed the maximum.
/// assert!(long.push(6).is_err());
///
/// let mut uninit = RuntimeVariableList::empty_uninitialized();
/// assert!(uninit.push(5).is_err());
///
/// // Set max_len to allow mutation.
/// uninit.set_max_len(5usize);
///
/// uninit.push(5).unwrap();
/// assert_eq!(&uninit[..], &[5]);
///
/// ```
#[derive(Debug, Clone, Serialize, Deserialize, Derivative)]
#[derivative(PartialEq, Eq, Hash(bound = "T: std::hash::Hash"))]
#[serde(transparent)]
pub struct RuntimeVariableList<T> {
vec: Vec<T>,
/// A `None` here indicates an uninitialized `Self`.
/// No mutating operation will be allowed until `max_len` is Some
#[serde(skip)]
max_len: usize,
max_len: Option<usize>,
}
impl<T> RuntimeVariableList<T> {
@@ -50,7 +67,10 @@ impl<T> RuntimeVariableList<T> {
/// `Err(OutOfBounds { .. })`.
pub fn new(vec: Vec<T>, max_len: usize) -> Result<Self, Error> {
if vec.len() <= max_len {
Ok(Self { vec, max_len })
Ok(Self {
vec,
max_len: Some(max_len),
})
} else {
Err(Error::OutOfBounds {
i: vec.len(),
@@ -62,14 +82,17 @@ impl<T> RuntimeVariableList<T> {
pub fn from_vec(mut vec: Vec<T>, max_len: usize) -> Self {
vec.truncate(max_len);
Self { vec, max_len }
Self {
vec,
max_len: Some(max_len),
}
}
/// Create an empty list.
/// Create an empty list with the given `max_len`.
pub fn empty(max_len: usize) -> Self {
Self {
vec: vec![],
max_len,
max_len: Some(max_len),
}
}
@@ -77,6 +100,28 @@ impl<T> RuntimeVariableList<T> {
self.vec.as_slice()
}
pub fn as_mut_slice(&mut self) -> Option<&mut [T]> {
if self.max_len.is_none() {
return None;
};
Some(self.vec.as_mut_slice())
}
/// Returns an instance of `Self` with max_len = None.
///
/// No mutating operation can be performed on an uninitialized instance
/// without first setting `max_len`.
pub fn empty_uninitialized() -> Self {
Self {
vec: vec![],
max_len: None,
}
}
pub fn set_max_len(&mut self, max_len: usize) {
self.max_len = Some(max_len);
}
/// Returns the number of values presently in `self`.
pub fn len(&self) -> usize {
self.vec.len()
@@ -88,7 +133,9 @@ impl<T> RuntimeVariableList<T> {
}
/// Returns the type-level maximum length.
pub fn max_len(&self) -> usize {
///
/// Returns `None` if self is uninitialized with a max_len.
pub fn max_len(&self) -> Option<usize> {
self.max_len
}
@@ -96,13 +143,17 @@ impl<T> RuntimeVariableList<T> {
///
/// Returns `Err(())` when appending `value` would exceed the maximum length.
pub fn push(&mut self, value: T) -> Result<(), Error> {
if self.vec.len() < self.max_len {
let Some(max_len) = self.max_len else {
// TODO(pawan): set a better error?
return Err(Error::MissingLengthInformation);
};
if self.vec.len() < max_len {
self.vec.push(value);
Ok(())
} else {
Err(Error::OutOfBounds {
i: self.vec.len().saturating_add(1),
len: self.max_len,
len: max_len,
})
}
}
@@ -135,7 +186,10 @@ impl<T: Decode> RuntimeVariableList<T> {
} else {
ssz::decode_list_of_variable_length_items(bytes, Some(max_len))?
};
Ok(Self { vec, max_len })
Ok(Self {
vec,
max_len: Some(max_len),
})
}
}
@@ -169,12 +223,6 @@ impl<T> Deref for RuntimeVariableList<T> {
}
}
impl<T> DerefMut for RuntimeVariableList<T> {
fn deref_mut(&mut self) -> &mut [T] {
&mut self.vec[..]
}
}
impl<'a, T> IntoIterator for &'a RuntimeVariableList<T> {
type Item = &'a T;
type IntoIter = std::slice::Iter<'a, T>;
@@ -283,7 +331,7 @@ mod test {
}
fn round_trip<T: Encode + Decode + PartialEq + Debug>(item: RuntimeVariableList<T>) {
let max_len = item.max_len();
let max_len = item.max_len().unwrap();
let encoded = &item.as_ssz_bytes();
assert_eq!(item.ssz_bytes_len(), encoded.len());
assert_eq!(