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

Seperate crate for blob crypto #28

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
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
333 changes: 88 additions & 245 deletions Cargo.lock

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ members = ["bin/*", "crates/*"]
hokulea-client = { path = "bin/client", version = "0.1.0", default-features = false }
hokulea-eigenda = { path = "crates/eigenda", version = "0.1.0", default-features = false }
hokulea-proof = { path = "crates/proof", version = "0.1.0", default-features = false }
hokulea-cryptography = { path = "crates/cryptography", version = "0.1.0", default-features = false }

# Kona
# We use git dependencies instead of version dependencies because Kona is moving very fast right now
Expand Down Expand Up @@ -63,13 +64,19 @@ reqwest = "0.12.12"
async-trait = "0.1.85"
linked_list_allocator = "0.10.5"
bytes = "1.9.0"
num = "0.4"

# General
sha2 = { version = "0.10.8", default-features = false }
c-kzg = { version = "2.0.0", default-features = false }
anyhow = { version = "1.0.95", default-features = false }
thiserror = { version = "2.0.9", default-features = false }
rust-kzg-bn254 = { version = "0.2.1", default-features = false }
rust-kzg-bn254-primitives = { git = "https://github.com/Layr-Labs/rust-kzg-bn254", rev = "b3e532e9aad533009849755d5ad7b9578a16bfb2", default-features = false }
rust-kzg-bn254-prover = { git = "https://github.com/Layr-Labs/rust-kzg-bn254", rev = "b3e532e9aad533009849755d5ad7b9578a16bfb2", default-features = false }
rust-kzg-bn254-verifier = { git = "https://github.com/Layr-Labs/rust-kzg-bn254", rev = "b3e532e9aad533009849755d5ad7b9578a16bfb2", default-features = false }

ark-bn254 = "0.5.0"
ark-ff = { version = "0.5.0", features = ["parallel"] }

# Tracing
tracing-loki = "0.2.5"
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

