Merge remote-tracking branch 'origin/unstable' into tree-states

This commit is contained in:
Michael Sproul
2023-12-14 09:59:43 +11:00
126 changed files with 5081 additions and 3916 deletions

View File

@@ -10,3 +10,5 @@ FIELD_ELEMENTS_PER_BLOB: 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

@@ -8,3 +8,5 @@ FIELD_ELEMENTS_PER_BLOB: 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

@@ -8,3 +8,5 @@ FIELD_ELEMENTS_PER_BLOB: 4096
MAX_BLOB_COMMITMENTS_PER_BLOCK: 16
# `uint64(6)`
MAX_BLOBS_PER_BLOCK: 6
# [customized] `floorlog2(BLOB_KZG_COMMITMENTS_GINDEX) + 1 + ceillog2(MAX_BLOB_COMMITMENTS_PER_BLOCK)` = 4 + 1 + 4 = 9
KZG_COMMITMENT_INCLUSION_PROOF_DEPTH: 9

View File

@@ -1,12 +1,14 @@
use crate::test_utils::TestRandom;
use crate::*;
use derivative::Derivative;
use merkle_proof::{MerkleTree, MerkleTreeError};
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use ssz_types::{FixedVector, VariableList};
use std::marker::PhantomData;
use superstruct::superstruct;
use test_random_derive::TestRandom;
use tree_hash::{TreeHash, BYTES_PER_CHUNK};
use tree_hash_derive::TreeHash;
pub type KzgCommitments<T> =
@@ -14,6 +16,9 @@ pub type KzgCommitments<T> =
pub type KzgCommitmentOpts<T> =
FixedVector<Option<KzgCommitment>, <T as EthSpec>::MaxBlobsPerBlock>;
/// Index of the `blob_kzg_commitments` leaf in the `BeaconBlockBody` tree post-deneb.
pub const BLOB_KZG_COMMITMENTS_INDEX: usize = 11;
/// The body of a `BeaconChain` block, containing operations.
///
/// This *superstruct* abstracts over the hard-fork.
@@ -98,6 +103,79 @@ impl<'a, T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlockBodyRef<'a, T,
Self::Deneb(body) => Ok(Payload::Ref::from(&body.execution_payload)),
}
}
/// Produces the proof of inclusion for a `KzgCommitment` in `self.blob_kzg_commitments`
/// at `index`.
pub fn kzg_commitment_merkle_proof(
&self,
index: usize,
) -> Result<FixedVector<Hash256, T::KzgCommitmentInclusionProofDepth>, Error> {
match self {
Self::Base(_) | Self::Altair(_) | Self::Merge(_) | Self::Capella(_) => {
Err(Error::IncorrectStateVariant)
}
Self::Deneb(body) => {
// 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.
// Part1 (Branches for the subtree rooted at `blob_kzg_commitments`)
//
// Branches for `blob_kzg_commitments` without length mix-in
let depth = T::max_blob_commitments_per_block()
.next_power_of_two()
.ilog2();
let leaves: Vec<_> = body
.blob_kzg_commitments
.iter()
.map(|commitment| commitment.tree_hash_root())
.collect();
let tree = MerkleTree::create(&leaves, depth as usize);
let (_, mut proof) = tree
.generate_proof(index, depth as usize)
.map_err(Error::MerkleTreeError)?;
// Add the branch corresponding to the length mix-in.
let length = body.blob_kzg_commitments.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);
// Part 2
// Branches for `BeaconBlockBody` container
let leaves = [
body.randao_reveal.tree_hash_root(),
body.eth1_data.tree_hash_root(),
body.graffiti.tree_hash_root(),
body.proposer_slashings.tree_hash_root(),
body.attester_slashings.tree_hash_root(),
body.attestations.tree_hash_root(),
body.deposits.tree_hash_root(),
body.voluntary_exits.tree_hash_root(),
body.sync_aggregate.tree_hash_root(),
body.execution_payload.tree_hash_root(),
body.bls_to_execution_changes.tree_hash_root(),
body.blob_kzg_commitments.tree_hash_root(),
];
let beacon_block_body_depth = leaves.len().next_power_of_two().ilog2() as usize;
let tree = MerkleTree::create(&leaves, beacon_block_body_depth);
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(), T::kzg_proof_inclusion_proof_depth());
Ok(proof.into())
}
}
}
}
impl<'a, T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlockBodyRef<'a, T, Payload> {

View File

@@ -60,6 +60,16 @@ impl BeaconBlockHeader {
signature,
}
}
pub fn empty() -> Self {
Self {
body_root: Default::default(),
parent_root: Default::default(),
proposer_index: Default::default(),
slot: Default::default(),
state_root: Default::default(),
}
}
}
#[cfg(test)]

