Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Blob Tx Sidecar Iterator #1334

Merged
merged 1 commit into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions crates/eips/src/eip4844/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,20 @@ pub const BYTES_PER_PROOF: usize = 48;
/// A Blob serialized as 0x-prefixed hex string
pub type Blob = FixedBytes<BYTES_PER_BLOB>;

/// Helper function to deserialize boxed blobs.
#[cfg(feature = "serde")]
pub fn deserialize_blob<'de, D>(deserializer: D) -> Result<alloc::boxed::Box<Blob>, D::Error>
where
D: serde::de::Deserializer<'de>,
{
use serde::Deserialize;
let raw_blob = <alloy_primitives::Bytes>::deserialize(deserializer)?;
let blob = alloc::boxed::Box::new(
Blob::try_from(raw_blob.as_ref()).map_err(serde::de::Error::custom)?,
);
Ok(blob)
}

/// A commitment/proof serialized as 0x-prefixed hex string
pub type Bytes48 = FixedBytes<48>;

Expand Down
101 changes: 96 additions & 5 deletions crates/eips/src/eip4844/sidecar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use crate::eip4844::{
kzg_to_versioned_hash, Blob, Bytes48, BYTES_PER_BLOB, BYTES_PER_COMMITMENT, BYTES_PER_PROOF,
};
use alloc::boxed::Box;
use alloy_primitives::{bytes::BufMut, B256};
use alloy_rlp::{Decodable, Encodable};

Expand All @@ -12,6 +13,10 @@ use crate::eip4844::MAX_BLOBS_PER_BLOCK;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;

/// The versioned hash version for KZG.
#[cfg(feature = "kzg")]
pub(crate) const VERSIONED_HASH_VERSION_KZG: u8 = 0x01;

/// This represents a set of blobs, and its corresponding commitments and proofs.
///
/// This type encodes and decodes the fields without an rlp header.
Expand All @@ -32,6 +37,96 @@ pub struct BlobTransactionSidecar {
pub proofs: Vec<Bytes48>,
}

impl IntoIterator for BlobTransactionSidecar {
type Item = BlobTransactionSidecarItem;
type IntoIter = alloc::vec::IntoIter<BlobTransactionSidecarItem>;

fn into_iter(self) -> Self::IntoIter {
self.blobs
.into_iter()
.zip(self.commitments)
.zip(self.proofs)
.enumerate()
.map(|(index, ((blob, commitment), proof))| BlobTransactionSidecarItem {
index,
blob: Box::new(blob),
kzg_commitment: commitment,
kzg_proof: proof,
})
.collect::<Vec<_>>()
refcell marked this conversation as resolved.
Show resolved Hide resolved
.into_iter()
}
}

/// A single blob sidecar.
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
#[repr(C)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BlobTransactionSidecarItem {
/// The index of this item within the [BlobTransactionSidecar].
pub index: usize,
/// The blob in this sidecar item.
#[cfg_attr(feature = "serde", serde(deserialize_with = "super::deserialize_blob"))]
pub blob: Box<Blob>,
/// The KZG commitment.
pub kzg_commitment: Bytes48,
/// The KZG proof.
pub kzg_proof: Bytes48,
}

#[cfg(feature = "kzg")]
impl BlobTransactionSidecarItem {
/// `VERSIONED_HASH_VERSION_KZG ++ sha256(commitment)[1..]`
pub fn to_kzg_versioned_hash(&self) -> [u8; 32] {
use sha2::Digest;
let commitment = self.kzg_commitment.as_slice();
let mut hash: [u8; 32] = sha2::Sha256::digest(commitment).into();
hash[0] = VERSIONED_HASH_VERSION_KZG;
hash
}

/// Verifies the KZG proof of a blob to ensure its integrity and correctness.
pub fn verify_blob_kzg_proof(&self) -> Result<(), BlobTransactionValidationError> {
let binding = crate::eip4844::env_settings::EnvKzgSettings::Default;
let settings = binding.get();

let blob = c_kzg::Blob::from_bytes(self.blob.as_slice())
.map_err(BlobTransactionValidationError::KZGError)?;

let commitment = c_kzg::Bytes48::from_bytes(self.kzg_commitment.as_slice())
.map_err(BlobTransactionValidationError::KZGError)?;

let proof = c_kzg::Bytes48::from_bytes(self.kzg_proof.as_slice())
.map_err(BlobTransactionValidationError::KZGError)?;

let result = c_kzg::KzgProof::verify_blob_kzg_proof(&blob, &commitment, &proof, settings)
.map_err(BlobTransactionValidationError::KZGError)?;

result.then_some(()).ok_or(BlobTransactionValidationError::InvalidProof)
}

/// Verify the blob sidecar against its [crate::NumHash].
pub fn verify_blob(&self, hash: &crate::NumHash) -> Result<(), BlobTransactionValidationError> {
if self.index != hash.number as usize {
let blob_hash_part = B256::from_slice(&self.blob[0..32]);
return Err(BlobTransactionValidationError::WrongVersionedHash {
have: blob_hash_part,
expected: hash.hash,
});
}

let computed_hash = self.to_kzg_versioned_hash();
if computed_hash != hash.hash {
return Err(BlobTransactionValidationError::WrongVersionedHash {
have: computed_hash.into(),
expected: hash.hash,
});
}

self.verify_blob_kzg_proof()
}
}

#[cfg(any(test, feature = "arbitrary"))]
impl<'a> arbitrary::Arbitrary<'a> for BlobTransactionSidecar {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Expand Down Expand Up @@ -139,11 +234,7 @@ impl BlobTransactionSidecar {
}
.map_err(BlobTransactionValidationError::KZGError)?;

if res {
Ok(())
} else {
Err(BlobTransactionValidationError::InvalidProof)
}
res.then_some(()).ok_or(BlobTransactionValidationError::InvalidProof)
}

/// Returns an iterator over the versioned hashes of the commitments.
Expand Down
16 changes: 2 additions & 14 deletions crates/rpc-types-beacon/src/sidecar.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::header::Header;
use alloy_eips::eip4844::{Blob, BlobTransactionSidecar, Bytes48};
use alloy_primitives::{Bytes, B256};
use alloy_eips::eip4844::{deserialize_blob, Blob, BlobTransactionSidecar, Bytes48};
use alloy_primitives::B256;
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, DisplayFromStr};
use std::vec::IntoIter;
Expand Down Expand Up @@ -101,18 +101,6 @@ pub struct BlobData {
pub kzg_commitment_inclusion_proof: Vec<B256>,
}

/// Helper function to deserialize boxed blobs
fn deserialize_blob<'de, D>(deserializer: D) -> Result<Box<Blob>, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let raw_blob = <Bytes>::deserialize(deserializer)?;

let blob = Box::new(Blob::try_from(raw_blob.as_ref()).map_err(serde::de::Error::custom)?);

Ok(blob)
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down