Hokulea is a library to provide the altda providers for a derivation pipeline built with [kona](https://github.com/anton-rs/kona) to understand eigenDA blobs, following the [kona book](https://anton-rs.github.io/kona/sdk/pipeline/providers.html#implementing-a-custom-data-availability-provider) recommendation (also see this [comment](https://github.com/anton-rs/kona/pull/862#issuecomment-2515038089)).

### Download SRS points
Hokulea host currently computes a challenge proof that validates the correctness of the eigenda blob against the provided kzg commitment. Such computation requires the host to have access to sufficient KZG SRS points.

### Running against devnet

First start the devnet:
Expand All @@ -17,4 +20,4 @@ cd bin/client
just run-client-native-against-devnet
```

![](./hokulea.jpeg)
![](./hokulea.jpeg)
5 changes: 5 additions & 0 deletions bin/client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ edition = "2021"

[dependencies]
alloy-consensus.workspace = true
alloy-primitives.workspace = true
alloy-rlp.workspace = true

kona-client.workspace = true
kona-preimage.workspace = true
Expand All @@ -13,5 +15,8 @@ kona-driver.workspace = true
kona-executor.workspace = true

hokulea-proof.workspace = true
hokulea-eigenda.workspace = true
hokulea-cryptography.workspace = true

tracing.workspace = true
async-trait.workspace = true
72 changes: 72 additions & 0 deletions bin/client/src/cached_eigenda_provider.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use alloy_primitives::Bytes;
use alloy_rlp::Decodable;
use async_trait::async_trait;
use kona_preimage::errors::PreimageOracleError;
use kona_preimage::CommsClient;

use hokulea_eigenda::BlobInfo;
use hokulea_eigenda::EigenDABlobProvider;
use hokulea_proof::eigenda_provider::OracleEigenDAProvider;
use hokulea_cryptography::witness::EigenDABlobWitness;

use kona_proof::errors::OracleProviderError;

use std::sync::Mutex;
use alloc::sync::Arc;

/// CachedOracleEigenDAProvider is a wrapper outside OracleEigenDAProvider. Its intended use
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// CachedOracleEigenDAProvider is a wrapper outside OracleEigenDAProvider. Its intended use
/// CachedOracleEigenDAProvider is a wrapper around OracleEigenDAProvider. Its intended use

/// case is to fetch all eigenda blobs received during the derivation pipeline. So that it
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// case is to fetch all eigenda blobs received during the derivation pipeline. So that it
/// case is to fetch all eigenda blobs associated with the certs received during the derivation pipeline. So that it

/// is able to compute and cache the kzg witnesses, which can be verified inside ZKVM by checking
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we create a zkvm crate or somethign of that effect? Would be good to separate the structs and logic that is only used for zkvm vs those that are used for fraud proofs.

/// the point opening at the random Fiat Shamir evaluation index.
#[derive(Debug, Clone)]
pub struct CachedOracleEigenDAProvider<T: CommsClient> {
/// The preimage oracle client.
oracle: OracleEigenDAProvider<T>,
/// kzg proof witness
pub witness: Arc<Mutex<EigenDABlobWitness>>,
}

impl<T: CommsClient> CachedOracleEigenDAProvider<T> {
/// Constructs a new oracle-backed EigenDA provider.
pub fn new(oracle: OracleEigenDAProvider<T>, witness: Arc<Mutex<EigenDABlobWitness>>) -> Self {
Self { oracle, witness }
}
}

#[async_trait]
impl<T: CommsClient + Sync + Send> EigenDABlobProvider for CachedOracleEigenDAProvider<T> {
type Error = OracleProviderError;

async fn get_blob(&mut self, cert: &Bytes) -> Result<Bytes, Self::Error> {
let blob = self.oracle.get_blob(cert).await?;
let cert_blob_info = match BlobInfo::decode(&mut &cert[4..]) {
Ok(c) => c,
Err(_) => {
return Err(OracleProviderError::Preimage(PreimageOracleError::Other(
"does not contain header".into(),
)));
Comment on lines +42 to +47
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use map_err(...)? instead

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also isnt there a way to actually wrap the error? "does not contain header" is potentially wrong. could just be that elements are not bn254 FEs right?

}
};

let mut witness = self.witness.lock().unwrap();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

propagate error instead of unwrapping which panics


let _ = witness.push_witness(&blob).map_err(|e| {
return Err::<T, kona_proof::errors::OracleProviderError>(OracleProviderError::Preimage(PreimageOracleError::Other(
e.to_string(),
)));
});

let last_commitment = witness.commitments.last().unwrap();

// make sure locally computed proof equals to returned proof from the provider
if last_commitment[..32] != cert_blob_info.blob_header.commitment.x[..]
|| last_commitment[32..64] != cert_blob_info.blob_header.commitment.y[..]
Comment on lines +59 to +63
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these abstractions are super leaky. Better to create abstractions that only require local reasoning. Should in principle never have to index into a struct's fields like this for validation. That should all be handled by the struct itself. Maybe pass the commitment to the push_witness function and let it validate and return an error if it doesnt validate. Think that's what austin is doing in the golang lib

{
return Err(OracleProviderError::Preimage(PreimageOracleError::Other(
"proxy commitment is different from computed commitment proxy".into(),
)));
};

Ok(blob)
}
}
20 changes: 17 additions & 3 deletions bin/client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use kona_preimage::{
};

use alloc::sync::Arc;
use std::sync::Mutex;

use core::fmt::Debug;
use kona_executor::TrieDBProvider;
use kona_proof::{
Expand All @@ -21,6 +23,9 @@ use kona_proof::{
use tracing::{error, info};

use hokulea_proof::eigenda_provider::OracleEigenDAProvider;
use hokulea_cryptography::witness::EigenDABlobWitness;

pub mod cached_eigenda_provider;

#[inline]
pub async fn run<P, H>(oracle_client: P, hint_client: H) -> Result<(), FaultProofProgramError>
Expand All @@ -41,7 +46,7 @@ where
));
let boot = match BootInfo::load(oracle.as_ref()).await {
Ok(boot) => Arc::new(boot),
Err(e) => {
Err(e ) => {
error!(target: "client", "Failed to load boot info: {:?}", e);
return Err(e.into());
}
Expand All @@ -51,6 +56,9 @@ where
let beacon = OracleBlobProvider::new(oracle.clone());
let eigenda_blob_provider = OracleEigenDAProvider::new(oracle.clone());

let eigenda_blob_witness = Arc::new(Mutex::new(EigenDABlobWitness::new()));
let cached_eigenda_blob_provider = cached_eigenda_provider::CachedOracleEigenDAProvider::new(eigenda_blob_provider, eigenda_blob_witness);

// If the claimed L2 block number is less than the safe head of the L2 chain, the claim is
// invalid.
let safe_head = fetch_safe_head(oracle.as_ref(), boot.as_ref(), &mut l2_provider).await?;
Expand Down Expand Up @@ -91,17 +99,23 @@ where
beacon,
l1_provider.clone(),
l2_provider.clone(),
eigenda_blob_provider.clone(),
cached_eigenda_blob_provider.clone(),
);
let executor = KonaExecutor::new(&cfg, l2_provider.clone(), l2_provider, None, None);
let mut driver = Driver::new(cursor, executor, pipeline);

// Run the derivation pipeline until we are able to produce the output root of the claimed
// L2 block.
// L2 block.
let (number, output_root) = driver
.advance_to_target(&boot.rollup_config, Some(boot.claimed_l2_block_number))
.await?;

// batch Verify cache
let witness = cached_eigenda_blob_provider.witness.lock().unwrap();
if !witness.batch_verify() {
panic!("batch verify wrong");
}
Comment on lines +115 to +117
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rust has asserts. But why do we panic? prob should not panic and unwrap everywhere, and instead propagate errors?


////////////////////////////////////////////////////////////////
// EPILOGUE //
////////////////////////////////////////////////////////////////
Expand Down
21 changes: 10 additions & 11 deletions bin/host/src/eigenda_fetcher/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,15 +156,15 @@ where
let cert_blob_info = BlobInfo::decode(&mut &item_slice[4..]).unwrap();

// Proxy should return a cert whose data_length measured in symbol (i.e. 32 Bytes)
let blob_length = cert_blob_info.blob_header.data_length as u64;
warn!("blob length: {:?}", blob_length);
let data_length = cert_blob_info.blob_header.data_length as u64;
warn!("data length: {:?}", data_length);

let eigenda_blob = EigenDABlobData::encode(rollup_data.as_ref());

if eigenda_blob.blob.len() != blob_length as usize * BYTES_PER_FIELD_ELEMENT {
if eigenda_blob.blob.len() != data_length as usize * BYTES_PER_FIELD_ELEMENT {
return Err(
anyhow!("data size from cert does not equal to reconstructed data codec_rollup_data_len {} blob size {}",
eigenda_blob.blob.len(), blob_length as usize * BYTES_PER_FIELD_ELEMENT));
eigenda_blob.blob.len(), data_length as usize * BYTES_PER_FIELD_ELEMENT));
}

// Write all the field elements to the key-value store.
Expand All @@ -176,9 +176,9 @@ where
blob_key[..32].copy_from_slice(cert_blob_info.blob_header.commitment.x.as_ref());
blob_key[32..64].copy_from_slice(cert_blob_info.blob_header.commitment.y.as_ref());

trace!("cert_blob_info blob_length {:?}", blob_length);
trace!("cert_blob_info data_length {:?}", data_length);

for i in 0..blob_length {
for i in 0..data_length {
blob_key[88..].copy_from_slice(i.to_be_bytes().as_ref());
let blob_key_hash = keccak256(blob_key.as_ref());

Expand All @@ -192,20 +192,19 @@ where
)?;
}

// TODO proof is at the random point, but we need to figure out where to generate
//
// TODO currenlty proof is only computed in the client side if cached_eigenda_provider
// is used. We can add this back, if hosts needs to get the proof.
// Write the KZG Proof as the last element, needed for ZK
//blob_key[88..].copy_from_slice((blob_length).to_be_bytes().as_ref());
//blob_key[88..].copy_from_slice((data_length).to_be_bytes().as_ref());
//let blob_key_hash = keccak256(blob_key.as_ref());

//kv_write_lock.set(
// PreimageKey::new(*blob_key_hash, PreimageKeyType::Keccak256).into(),
// blob_key.into(),
//)?;
// proof to be done
//kv_write_lock.set(
// PreimageKey::new(*blob_key_hash, PreimageKeyType::GlobalGeneric).into(),
// [1, 2, 3].to_vec(),
// output[64..].to_vec(),
//)?;
} else {
panic!("Invalid hint type: {hint_type}. FetcherWithEigenDASupport.prefetch only supports EigenDACommitment hints.");
Expand Down
17 changes: 17 additions & 0 deletions crates/cryptography/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "hokulea-cryptography"
version = "0.1.0"
edition = "2021"


[dependencies]
rust-kzg-bn254-verifier.workspace = true
rust-kzg-bn254-prover.workspace = true
rust-kzg-bn254-primitives.workspace = true
num.workspace = true
ark-bn254.workspace = true
ark-ff.workspace = true

tracing.workspace = true
alloy-primitives.workspace = true
thiserror.workspace = true
3 changes: 3 additions & 0 deletions crates/cryptography/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# `cryptography`

This crate contains bn254 logics for generating kzg proof for either client or host. This crate uses STD.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't client not have access to std? or do some zkvms have access to the stdlib?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not call this crate kzg-proof or something more explicit?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

crates dir contains a list of crates, some are std some are not

it does both prove and verify,. But sg.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

call it kzg-crypto

12 changes: 12 additions & 0 deletions crates/cryptography/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#![doc = include_str!("../README.md")]
#![warn(
missing_debug_implementations,
missing_docs,
unreachable_pub,
rustdoc::all
)]
#![deny(unused_must_use, rust_2018_idioms)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
#![cfg_attr(not(test), warn(unused_crate_dependencies))]

pub mod witness;
Loading
Loading