View File

@@ -1,11 +1,18 @@
use crate::test_utils::TestRandom;
use crate::{Blob, EthSpec, Hash256, SignedRoot, Slot};
use crate::{
beacon_block_body::BLOB_KZG_COMMITMENTS_INDEX, BeaconBlockHeader, BeaconStateError, Blob,
EthSpec, Hash256, SignedBeaconBlockHeader, Slot,
};
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 merkle_proof::{merkle_root_from_branch, verify_merkle_proof, MerkleTreeError};
use rand::Rng;
use safe_arith::{ArithError, SafeArith};
use serde::{Deserialize, Serialize};
use ssz::Encode;
use ssz_derive::{Decode, Encode};
@@ -67,47 +74,14 @@ impl Ord for BlobIdentifier {
#[arbitrary(bound = "T: EthSpec")]
#[derivative(PartialEq, Eq, Hash(bound = "T: EthSpec"))]
pub struct BlobSidecar<T: EthSpec> {
pub block_root: Hash256,
#[serde(with = "serde_utils::quoted_u64")]
pub index: u64,
pub slot: Slot,
pub block_parent_root: Hash256,
#[serde(with = "serde_utils::quoted_u64")]
pub proposer_index: u64,
#[serde(with = "ssz_types::serde_utils::hex_fixed_vec")]
pub blob: Blob<T>,
pub kzg_commitment: KzgCommitment,
pub kzg_proof: KzgProof,
}
impl<E: EthSpec> From<Arc<BlobSidecar<E>>> for BlindedBlobSidecar {
fn from(blob_sidecar: Arc<BlobSidecar<E>>) -> Self {
BlindedBlobSidecar {
block_root: blob_sidecar.block_root,
index: blob_sidecar.index,
slot: blob_sidecar.slot,
block_parent_root: blob_sidecar.block_parent_root,
proposer_index: blob_sidecar.proposer_index,
blob_root: blob_sidecar.blob.tree_hash_root(),
kzg_commitment: blob_sidecar.kzg_commitment,
kzg_proof: blob_sidecar.kzg_proof,
}
}
}
impl<E: EthSpec> From<BlobSidecar<E>> for BlindedBlobSidecar {
fn from(blob_sidecar: BlobSidecar<E>) -> Self {
BlindedBlobSidecar {
block_root: blob_sidecar.block_root,
index: blob_sidecar.index,
slot: blob_sidecar.slot,
block_parent_root: blob_sidecar.block_parent_root,
proposer_index: blob_sidecar.proposer_index,
blob_root: blob_sidecar.blob.tree_hash_root(),
kzg_commitment: blob_sidecar.kzg_commitment,
kzg_proof: blob_sidecar.kzg_proof,
}
}
pub signed_block_header: SignedBeaconBlockHeader,
pub kzg_commitment_inclusion_proof: FixedVector<Hash256, T::KzgCommitmentInclusionProofDepth>,
}
impl<T: EthSpec> PartialOrd for BlobSidecar<T> {
@@ -122,29 +96,130 @@ impl<T: EthSpec> Ord for BlobSidecar<T> {
}
}
impl<T: EthSpec> SignedRoot for BlobSidecar<T> {}
#[derive(Debug)]
pub enum BlobSidecarError {
PreDeneb,
MissingKzgCommitment,
BeaconState(BeaconStateError),
MerkleTree(MerkleTreeError),
ArithError(ArithError),
}
impl From<BeaconStateError> for BlobSidecarError {
fn from(e: BeaconStateError) -> Self {
BlobSidecarError::BeaconState(e)
}
}
impl From<MerkleTreeError> for BlobSidecarError {
fn from(e: MerkleTreeError) -> Self {
BlobSidecarError::MerkleTree(e)
}
}
impl From<ArithError> for BlobSidecarError {
fn from(e: ArithError) -> Self {
BlobSidecarError::ArithError(e)
}
}
impl<T: EthSpec> BlobSidecar<T> {
pub fn new(
index: usize,
blob: Blob<T>,
signed_block: &SignedBeaconBlock<T>,
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()
.kzg_commitment_merkle_proof(index)?;
Ok(Self {
index: index as u64,
blob,
kzg_commitment,
kzg_proof,
signed_block_header: signed_block.signed_block_header(),
kzg_commitment_inclusion_proof,
})
}
pub fn id(&self) -> BlobIdentifier {
BlobIdentifier {
block_root: self.block_root,
block_root: self.block_root(),
index: self.index,
}
}
pub fn slot(&self) -> Slot {
self.signed_block_header.message.slot
}
pub fn block_root(&self) -> Hash256 {
self.signed_block_header.message.tree_hash_root()
}
pub fn block_parent_root(&self) -> Hash256 {
self.signed_block_header.message.parent_root
}
pub fn block_proposer_index(&self) -> u64 {
self.signed_block_header.message.proposer_index
}
pub fn empty() -> Self {
Self {
block_root: Hash256::zero(),
index: 0,
slot: Slot::new(0),
block_parent_root: Hash256::zero(),
proposer_index: 0,
blob: Blob::<T>::default(),
kzg_commitment: KzgCommitment::empty_for_testing(),
kzg_proof: KzgProof::empty(),
signed_block_header: SignedBeaconBlockHeader {
message: BeaconBlockHeader::empty(),
signature: Signature::empty(),
},
kzg_commitment_inclusion_proof: Default::default(),
}
}
/// 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 = (T::max_blob_commitments_per_block()
.next_power_of_two()
.ilog2()
.safe_add(1))? as usize;
// 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_commitments_tree_depth,
self.index as usize,
);
// The remaining inclusion proof branches are for the top level `BeaconBlockBody` tree
Ok(verify_merkle_proof(
blob_kzg_commitments_root,
self.kzg_commitment_inclusion_proof
.get(kzg_commitments_tree_depth..T::kzg_proof_inclusion_proof_depth())
.ok_or(MerkleTreeError::PleaseNotifyTheDevs)?,
T::kzg_proof_inclusion_proof_depth().safe_sub(kzg_commitments_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> {
let mut blob_bytes = vec![0u8; BYTES_PER_BLOB];
rng.fill_bytes(&mut blob_bytes);
@@ -185,57 +260,22 @@ impl<T: EthSpec> BlobSidecar<T> {
// Fixed part
Self::empty().as_ssz_bytes().len()
}
}
#[derive(
Debug,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
Derivative,
arbitrary::Arbitrary,
)]
#[derivative(PartialEq, Eq, Hash)]
pub struct BlindedBlobSidecar {
pub block_root: Hash256,
#[serde(with = "serde_utils::quoted_u64")]
pub index: u64,
pub slot: Slot,
pub block_parent_root: Hash256,
#[serde(with = "serde_utils::quoted_u64")]
pub proposer_index: u64,
pub blob_root: Hash256,
pub kzg_commitment: KzgCommitment,
pub kzg_proof: KzgProof,
}
impl BlindedBlobSidecar {
pub fn empty() -> Self {
Self {
block_root: Hash256::zero(),
index: 0,
slot: Slot::new(0),
block_parent_root: Hash256::zero(),
proposer_index: 0,
blob_root: Hash256::zero(),
kzg_commitment: KzgCommitment::empty_for_testing(),
kzg_proof: KzgProof::empty(),
pub fn build_sidecars(
blobs: BlobsList<T>,
block: &SignedBeaconBlock<T>,
kzg_proofs: KzgProofs<T>,
) -> Result<BlobSidecarList<T>, 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))
}
}
impl SignedRoot for BlindedBlobSidecar {}
pub type SidecarList<T, Sidecar> = VariableList<Arc<Sidecar>, <T as EthSpec>::MaxBlobsPerBlock>;
pub type BlobSidecarList<T> = SidecarList<T, BlobSidecar<T>>;
pub type BlindedBlobSidecarList<T> = SidecarList<T, BlindedBlobSidecar>;
pub type BlobSidecarList<T> = VariableList<Arc<BlobSidecar<T>>, <T as EthSpec>::MaxBlobsPerBlock>;
pub type FixedBlobSidecarList<T> =
FixedVector<Option<Arc<BlobSidecar<T>>>, <T as EthSpec>::MaxBlobsPerBlock>;
pub type BlobsList<T> = VariableList<Blob<T>, <T as EthSpec>::MaxBlobCommitmentsPerBlock>;
pub type BlobRootsList<T> = VariableList<Hash256, <T as EthSpec>::MaxBlobCommitmentsPerBlock>;

