Skip to content

Commit

Permalink
Block processing eip4844 (sigp#3673)
Browse files Browse the repository at this point in the history
* add eip4844 block processing

* fix blob processing code

* consensus logic fixes and cleanup

* use safe arith
  • Loading branch information
realbigsean authored and Woodpile37 committed Jan 6, 2024
1 parent b13dd8a commit e4ec625
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 0 deletions.
4 changes: 4 additions & 0 deletions consensus/state_processing/src/per_block_processing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub use self::verify_attester_slashing::{
pub use self::verify_proposer_slashing::verify_proposer_slashing;
pub use altair::sync_committee::process_sync_aggregate;
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 process_operations::process_operations;
pub use verify_attestation::{
Expand All @@ -24,6 +25,7 @@ pub use verify_exit::verify_exit;

pub mod altair;
pub mod block_signature_verifier;
pub mod eip4844;
pub mod errors;
mod is_valid_indexed_attestation;
pub mod process_operations;
Expand Down Expand Up @@ -171,6 +173,8 @@ pub fn per_block_processing<T: EthSpec, Payload: AbstractExecPayload<T>>(
)?;
}

process_blob_kzg_commitments(block.body())?;

Ok(())
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod eip4844;
122 changes: 122 additions & 0 deletions consensus/state_processing/src/per_block_processing/eip4844/eip4844.rs
Original file line number Diff line number Diff line change
@@ -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)
}
20 changes: 20 additions & 0 deletions consensus/state_processing/src/per_block_processing/errors.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::signature_sets::Error as SignatureSetError;
use merkle_proof::MerkleTreeError;
use safe_arith::ArithError;
use ssz::DecodeError;
use types::*;

/// The error returned from the `per_block_processing` function. Indicates that a block is either
Expand Down Expand Up @@ -53,6 +54,7 @@ pub enum BlockProcessingError {
BeaconStateError(BeaconStateError),
SignatureSetError(SignatureSetError),
SszTypesError(ssz_types::Error),
SszDecodeError(DecodeError),
MerkleTreeError(MerkleTreeError),
ArithError(ArithError),
InconsistentBlockFork(InconsistentFork),
Expand All @@ -70,6 +72,18 @@ pub enum BlockProcessingError {
found: u64,
},
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 {
Expand All @@ -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 {
fn from(e: ArithError) -> Self {
BlockProcessingError::ArithError(e)
Expand Down
1 change: 1 addition & 0 deletions consensus/types/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,5 @@ pub mod eip4844 {
.expect("should initialize BLS_MODULUS");
}
pub const BLOB_TX_TYPE: u8 = 5;
pub const VERSIONED_HASH_VERSION_KZG: u8 = 1;
}

0 comments on commit e4ec625

Please sign in to comment.