Skip to content

Commit

Permalink
feat: add v2 cert in client (#32)
Browse files Browse the repository at this point in the history
* add trait and impl or preloader and oracle

* add cert version, make both v1 v2 share the identical interface

* add cert version and multiplex cert handling
  • Loading branch information
bxue-l2 authored Feb 25, 2025
1 parent fe4d17c commit d99a3d3
Show file tree
Hide file tree
Showing 11 changed files with 213 additions and 34 deletions.
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ rust-kzg-bn254-prover = { git = "https://github.com/Layr-Labs/rust-kzg-bn254", r
rust-kzg-bn254-verifier = { git = "https://github.com/Layr-Labs/rust-kzg-bn254", rev = "b3e532e9aad533009849755d5ad7b9578a16bfb2", default-features = false }

# EigenDA v2 struct
eigenda-v2-struct-rust = { git = "https://github.com/bxue-l2/eigenda-v2-struct-rust", rev = "cdeaf4e6ed7c3d55e70bf97c2c04a5e056b780ef" }
eigenda-v2-struct-rust = { git = "https://github.com/bxue-l2/eigenda-v2-struct-rust", rev = "d3e3b956a59dcd2bb6bb7ea01c9e9d699350c4d8" }

ark-bn254 = "0.5.0"
ark-ff = { version = "0.5.0", features = ["parallel"] }
Expand Down
1 change: 1 addition & 0 deletions crates/eigenda/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ edition = "2021"

[dependencies]
kona-derive.workspace = true
eigenda-v2-struct-rust.workspace = true

# Op Alloy
alloy-primitives.workspace = true
Expand Down
70 changes: 50 additions & 20 deletions crates/eigenda/src/eigenda.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
use crate::eigenda_blobs::EigenDABlobSource;
use crate::traits::EigenDABlobProvider;
use crate::{BlobInfo, STALE_GAP};
use crate::{BlobInfo, CertVersion, STALE_GAP};
use alloy_rlp::Decodable;

use alloc::{boxed::Box, fmt::Debug};
use alloy_primitives::Bytes;
use async_trait::async_trait;
use eigenda_v2_struct_rust::EigenDAV2Cert;
use kona_derive::{
errors::{PipelineError, PipelineErrorKind},
sources::EthereumDataSource,
Expand Down Expand Up @@ -61,28 +62,57 @@ where
async fn next(&mut self, block_ref: &BlockInfo) -> PipelineResult<Self::Item> {
// then acutally use ethereum da to fetch. items are Bytes
let cert = self.ethereum_source.next(block_ref).await?;

// verify if cert is too stale
let cert_blob_info = BlobInfo::decode(&mut &cert.as_ref()[4..]).unwrap();
info!("cert_blob_info {:?}", cert_blob_info);
let rbn = cert_blob_info
.blob_verification_proof
.batch_medatada
.batch_header
.reference_block_number as u64;
let l1_block_number = block_ref.number;

// check staleness
// TODO: this would require the op-rollup to follow the same pattern
// but passing blockId to proxy which implement the logic,
// see https://github.com/ethereum-optimism/optimism/blob/0bb2ff57c8133f1e3983820c0bf238001eca119b/op-alt-da/damgr.go#L211
if rbn + STALE_GAP < l1_block_number {
// TODO: double check
return Err(PipelineErrorKind::Temporary(PipelineError::EndOfSource));
}
let cert_version_byte = cert.as_ref()[3];
let cert_version: CertVersion = cert_version_byte.into();
match cert_version {
CertVersion::Version1 => {
// TODO if punctuality is checked elsewhere, then we don't need to deserialize here
let cert_blob_info = BlobInfo::decode(&mut &cert.as_ref()[4..]).unwrap();
info!("cert_blob_info {:?}", cert_blob_info);
let rbn = cert_blob_info
.blob_verification_proof
.batch_medatada
.batch_header
.reference_block_number as u64;

let eigenda_blob = self.eigenda_source.next(&cert).await?;
Ok(eigenda_blob)
// check staleness
// TODO: this would require the op-rollup to follow the same pattern
// but passing blockId to proxy which implement the logic,
// see https://github.com/ethereum-optimism/optimism/blob/0bb2ff57c8133f1e3983820c0bf238001eca119b/op-alt-da/damgr.go#L211
if rbn + STALE_GAP < l1_block_number {
// TODO: double check
return Err(PipelineErrorKind::Temporary(PipelineError::EndOfSource));
}

let eigenda_blob = self.eigenda_source.next(&cert).await?;
Ok(eigenda_blob)
}
CertVersion::Version2 => {
// TODO if punctuality is checked elsewhere, then we don't need to deserialize here
let eigenda_v2_cert = match EigenDAV2Cert::decode(&mut &cert.as_ref()[4..]) {
Ok(c) => c,
Err(_e) => {
return Err(PipelineErrorKind::Temporary(PipelineError::EndOfSource))
}
};
let rbn = eigenda_v2_cert.batch_header_v2.reference_block_number as u64;
// check staleness
// TODO: this would require the op-rollup to follow the same pattern
// but passing blockId to proxy which implement the logic,
// see https://github.com/ethereum-optimism/optimism/blob/0bb2ff57c8133f1e3983820c0bf238001eca119b/op-alt-da/damgr.go#L211
if rbn + STALE_GAP < l1_block_number {
// TODO: double check
return Err(PipelineErrorKind::Temporary(PipelineError::EndOfSource));
}
let eigenda_blob = self.eigenda_source.next(&cert).await?;
Ok(eigenda_blob)
}
CertVersion::Unknown => {
return Err(PipelineErrorKind::Temporary(PipelineError::EndOfSource));
}
}
}

fn clear(&mut self) {
Expand Down
23 changes: 18 additions & 5 deletions crates/eigenda/src/eigenda_blobs.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
//! Blob Data Source
use crate::eigenda_data::EigenDABlobData;
use crate::traits::EigenDABlobProvider;
use crate::{eigenda_data::EigenDABlobData, CertVersion};
use alloy_rlp::Decodable;
use eigenda_v2_struct_rust::EigenDAV2Cert;

use alloc::vec::Vec;
use alloy_primitives::Bytes;
Expand Down Expand Up @@ -68,13 +70,24 @@ where
}

info!(target: "eigenda-blobsource", "going to fetch through eigenda fetcher");
let data = self.eigenda_fetcher.get_blob(eigenda_commitment).await;
let cert_version: CertVersion = eigenda_commitment.as_ref()[3].into();
let data = match cert_version {
CertVersion::Version1 => self.eigenda_fetcher.get_blob(eigenda_commitment).await,
CertVersion::Version2 => {
let eigenda_v2_cert =
EigenDAV2Cert::decode(&mut &eigenda_commitment.as_ref()[4..]).unwrap();
self.eigenda_fetcher.get_blob_v2(&eigenda_v2_cert).await
}
CertVersion::Unknown => panic!("impossible to trigger"),
};

match data {
Ok(data) => {
self.open = true;
let new_blob = data.clone();
// new_blob.truncate(data.len()-1);
let eigenda_blob = EigenDABlobData { blob: new_blob };
let new_blob: Vec<u8> = data.into();
let eigenda_blob = EigenDABlobData {
blob: new_blob.into(),
};
self.data.push(eigenda_blob);

info!(target: "eigenda-blobsource", "load_blobs {:?}", self.data);
Expand Down
3 changes: 2 additions & 1 deletion crates/eigenda/src/eigenda_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ use kona_derive::errors::BlobDecodingError;
use rust_kzg_bn254_primitives::helpers;

#[derive(Default, Clone, Debug)]
/// Represents the data structure for EigenDA Blob.
/// Represents the data structure for EigenDA Blob
/// intended for deriving rollup channel frame from eigenda blob
pub struct EigenDABlobData {
/// The calldata
pub blob: Bytes,
Expand Down
3 changes: 3 additions & 0 deletions crates/eigenda/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ pub use eigenda_data::EigenDABlobData;
mod certificate;
pub use certificate::BlobInfo;

mod version;
pub use version::CertVersion;

mod constant;
pub use constant::BLOB_ENCODING_VERSION_0;
pub use constant::BYTES_PER_FIELD_ELEMENT;
Expand Down
9 changes: 7 additions & 2 deletions crates/eigenda/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use alloc::{boxed::Box, string::ToString};
use alloy_primitives::Bytes;
use async_trait::async_trait;
use core::fmt::Display;
use eigenda_v2_struct_rust::EigenDAV2Cert;
use kona_derive::errors::PipelineErrorKind;
use rust_kzg_bn254_primitives::blob::Blob;

/// A trait for providing EigenDA blobs.
/// TODO: add explanation for why we need this to be a trait.
Expand All @@ -11,6 +13,9 @@ pub trait EigenDABlobProvider {
/// The error type for the [EigenDABlobProvider].
type Error: Display + ToString + Into<PipelineErrorKind>;

/// Fetches a blob.
async fn get_blob(&mut self, cert: &Bytes) -> Result<Bytes, Self::Error>;
/// Fetches a blob with v1 cert
async fn get_blob(&mut self, cert: &Bytes) -> Result<Blob, Self::Error>;

/// Fetches a blob with v2 cert
async fn get_blob_v2(&mut self, cert: &EigenDAV2Cert) -> Result<Blob, Self::Error>;
}
23 changes: 23 additions & 0 deletions crates/eigenda/src/version.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#[derive(Debug, PartialEq, Copy, Clone)]
/// Represents the cert version derived from rollup inbox
/// The version is needed to decode the Cert from serialiezd bytes
/// Once a valid blob is retrieved, both versions use the identical
/// logic to derive the rollup channel frame from eigenda blobs
pub enum CertVersion {
/// unknown
Unknown,
/// existing eigenda cert version
Version1,
/// lastest eigenda cert version
Version2,
}

impl From<u8> for CertVersion {
fn from(value: u8) -> Self {
match value {
0 => Self::Version1,
1 => Self::Version2,
_ => Self::Unknown,
}
}
}
79 changes: 77 additions & 2 deletions crates/proof/src/eigenda_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ use kona_preimage::{errors::PreimageOracleError, CommsClient, PreimageKey, Preim

use kona_proof::errors::OracleProviderError;

use eigenda_v2_struct_rust::EigenDAV2Cert;
use rust_kzg_bn254_primitives::blob::Blob;

use crate::hint::ExtendedHintType;
use alloy_rlp::Decodable;
use alloy_rlp::{Decodable, Encodable};
use tracing::info;

/// The oracle-backed EigenDA provider for the client program.
Expand All @@ -29,7 +32,8 @@ impl<T: CommsClient> OracleEigenDAProvider<T> {
impl<T: CommsClient + Sync + Send> EigenDABlobProvider for OracleEigenDAProvider<T> {
type Error = OracleProviderError;

async fn get_blob(&mut self, cert: &Bytes) -> Result<Bytes, Self::Error> {
/// Get V1 blobs. TODO remove in the future if not needed for testing
async fn get_blob(&mut self, cert: &Bytes) -> Result<Blob, Self::Error> {
self.oracle
.write(&ExtendedHintType::EigenDACommitment.encode_with(&[cert]))
.await
Expand Down Expand Up @@ -100,4 +104,75 @@ impl<T: CommsClient + Sync + Send> EigenDABlobProvider for OracleEigenDAProvider

Ok(blob.into())
}

/// get_blob_v2 takes a v2 cert type as opposed to bytes stream
async fn get_blob_v2(&mut self, cert: &EigenDAV2Cert) -> Result<Blob, Self::Error> {
let mut cert_rlp_bytes = Vec::<u8>::new();
// rlp encode of cert
cert.encode(&mut cert_rlp_bytes);

self.oracle
.write(&ExtendedHintType::EigenDACommitment.encode_with(&[&cert_rlp_bytes]))
.await
.map_err(OracleProviderError::Preimage)?;

let blob_length = cert
.blob_inclusion_info
.blob_certificate
.blob_header
.commitment
.length as usize;

// data_length measurs in field element, multiply to get num bytes
let mut blob: Vec<u8> = vec![0; blob_length * BYTES_PER_FIELD_ELEMENT];

// TODO: Investigate of using cert_rlp_bytes as key, instead of 96 bytes
let mut blob_key = [0u8; 96];

// the common key
let x: [u8; 32] = cert
.blob_inclusion_info
.blob_certificate
.blob_header
.commitment
.commitment
.x
.to_be_bytes();
let y: [u8; 32] = cert
.blob_inclusion_info
.blob_certificate
.blob_header
.commitment
.commitment
.y
.to_be_bytes();

blob_key[..32].copy_from_slice(&x);
blob_key[32..64].copy_from_slice(&y);

for i in 0..blob_length {
blob_key[88..].copy_from_slice(i.to_be_bytes().as_ref());

let mut field_element = [0u8; 32];
self.oracle
.get_exact(
PreimageKey::new(*keccak256(blob_key), PreimageKeyType::GlobalGeneric),
&mut field_element,
)
.await
.map_err(OracleProviderError::Preimage)?;

// if field element is 0, it means the host has identified that the data
// has breached eigenda invariant, i.e cert is valid
if field_element.is_empty() {
return Err(OracleProviderError::Preimage(PreimageOracleError::Other(
"field elememnt is empty, breached eigenda invariant".into(),
)));
}

blob[i << 5..(i + 1) << 5].copy_from_slice(field_element.as_ref());
}

Ok(Blob::new(&blob))
}
}
31 changes: 29 additions & 2 deletions crates/proof/src/preloaded_eigenda_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ use crate::eigenda_blob_witness::EigenDABlobWitnessData;
use alloy_primitives::{Bytes, FixedBytes, U256};
use ark_bn254::{Fq, G1Affine};
use ark_ff::PrimeField;
use async_trait::async_trait;
use eigenda_v2_struct_rust::EigenDAV2Cert;
use hokulea_eigenda::EigenDABlobProvider;
use kona_preimage::errors::PreimageOracleError;
use kona_proof::errors::OracleProviderError;
use rust_kzg_bn254_primitives::blob::Blob;
use rust_kzg_bn254_verifier::batch;
use tracing::info;
Expand All @@ -14,7 +18,7 @@ use tracing::info;
#[derive(Clone, Debug, Default)]
pub struct PreloadedEigenDABlobProvider {
/// The tuple contains EigenDAV2Cert, Blob, isValid cert.
pub entries: Vec<(EigenDAV2Cert, Blob, bool)>,
pub entries: Vec<(EigenDAV2Cert, Blob)>,
}

impl From<EigenDABlobWitnessData> for PreloadedEigenDABlobProvider {
Expand Down Expand Up @@ -51,7 +55,6 @@ impl From<EigenDABlobWitnessData> for PreloadedEigenDABlobProvider {
entries.push((
value.eigenda_certs[i].clone(),
value.eigenda_blobs[i].clone(),
is_valid,
));
}

Expand All @@ -62,6 +65,30 @@ impl From<EigenDABlobWitnessData> for PreloadedEigenDABlobProvider {
}
}

#[async_trait]
impl EigenDABlobProvider for PreloadedEigenDABlobProvider {
// TODO investigate if create a speical error type EigenDABlobProviderError
type Error = OracleProviderError;

/// Fetches a blob for V1
async fn get_blob(&mut self, _cert: &Bytes) -> Result<Blob, Self::Error> {
unimplemented!()
}

/// Fetches a blob for V2 using preloaded data
/// Return an error if cert does not match the immeditate next item
async fn get_blob_v2(&mut self, cert: &EigenDAV2Cert) -> Result<Blob, Self::Error> {
let (eigenda_cert, eigenda_blob) = self.entries.pop().unwrap();
if eigenda_cert == *cert {
Ok(eigenda_blob)
} else {
Err(OracleProviderError::Preimage(PreimageOracleError::Other(
"does not contain header".into(),
)))
}
}
}

/// Eventually, rust-kzg-bn254 would provide a nice interface that takes
/// bytes input, so that we can remove this wrapper. For now, just include it here
pub fn batch_verify(
Expand Down

0 comments on commit d99a3d3

Please sign in to comment.