Block processing eip4844 (#3673)
* add eip4844 block processing * fix blob processing code * consensus logic fixes and cleanup * use safe arith
This commit is contained in:
parent
29f2ec46d3
commit
5ad834280b
@ -12,6 +12,7 @@ pub use self::verify_attester_slashing::{
|
|||||||
pub use self::verify_proposer_slashing::verify_proposer_slashing;
|
pub use self::verify_proposer_slashing::verify_proposer_slashing;
|
||||||
pub use altair::sync_committee::process_sync_aggregate;
|
pub use altair::sync_committee::process_sync_aggregate;
|
||||||
pub use block_signature_verifier::{BlockSignatureVerifier, ParallelSignatureSets};
|
pub use block_signature_verifier::{BlockSignatureVerifier, ParallelSignatureSets};
|
||||||
|
pub use eip4844::eip4844::process_blob_kzg_commitments;
|
||||||
pub use is_valid_indexed_attestation::is_valid_indexed_attestation;
|
pub use is_valid_indexed_attestation::is_valid_indexed_attestation;
|
||||||
pub use process_operations::process_operations;
|
pub use process_operations::process_operations;
|
||||||
pub use verify_attestation::{
|
pub use verify_attestation::{
|
||||||
@ -24,6 +25,7 @@ pub use verify_exit::verify_exit;
|
|||||||
|
|
||||||
pub mod altair;
|
pub mod altair;
|
||||||
pub mod block_signature_verifier;
|
pub mod block_signature_verifier;
|
||||||
|
pub mod eip4844;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
mod is_valid_indexed_attestation;
|
mod is_valid_indexed_attestation;
|
||||||
pub mod process_operations;
|
pub mod process_operations;
|
||||||
@ -171,6 +173,8 @@ pub fn per_block_processing<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
process_blob_kzg_commitments(block.body())?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
pub mod eip4844;
|
@ -0,0 +1,122 @@
|
|||||||
|
use crate::BlockProcessingError;
|
||||||
|
use eth2_hashing::hash_fixed;
|
||||||
|
use itertools::{EitherOrBoth, Itertools};
|
||||||
|
use safe_arith::SafeArith;
|
||||||
|
use ssz::Decode;
|
||||||
|
use ssz_types::VariableList;
|
||||||
|
use types::consts::eip4844::{BLOB_TX_TYPE, VERSIONED_HASH_VERSION_KZG};
|
||||||
|
use types::{
|
||||||
|
AbstractExecPayload, BeaconBlockBodyRef, EthSpec, ExecPayload, FullPayload, FullPayloadRef,
|
||||||
|
KzgCommitment, Transaction, Transactions, VersionedHash,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn process_blob_kzg_commitments<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
||||||
|
block_body: BeaconBlockBodyRef<T, Payload>,
|
||||||
|
) -> Result<(), BlockProcessingError> {
|
||||||
|
if let (Ok(payload), Ok(kzg_commitments)) = (
|
||||||
|
block_body.execution_payload(),
|
||||||
|
block_body.blob_kzg_commitments(),
|
||||||
|
) {
|
||||||
|
if let Some(transactions) = payload.transactions() {
|
||||||
|
if !verify_kzg_commitments_against_transactions::<T>(transactions, kzg_commitments)? {
|
||||||
|
return Err(BlockProcessingError::BlobVersionHashMismatch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn verify_kzg_commitments_against_transactions<T: EthSpec>(
|
||||||
|
transactions: &Transactions<T>,
|
||||||
|
kzg_commitments: &VariableList<KzgCommitment, T::MaxBlobsPerBlock>,
|
||||||
|
) -> Result<bool, BlockProcessingError> {
|
||||||
|
let nested_iter = transactions
|
||||||
|
.into_iter()
|
||||||
|
.filter(|tx| {
|
||||||
|
tx.get(0)
|
||||||
|
.map(|tx_type| *tx_type == BLOB_TX_TYPE)
|
||||||
|
.unwrap_or(false)
|
||||||
|
})
|
||||||
|
.map(|tx| tx_peek_blob_versioned_hashes::<T>(tx));
|
||||||
|
|
||||||
|
itertools::process_results(nested_iter, |iter| {
|
||||||
|
let zipped_iter = iter
|
||||||
|
.flatten()
|
||||||
|
// Need to use `itertools::zip_longest` here because just zipping hides if one iter is shorter
|
||||||
|
// and `itertools::zip_eq` panics.
|
||||||
|
.zip_longest(kzg_commitments.into_iter())
|
||||||
|
.enumerate()
|
||||||
|
.map(|(index, next)| match next {
|
||||||
|
EitherOrBoth::Both(hash, commitment) => Ok((hash?, commitment)),
|
||||||
|
// The number of versioned hashes from the blob transactions exceeds the number of
|
||||||
|
// commitments in the block.
|
||||||
|
EitherOrBoth::Left(_) => Err(BlockProcessingError::BlobNumCommitmentsMismatch {
|
||||||
|
commitments_processed_in_block: index,
|
||||||
|
commitments_processed_in_transactions: index.safe_add(1)?,
|
||||||
|
}),
|
||||||
|
// The number of commitments in the block exceeds the number of versioned hashes
|
||||||
|
// in the blob transactions.
|
||||||
|
EitherOrBoth::Right(_) => Err(BlockProcessingError::BlobNumCommitmentsMismatch {
|
||||||
|
commitments_processed_in_block: index.safe_add(1)?,
|
||||||
|
commitments_processed_in_transactions: index,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
itertools::process_results(zipped_iter, |mut iter| {
|
||||||
|
iter.all(|(tx_versioned_hash, commitment)| {
|
||||||
|
tx_versioned_hash == kzg_commitment_to_versioned_hash(commitment)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})?
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only transactions of type `BLOB_TX_TYPE` should be passed into this function.
|
||||||
|
fn tx_peek_blob_versioned_hashes<T: EthSpec>(
|
||||||
|
opaque_tx: &Transaction<T::MaxBytesPerTransaction>,
|
||||||
|
) -> Result<
|
||||||
|
impl IntoIterator<Item = Result<VersionedHash, BlockProcessingError>> + '_,
|
||||||
|
BlockProcessingError,
|
||||||
|
> {
|
||||||
|
let tx_len = opaque_tx.len();
|
||||||
|
let message_offset = 1.safe_add(u32::from_ssz_bytes(opaque_tx.get(1..5).ok_or(
|
||||||
|
BlockProcessingError::BlobVersionHashIndexOutOfBounds {
|
||||||
|
length: tx_len,
|
||||||
|
index: 5,
|
||||||
|
},
|
||||||
|
)?)?)?;
|
||||||
|
|
||||||
|
let message_offset_usize = message_offset as usize;
|
||||||
|
|
||||||
|
// field offset: 32 + 8 + 32 + 32 + 8 + 4 + 32 + 4 + 4 + 32 = 188
|
||||||
|
let blob_versioned_hashes_offset = message_offset.safe_add(u32::from_ssz_bytes(
|
||||||
|
opaque_tx
|
||||||
|
.get(message_offset_usize.safe_add(188)?..message_offset_usize.safe_add(192)?)
|
||||||
|
.ok_or(BlockProcessingError::BlobVersionHashIndexOutOfBounds {
|
||||||
|
length: tx_len,
|
||||||
|
index: message_offset_usize.safe_add(192)?,
|
||||||
|
})?,
|
||||||
|
)?)?;
|
||||||
|
|
||||||
|
let num_hashes = tx_len
|
||||||
|
.safe_sub(blob_versioned_hashes_offset as usize)?
|
||||||
|
.safe_div(32)?;
|
||||||
|
|
||||||
|
Ok((0..num_hashes).into_iter().map(move |i| {
|
||||||
|
let next_version_hash_index =
|
||||||
|
(blob_versioned_hashes_offset as usize).safe_add(i.safe_mul(32)?)?;
|
||||||
|
let bytes = opaque_tx
|
||||||
|
.get(next_version_hash_index..next_version_hash_index.safe_add(32)?)
|
||||||
|
.ok_or(BlockProcessingError::BlobVersionHashIndexOutOfBounds {
|
||||||
|
length: tx_len,
|
||||||
|
index: (next_version_hash_index as usize).safe_add(32)?,
|
||||||
|
})?;
|
||||||
|
Ok(VersionedHash::from_slice(bytes))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kzg_commitment_to_versioned_hash(kzg_commitment: &KzgCommitment) -> VersionedHash {
|
||||||
|
let mut hashed_commitment = hash_fixed(&kzg_commitment.0);
|
||||||
|
hashed_commitment[0] = VERSIONED_HASH_VERSION_KZG;
|
||||||
|
VersionedHash::from(hashed_commitment)
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
use super::signature_sets::Error as SignatureSetError;
|
use super::signature_sets::Error as SignatureSetError;
|
||||||
use merkle_proof::MerkleTreeError;
|
use merkle_proof::MerkleTreeError;
|
||||||
use safe_arith::ArithError;
|
use safe_arith::ArithError;
|
||||||
|
use ssz::DecodeError;
|
||||||
use types::*;
|
use types::*;
|
||||||
|
|
||||||
/// The error returned from the `per_block_processing` function. Indicates that a block is either
|
/// The error returned from the `per_block_processing` function. Indicates that a block is either
|
||||||
@ -53,6 +54,7 @@ pub enum BlockProcessingError {
|
|||||||
BeaconStateError(BeaconStateError),
|
BeaconStateError(BeaconStateError),
|
||||||
SignatureSetError(SignatureSetError),
|
SignatureSetError(SignatureSetError),
|
||||||
SszTypesError(ssz_types::Error),
|
SszTypesError(ssz_types::Error),
|
||||||
|
SszDecodeError(DecodeError),
|
||||||
MerkleTreeError(MerkleTreeError),
|
MerkleTreeError(MerkleTreeError),
|
||||||
ArithError(ArithError),
|
ArithError(ArithError),
|
||||||
InconsistentBlockFork(InconsistentFork),
|
InconsistentBlockFork(InconsistentFork),
|
||||||
@ -70,6 +72,18 @@ pub enum BlockProcessingError {
|
|||||||
found: u64,
|
found: u64,
|
||||||
},
|
},
|
||||||
ExecutionInvalid,
|
ExecutionInvalid,
|
||||||
|
BlobVersionHashMismatch,
|
||||||
|
/// The number of commitments in blob transactions in the payload does not match the number
|
||||||
|
/// of commitments in the block.
|
||||||
|
BlobNumCommitmentsMismatch {
|
||||||
|
commitments_processed_in_block: usize,
|
||||||
|
/// This number depic
|
||||||
|
commitments_processed_in_transactions: usize,
|
||||||
|
},
|
||||||
|
BlobVersionHashIndexOutOfBounds {
|
||||||
|
index: usize,
|
||||||
|
length: usize,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<BeaconStateError> for BlockProcessingError {
|
impl From<BeaconStateError> for BlockProcessingError {
|
||||||
@ -90,6 +104,12 @@ impl From<ssz_types::Error> for BlockProcessingError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<DecodeError> for BlockProcessingError {
|
||||||
|
fn from(error: DecodeError) -> Self {
|
||||||
|
BlockProcessingError::SszDecodeError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<ArithError> for BlockProcessingError {
|
impl From<ArithError> for BlockProcessingError {
|
||||||
fn from(e: ArithError) -> Self {
|
fn from(e: ArithError) -> Self {
|
||||||
BlockProcessingError::ArithError(e)
|
BlockProcessingError::ArithError(e)
|
||||||
|
@ -34,4 +34,5 @@ pub mod eip4844 {
|
|||||||
.expect("should initialize BLS_MODULUS");
|
.expect("should initialize BLS_MODULUS");
|
||||||
}
|
}
|
||||||
pub const BLOB_TX_TYPE: u8 = 5;
|
pub const BLOB_TX_TYPE: u8 = 5;
|
||||||
|
pub const VERSIONED_HASH_VERSION_KZG: u8 = 1;
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ use tree_hash::{PackedEncoding, TreeHash};
|
|||||||
|
|
||||||
#[derive(Derivative, Debug, Clone, Serialize, Deserialize)]
|
#[derive(Derivative, Debug, Clone, Serialize, Deserialize)]
|
||||||
#[derivative(PartialEq, Eq, Hash)]
|
#[derivative(PartialEq, Eq, Hash)]
|
||||||
pub struct KzgCommitment(#[serde(with = "BigArray")] [u8; 48]);
|
pub struct KzgCommitment(#[serde(with = "BigArray")] pub [u8; 48]);
|
||||||
|
|
||||||
impl Display for KzgCommitment {
|
impl Display for KzgCommitment {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use crate::test_utils::{RngCore, TestRandom};
|
use crate::test_utils::{RngCore, TestRandom};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_big_array::BigArray;
|
||||||
use ssz::{Decode, DecodeError, Encode};
|
use ssz::{Decode, DecodeError, Encode};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use tree_hash::{PackedEncoding, TreeHash};
|
use tree_hash::{PackedEncoding, TreeHash};
|
||||||
use serde_big_array::BigArray;
|
|
||||||
|
|
||||||
const KZG_PROOF_BYTES_LEN: usize = 48;
|
const KZG_PROOF_BYTES_LEN: usize = 48;
|
||||||
|
|
||||||
|
@ -193,6 +193,7 @@ pub type Address = H160;
|
|||||||
pub type ForkVersion = [u8; 4];
|
pub type ForkVersion = [u8; 4];
|
||||||
pub type BLSFieldElement = Uint256;
|
pub type BLSFieldElement = Uint256;
|
||||||
pub type Blob<T> = FixedVector<BLSFieldElement, <T as EthSpec>::FieldElementsPerBlob>;
|
pub type Blob<T> = FixedVector<BLSFieldElement, <T as EthSpec>::FieldElementsPerBlob>;
|
||||||
|
pub type VersionedHash = Hash256;
|
||||||
|
|
||||||
pub use bls::{
|
pub use bls::{
|
||||||
AggregatePublicKey, AggregateSignature, Keypair, PublicKey, PublicKeyBytes, SecretKey,
|
AggregatePublicKey, AggregateSignature, Keypair, PublicKey, PublicKeyBytes, SecretKey,
|
||||||
|
@ -35,6 +35,9 @@ pub trait ExecPayload<T: EthSpec>: Debug + Clone + PartialEq + Hash + TreeHash +
|
|||||||
fn fee_recipient(&self) -> Address;
|
fn fee_recipient(&self) -> Address;
|
||||||
fn gas_limit(&self) -> u64;
|
fn gas_limit(&self) -> u64;
|
||||||
|
|
||||||
|
/// This will return `None` on blinded blocks or pre-merge blocks.
|
||||||
|
fn transactions(&self) -> Option<&Transactions<T>>;
|
||||||
|
|
||||||
// Is this a default payload? (pre-merge)
|
// Is this a default payload? (pre-merge)
|
||||||
fn is_default(&self) -> bool;
|
fn is_default(&self) -> bool;
|
||||||
}
|
}
|
||||||
@ -191,6 +194,10 @@ impl<T: EthSpec> ExecPayload<T> for FullPayloadMerge<T> {
|
|||||||
self.execution_payload.gas_limit
|
self.execution_payload.gas_limit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn transactions(&self) -> Option<&Transactions<T>> {
|
||||||
|
Some(&self.execution_payload.transactions)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: can this function be optimized?
|
// TODO: can this function be optimized?
|
||||||
fn is_default(&self) -> bool {
|
fn is_default(&self) -> bool {
|
||||||
self.execution_payload == ExecutionPayloadMerge::default()
|
self.execution_payload == ExecutionPayloadMerge::default()
|
||||||
@ -235,6 +242,10 @@ impl<T: EthSpec> ExecPayload<T> for FullPayloadCapella<T> {
|
|||||||
self.execution_payload.gas_limit
|
self.execution_payload.gas_limit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn transactions(&self) -> Option<&Transactions<T>> {
|
||||||
|
Some(&self.execution_payload.transactions)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: can this function be optimized?
|
// TODO: can this function be optimized?
|
||||||
fn is_default(&self) -> bool {
|
fn is_default(&self) -> bool {
|
||||||
self.execution_payload == ExecutionPayloadCapella::default()
|
self.execution_payload == ExecutionPayloadCapella::default()
|
||||||
@ -279,6 +290,10 @@ impl<T: EthSpec> ExecPayload<T> for FullPayloadEip4844<T> {
|
|||||||
self.execution_payload.gas_limit
|
self.execution_payload.gas_limit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn transactions(&self) -> Option<&Transactions<T>> {
|
||||||
|
Some(&self.execution_payload.transactions)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: can this function be optimized?
|
// TODO: can this function be optimized?
|
||||||
fn is_default(&self) -> bool {
|
fn is_default(&self) -> bool {
|
||||||
self.execution_payload == ExecutionPayloadEip4844::default()
|
self.execution_payload == ExecutionPayloadEip4844::default()
|
||||||
@ -347,6 +362,13 @@ impl<T: EthSpec> ExecPayload<T> for FullPayload<T> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn transactions<'a>(&'a self) -> Option<&'a Transactions<T>> {
|
||||||
|
map_full_payload_ref!(&'a _, self.to_ref(), move |payload, cons| {
|
||||||
|
cons(payload);
|
||||||
|
Some(&payload.execution_payload.transactions)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn is_default(&self) -> bool {
|
fn is_default(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Merge(payload) => payload.is_default(),
|
Self::Merge(payload) => payload.is_default(),
|
||||||
@ -428,6 +450,13 @@ impl<'b, T: EthSpec> ExecPayload<T> for FullPayloadRef<'b, T> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn transactions<'a>(&'a self) -> Option<&'a Transactions<T>> {
|
||||||
|
map_full_payload_ref!(&'a _, self, move |payload, cons| {
|
||||||
|
cons(payload);
|
||||||
|
Some(&payload.execution_payload.transactions)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: can this function be optimized?
|
// TODO: can this function be optimized?
|
||||||
fn is_default<'a>(&'a self) -> bool {
|
fn is_default<'a>(&'a self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
@ -687,6 +716,10 @@ impl<T: EthSpec> ExecPayload<T> for BlindedPayload<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn transactions(&self) -> Option<&Transactions<T>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: can this function be optimized?
|
// TODO: can this function be optimized?
|
||||||
fn is_default(&self) -> bool {
|
fn is_default(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
@ -773,6 +806,10 @@ impl<'b, T: EthSpec> ExecPayload<T> for BlindedPayloadRef<'b, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn transactions(&self) -> Option<&Transactions<T>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: can this function be optimized?
|
// TODO: can this function be optimized?
|
||||||
fn is_default<'a>(&'a self) -> bool {
|
fn is_default<'a>(&'a self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
@ -828,6 +865,10 @@ impl<T: EthSpec> ExecPayload<T> for BlindedPayloadMerge<T> {
|
|||||||
self.execution_payload_header.gas_limit
|
self.execution_payload_header.gas_limit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn transactions(&self) -> Option<&Transactions<T>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn is_default(&self) -> bool {
|
fn is_default(&self) -> bool {
|
||||||
self.execution_payload_header == ExecutionPayloadHeaderMerge::default()
|
self.execution_payload_header == ExecutionPayloadHeaderMerge::default()
|
||||||
}
|
}
|
||||||
@ -871,6 +912,10 @@ impl<T: EthSpec> ExecPayload<T> for BlindedPayloadCapella<T> {
|
|||||||
self.execution_payload_header.gas_limit
|
self.execution_payload_header.gas_limit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn transactions(&self) -> Option<&Transactions<T>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn is_default(&self) -> bool {
|
fn is_default(&self) -> bool {
|
||||||
self.execution_payload_header == ExecutionPayloadHeaderCapella::default()
|
self.execution_payload_header == ExecutionPayloadHeaderCapella::default()
|
||||||
}
|
}
|
||||||
@ -914,6 +959,10 @@ impl<T: EthSpec> ExecPayload<T> for BlindedPayloadEip4844<T> {
|
|||||||
self.execution_payload_header.gas_limit
|
self.execution_payload_header.gas_limit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn transactions(&self) -> Option<&Transactions<T>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn is_default(&self) -> bool {
|
fn is_default(&self) -> bool {
|
||||||
self.execution_payload_header == ExecutionPayloadHeaderEip4844::default()
|
self.execution_payload_header == ExecutionPayloadHeaderEip4844::default()
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user