View File

@@ -1,24 +1,15 @@
use crate::beacon_block_body::KzgCommitments;
use crate::{
BlobRootsList, ChainSpec, EthSpec, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb,
ChainSpec, EthSpec, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb,
ExecutionPayloadHeaderMerge, ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut, ForkName,
ForkVersionDeserialize, KzgProofs, SignedRoot, Uint256,
ForkVersionDeserialize, SignedRoot, Uint256,
};
use bls::PublicKeyBytes;
use bls::Signature;
use serde::{Deserialize, Deserializer, Serialize};
use ssz_derive::Encode;
use superstruct::superstruct;
use tree_hash_derive::TreeHash;
#[derive(PartialEq, Debug, Default, Serialize, Deserialize, TreeHash, Clone, Encode)]
#[serde(bound = "E: EthSpec")]
pub struct BlindedBlobsBundle<E: EthSpec> {
pub commitments: KzgCommitments<E>,
pub proofs: KzgProofs<E>,
pub blob_roots: BlobRootsList<E>,
}
#[superstruct(
variants(Merge, Capella, Deneb),
variant_attributes(
@@ -39,7 +30,7 @@ pub struct BuilderBid<E: EthSpec> {
#[superstruct(only(Deneb), partial_getter(rename = "header_deneb"))]
pub header: ExecutionPayloadHeaderDeneb<E>,
#[superstruct(only(Deneb))]
pub blinded_blobs_bundle: BlindedBlobsBundle<E>,
pub blob_kzg_commitments: KzgCommitments<E>,
#[serde(with = "serde_utils::quoted_u256")]
pub value: Uint256,
pub pubkey: PublicKeyBytes,

View File

@@ -15,7 +15,6 @@ pub enum Domain {
BlsToExecutionChange,
BeaconProposer,
BeaconAttester,
BlobSidecar,
Randao,
Deposit,
VoluntaryExit,
@@ -102,7 +101,6 @@ pub struct ChainSpec {
*/
pub(crate) domain_beacon_proposer: u32,
pub(crate) domain_beacon_attester: u32,
pub(crate) domain_blob_sidecar: u32,
pub(crate) domain_randao: u32,
pub(crate) domain_deposit: u32,
pub(crate) domain_voluntary_exit: u32,
@@ -386,7 +384,6 @@ impl ChainSpec {
match domain {
Domain::BeaconProposer => self.domain_beacon_proposer,
Domain::BeaconAttester => self.domain_beacon_attester,
Domain::BlobSidecar => self.domain_blob_sidecar,
Domain::Randao => self.domain_randao,
Domain::Deposit => self.domain_deposit,
Domain::VoluntaryExit => self.domain_voluntary_exit,
@@ -601,7 +598,6 @@ impl ChainSpec {
domain_voluntary_exit: 4,
domain_selection_proof: 5,
domain_aggregate_and_proof: 6,
domain_blob_sidecar: 11, // 0x0B000000
/*
* Fork choice
@@ -844,7 +840,6 @@ impl ChainSpec {
domain_voluntary_exit: 4,
domain_selection_proof: 5,
domain_aggregate_and_proof: 6,
domain_blob_sidecar: 11,
/*
* Fork choice
@@ -1438,7 +1433,6 @@ mod tests {
test_domain(Domain::BeaconProposer, spec.domain_beacon_proposer, &spec);
test_domain(Domain::BeaconAttester, spec.domain_beacon_attester, &spec);
test_domain(Domain::BlobSidecar, spec.domain_blob_sidecar, &spec);
test_domain(Domain::Randao, spec.domain_randao, &spec);
test_domain(Domain::Deposit, spec.domain_deposit, &spec);
test_domain(Domain::VoluntaryExit, spec.domain_voluntary_exit, &spec);
@@ -1463,8 +1457,6 @@ mod tests {
spec.domain_bls_to_execution_change,
&spec,
);
test_domain(Domain::BlobSidecar, spec.domain_blob_sidecar, &spec);
}
fn apply_bit_mask(domain_bytes: [u8; 4], spec: &ChainSpec) -> u32 {

View File

@@ -82,7 +82,6 @@ pub fn get_extra_fields(spec: &ChainSpec) -> HashMap<String, Value> {
"bls_withdrawal_prefix".to_uppercase() => u8_hex(spec.bls_withdrawal_prefix_byte),
"domain_beacon_proposer".to_uppercase() => u32_hex(spec.domain_beacon_proposer),
"domain_beacon_attester".to_uppercase() => u32_hex(spec.domain_beacon_attester),
"domain_blob_sidecar".to_uppercase() => u32_hex(spec.domain_blob_sidecar),
"domain_randao".to_uppercase()=> u32_hex(spec.domain_randao),
"domain_deposit".to_uppercase()=> u32_hex(spec.domain_deposit),
"domain_voluntary_exit".to_uppercase() => u32_hex(spec.domain_voluntary_exit),

View File

@@ -6,6 +6,7 @@ use ssz_types::typenum::{
bit::B0, UInt, Unsigned, U0, U1024, U1048576, U1073741824, U1099511627776, U128, U131072, U16,
U16777216, U2, U2048, U256, U32, U4, U4096, U512, U6, U625, U64, U65536, U8, U8192,
};
use ssz_types::typenum::{U17, U9};
use std::fmt::{self, Debug};
use std::str::FromStr;
@@ -109,6 +110,7 @@ pub trait EthSpec:
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;
type KzgCommitmentInclusionProofDepth: Unsigned + Clone + Sync + Send + Debug + PartialEq;
/*
* Derived values (set these CAREFULLY)
*/
@@ -271,6 +273,10 @@ pub trait EthSpec:
fn bytes_per_blob() -> usize {
Self::BytesPerBlob::to_usize()
}
/// Returns the `KZG_COMMITMENT_INCLUSION_PROOF_DEPTH` preset for this specification.
fn kzg_proof_inclusion_proof_depth() -> usize {
Self::KzgCommitmentInclusionProofDepth::to_usize()
}
}
/// Macro to inherit some type values from another EthSpec.
@@ -315,6 +321,7 @@ impl EthSpec for MainnetEthSpec {
type BytesPerFieldElement = U32;
type FieldElementsPerBlob = U4096;
type BytesPerBlob = U131072;
type KzgCommitmentInclusionProofDepth = U17;
type SyncSubcommitteeSize = U128; // 512 committee size / 4 sync committee subnet count
type MaxPendingAttestations = U4096; // 128 max attestations * 32 slots per epoch
type SlotsPerEth1VotingPeriod = U2048; // 64 epochs * 32 slots per epoch
@@ -348,6 +355,7 @@ impl EthSpec for MinimalEthSpec {
type FieldElementsPerBlob = U4096;
type BytesPerBlob = U131072;
type MaxBlobCommitmentsPerBlock = U16;
type KzgCommitmentInclusionProofDepth = U9;
params_from_eth_spec!(MainnetEthSpec {
JustificationBitsLength,
@@ -421,6 +429,7 @@ impl EthSpec for GnosisEthSpec {
type FieldElementsPerBlob = U4096;
type BytesPerFieldElement = U32;
type BytesPerBlob = U131072;
type KzgCommitmentInclusionProofDepth = U17;
fn default_spec() -> ChainSpec {
ChainSpec::gnosis()

View File

@@ -101,8 +101,6 @@ pub mod sqlite;
pub mod blob_sidecar;
pub mod light_client_header;
pub mod sidecar;
pub mod signed_blob;
use ethereum_types::{H160, H256};
@@ -123,10 +121,7 @@ pub use crate::beacon_block_body::{
pub use crate::beacon_block_header::BeaconBlockHeader;
pub use crate::beacon_committee::{BeaconCommittee, OwnedBeaconCommittee};
pub use crate::beacon_state::{compact_state::CompactBeaconState, Error as BeaconStateError, *};
pub use crate::blob_sidecar::{
BlindedBlobSidecar, BlindedBlobSidecarList, BlobRootsList, BlobSidecar, BlobSidecarList,
BlobsList, SidecarList,
};
pub use crate::blob_sidecar::{BlobSidecar, BlobSidecarList, BlobsList};
pub use crate::bls_to_execution_change::BlsToExecutionChange;
pub use crate::chain_spec::{ChainSpec, Config, Domain};
pub use crate::checkpoint::Checkpoint;
@@ -185,7 +180,6 @@ pub use crate::signed_beacon_block::{
SignedBlindedBeaconBlock,
};
pub use crate::signed_beacon_block_header::SignedBeaconBlockHeader;
pub use crate::signed_blob::*;
pub use crate::signed_bls_to_execution_change::SignedBlsToExecutionChange;
pub use crate::signed_contribution_and_proof::SignedContributionAndProof;
pub use crate::signed_voluntary_exit::SignedVoluntaryExit;
@@ -225,6 +219,5 @@ pub use bls::{
};
pub use kzg::{KzgCommitment, KzgProof};
pub use milhouse::{self, Vector as FixedVector};
pub use sidecar::Sidecar;
pub use ssz_types::{typenum, typenum::Unsigned, BitList, BitVector, VariableList};
pub use superstruct::superstruct;

View File

@@ -83,8 +83,6 @@ pub trait AbstractExecPayload<T: EthSpec>:
+ TryInto<Self::Capella>
+ TryInto<Self::Deneb>
{
type Sidecar: Sidecar<T>;
type Ref<'a>: ExecPayload<T>
+ Copy
+ From<&'a Self::Merge>
@@ -103,11 +101,6 @@ pub trait AbstractExecPayload<T: EthSpec>:
+ Into<Self>
+ for<'a> From<Cow<'a, ExecutionPayloadDeneb<T>>>
+ TryFrom<ExecutionPayloadHeaderDeneb<T>>;
fn default_at_fork(fork_name: ForkName) -> Result<Self, Error>;
fn default_blobs_at_fork(
fork_name: ForkName,
) -> Result<<Self::Sidecar as Sidecar<T>>::BlobItems, Error>;
}
#[superstruct(
@@ -280,6 +273,15 @@ impl<T: EthSpec> FullPayload<T> {
cons(inner.execution_payload)
})
}
pub fn default_at_fork(fork_name: ForkName) -> Result<Self, Error> {
match fork_name {
ForkName::Base | ForkName::Altair => Err(Error::IncorrectStateVariant),
ForkName::Merge => Ok(FullPayloadMerge::default().into()),
ForkName::Capella => Ok(FullPayloadCapella::default().into()),
ForkName::Deneb => Ok(FullPayloadDeneb::default().into()),
}
}
}
impl<'a, T: EthSpec> FullPayloadRef<'a, T> {
@@ -384,28 +386,10 @@ impl<'b, T: EthSpec> ExecPayload<T> for FullPayloadRef<'b, T> {
}
impl<T: EthSpec> AbstractExecPayload<T> for FullPayload<T> {
type Sidecar = BlobSidecar<T>;
type Ref<'a> = FullPayloadRef<'a, T>;
type Merge = FullPayloadMerge<T>;
type Capella = FullPayloadCapella<T>;
type Deneb = FullPayloadDeneb<T>;
fn default_at_fork(fork_name: ForkName) -> Result<Self, Error> {
match fork_name {
ForkName::Base | ForkName::Altair => Err(Error::IncorrectStateVariant),
ForkName::Merge => Ok(FullPayloadMerge::default().into()),
ForkName::Capella => Ok(FullPayloadCapella::default().into()),
ForkName::Deneb => Ok(FullPayloadDeneb::default().into()),
}
}
fn default_blobs_at_fork(fork_name: ForkName) -> Result<BlobsList<T>, Error> {
match fork_name {
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {
Err(Error::IncorrectStateVariant)
}
ForkName::Deneb => Ok(VariableList::default()),
}
}
}
impl<T: EthSpec> From<ExecutionPayload<T>> for FullPayload<T> {
@@ -910,25 +894,6 @@ impl<T: EthSpec> AbstractExecPayload<T> for BlindedPayload<T> {
type Merge = BlindedPayloadMerge<T>;
type Capella = BlindedPayloadCapella<T>;
type Deneb = BlindedPayloadDeneb<T>;
type Sidecar = BlindedBlobSidecar;
fn default_at_fork(fork_name: ForkName) -> Result<Self, Error> {
match fork_name {
ForkName::Base | ForkName::Altair => Err(Error::IncorrectStateVariant),
ForkName::Merge => Ok(BlindedPayloadMerge::default().into()),
ForkName::Capella => Ok(BlindedPayloadCapella::default().into()),
ForkName::Deneb => Ok(BlindedPayloadDeneb::default().into()),
}
}
fn default_blobs_at_fork(fork_name: ForkName) -> Result<BlobRootsList<T>, Error> {
match fork_name {
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {
Err(Error::IncorrectStateVariant)
}
ForkName::Deneb => Ok(VariableList::default()),
}
}
}
impl<T: EthSpec> From<ExecutionPayload<T>> for BlindedPayload<T> {

View File

@@ -1,221 +0,0 @@
use crate::beacon_block_body::KzgCommitments;
use crate::test_utils::TestRandom;
use crate::{
AbstractExecPayload, BeaconBlock, BlindedBlobSidecar, BlindedBlobSidecarList, BlobRootsList,
BlobSidecar, BlobSidecarList, BlobsList, ChainSpec, Domain, EthSpec, Fork, Hash256,
SidecarList, SignedRoot, SignedSidecar, Slot,
};
use bls::SecretKey;
use kzg::KzgProof;
use serde::de::DeserializeOwned;
use ssz::{Decode, Encode};
use ssz_types::VariableList;
use std::fmt::Debug;
use std::hash::Hash;
use std::marker::PhantomData;
use std::sync::Arc;
use tree_hash::TreeHash;
pub trait Sidecar<E: EthSpec>:
serde::Serialize
+ Clone
+ DeserializeOwned
+ Encode
+ Decode
+ Hash
+ TreeHash
+ TestRandom
+ Debug
+ SignedRoot
+ Sync
+ Send
+ for<'a> arbitrary::Arbitrary<'a>
{
type BlobItems: BlobItems<E>;
fn slot(&self) -> Slot;
fn build_sidecar<Payload: AbstractExecPayload<E>>(
blob_items: Self::BlobItems,
block: &BeaconBlock<E, Payload>,
expected_kzg_commitments: &KzgCommitments<E>,
kzg_proofs: Vec<KzgProof>,
) -> Result<SidecarList<E, Self>, String>;
// this is mostly not used except for in testing
fn sign(
self: Arc<Self>,
secret_key: &SecretKey,
fork: &Fork,
genesis_validators_root: Hash256,
spec: &ChainSpec,
) -> SignedSidecar<E, Self> {
let signing_epoch = self.slot().epoch(E::slots_per_epoch());
let domain = spec.get_domain(
signing_epoch,
Domain::BlobSidecar,
fork,
genesis_validators_root,
);
let message = self.signing_root(domain);
let signature = secret_key.sign(message);
SignedSidecar {
message: self,
signature,
_phantom: PhantomData,
}
}
}
pub trait BlobItems<T: EthSpec>: Sync + Send + Sized {
fn try_from_blob_roots(roots: BlobRootsList<T>) -> Result<Self, String>;
fn try_from_blobs(blobs: BlobsList<T>) -> Result<Self, String>;
fn len(&self) -> usize;
fn is_empty(&self) -> bool;
fn blobs(&self) -> Option<&BlobsList<T>>;
}
impl<T: EthSpec> BlobItems<T> for BlobsList<T> {
fn try_from_blob_roots(_roots: BlobRootsList<T>) -> Result<Self, String> {
Err("Unexpected conversion from blob roots to blobs".to_string())
}
fn try_from_blobs(blobs: BlobsList<T>) -> Result<Self, String> {
Ok(blobs)
}
fn len(&self) -> usize {
VariableList::len(self)
}
fn is_empty(&self) -> bool {
VariableList::is_empty(self)
}
fn blobs(&self) -> Option<&BlobsList<T>> {
Some(self)
}
}
impl<T: EthSpec> BlobItems<T> for BlobRootsList<T> {
fn try_from_blob_roots(roots: BlobRootsList<T>) -> Result<Self, String> {
Ok(roots)
}
fn try_from_blobs(blobs: BlobsList<T>) -> Result<Self, String> {
VariableList::new(
blobs
.into_iter()
.map(|blob| blob.tree_hash_root())
.collect(),
)
.map_err(|e| format!("{e:?}"))
}
fn len(&self) -> usize {
VariableList::len(self)
}
fn is_empty(&self) -> bool {
VariableList::is_empty(self)
}
fn blobs(&self) -> Option<&BlobsList<T>> {
None
}
}
impl<E: EthSpec> Sidecar<E> for BlobSidecar<E> {
type BlobItems = BlobsList<E>;
fn slot(&self) -> Slot {
self.slot
}
fn build_sidecar<Payload: AbstractExecPayload<E>>(
blobs: BlobsList<E>,
block: &BeaconBlock<E, Payload>,
expected_kzg_commitments: &KzgCommitments<E>,
kzg_proofs: Vec<KzgProof>,
) -> Result<SidecarList<E, Self>, String> {
let beacon_block_root = block.canonical_root();
let slot = block.slot();
let blob_sidecars = BlobSidecarList::from(
blobs
.into_iter()
.enumerate()
.map(|(blob_index, blob)| {
let kzg_commitment = expected_kzg_commitments
.get(blob_index)
.ok_or("KZG commitment should exist for blob")?;
let kzg_proof = kzg_proofs
.get(blob_index)
.ok_or("KZG proof should exist for blob")?;
Ok(Arc::new(BlobSidecar {
block_root: beacon_block_root,
index: blob_index as u64,
slot,
block_parent_root: block.parent_root(),
proposer_index: block.proposer_index(),
blob,
kzg_commitment: *kzg_commitment,
kzg_proof: *kzg_proof,
}))
})
.collect::<Result<Vec<_>, String>>()?,
);
Ok(blob_sidecars)
}
}
impl<E: EthSpec> Sidecar<E> for BlindedBlobSidecar {
type BlobItems = BlobRootsList<E>;
fn slot(&self) -> Slot {
self.slot
}
fn build_sidecar<Payload: AbstractExecPayload<E>>(
blob_roots: BlobRootsList<E>,
block: &BeaconBlock<E, Payload>,
expected_kzg_commitments: &KzgCommitments<E>,
kzg_proofs: Vec<KzgProof>,
) -> Result<SidecarList<E, BlindedBlobSidecar>, String> {
let beacon_block_root = block.canonical_root();
let slot = block.slot();
let blob_sidecars = BlindedBlobSidecarList::<E>::from(
blob_roots
.into_iter()
.enumerate()
.map(|(blob_index, blob_root)| {
let kzg_commitment = expected_kzg_commitments
.get(blob_index)
.ok_or("KZG commitment should exist for blob")?;
let kzg_proof = kzg_proofs.get(blob_index).ok_or(format!(
"Missing KZG proof for slot {} blob index: {}",
slot, blob_index
))?;
Ok(Arc::new(BlindedBlobSidecar {
block_root: beacon_block_root,
index: blob_index as u64,
slot,
block_parent_root: block.parent_root(),
proposer_index: block.proposer_index(),
blob_root,
kzg_commitment: *kzg_commitment,
kzg_proof: *kzg_proof,
}))
})
.collect::<Result<Vec<_>, String>>()?,
);
Ok(blob_sidecars)
}
}

View File

@@ -1,114 +0,0 @@
use crate::sidecar::Sidecar;
use crate::{
test_utils::TestRandom, BlindedBlobSidecar, Blob, BlobSidecar, ChainSpec, Domain, EthSpec,
Fork, Hash256, Signature, SignedRoot, SigningData,
};
use bls::PublicKey;
use derivative::Derivative;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use ssz_types::VariableList;
use std::marker::PhantomData;
use std::sync::Arc;
use test_random_derive::TestRandom;
use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;
#[derive(
Debug,
Clone,
PartialEq,
Serialize,
Deserialize,
Encode,
Decode,
TestRandom,
TreeHash,
Derivative,
arbitrary::Arbitrary,
)]
#[serde(bound = "T: EthSpec, S: Sidecar<T>")]
#[arbitrary(bound = "T: EthSpec, S: Sidecar<T>")]
#[derivative(Hash(bound = "T: EthSpec, S: Sidecar<T>"))]
pub struct SignedSidecar<T: EthSpec, S: Sidecar<T>> {
pub message: Arc<S>,
pub signature: Signature,
#[ssz(skip_serializing, skip_deserializing)]
#[tree_hash(skip_hashing)]
#[serde(skip)]
#[arbitrary(default)]
pub _phantom: PhantomData<T>,
}
impl<T: EthSpec> SignedSidecar<T, BlindedBlobSidecar> {
pub fn into_full_blob_sidecars(self, blob: Blob<T>) -> SignedSidecar<T, BlobSidecar<T>> {
let blinded_sidecar = self.message;
SignedSidecar {
message: Arc::new(BlobSidecar {
block_root: blinded_sidecar.block_root,
index: blinded_sidecar.index,
slot: blinded_sidecar.slot,
block_parent_root: blinded_sidecar.block_parent_root,
proposer_index: blinded_sidecar.proposer_index,
blob,
kzg_commitment: blinded_sidecar.kzg_commitment,
kzg_proof: blinded_sidecar.kzg_proof,
}),
signature: self.signature,
_phantom: PhantomData,
}
}
}
impl<T: EthSpec> SignedBlobSidecar<T> {
/// Verify `self.signature`.
///
/// If the root of `block.message` is already known it can be passed in via `object_root_opt`.
/// Otherwise, it will be computed locally.
pub fn verify_signature(
&self,
object_root_opt: Option<Hash256>,
pubkey: &PublicKey,
fork: &Fork,
genesis_validators_root: Hash256,
spec: &ChainSpec,
) -> bool {
let domain = spec.get_domain(
self.message.slot.epoch(T::slots_per_epoch()),
Domain::BlobSidecar,
fork,
genesis_validators_root,
);
let message = if let Some(object_root) = object_root_opt {
SigningData {
object_root,
domain,
}
.tree_hash_root()
} else {
self.message.signing_root(domain)
};
self.signature.verify(pubkey, message)
}
}
impl<T: EthSpec> From<SignedBlobSidecar<T>> for SignedBlindedBlobSidecar<T> {
fn from(signed: SignedBlobSidecar<T>) -> Self {
SignedBlindedBlobSidecar {
message: Arc::new(signed.message.into()),
signature: signed.signature,
_phantom: PhantomData,
}
}
}
pub type SignedBlobSidecar<T> = SignedSidecar<T, BlobSidecar<T>>;
pub type SignedBlindedBlobSidecar<T> = SignedSidecar<T, BlindedBlobSidecar>;
/// List of Signed Sidecars that implements `Sidecar`.
pub type SignedSidecarList<T, Sidecar> =
VariableList<SignedSidecar<T, Sidecar>, <T as EthSpec>::MaxBlobsPerBlock>;
pub type SignedBlobSidecarList<T> = SignedSidecarList<T, BlobSidecar<T>>;
pub type SignedBlindedBlobSidecarList<T> = SignedSidecarList<T, BlindedBlobSidecar>;