From 982d2c5517f135abc52a598000ad9f39f5be025c Mon Sep 17 00:00:00 2001 From: Samuel Laferriere Date: Thu, 21 Nov 2024 23:05:12 +0400 Subject: [PATCH 1/4] style: fix small comment typos --- bin/host/src/providers/blob.rs | 6 +++--- crates/derive/src/sources/blobs.rs | 2 +- crates/proof-sdk/preimage/src/oracle.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bin/host/src/providers/blob.rs b/bin/host/src/providers/blob.rs index a8a96d7c..b64d4afa 100644 --- a/bin/host/src/providers/blob.rs +++ b/bin/host/src/providers/blob.rs @@ -102,11 +102,11 @@ impl OnlineBlobProvider { // Fetch blob sidecars for the slot using the given blob hashes. let sidecars = self.fetch_sidecars(slot, blob_hashes).await?; - // Filter blob sidecars that match the indicies in the specified list. - let blob_hash_indicies = blob_hashes.iter().map(|b| b.index).collect::>(); + // Filter blob sidecars that match the indices in the specified list. + let blob_hash_indices = blob_hashes.iter().map(|b| b.index).collect::>(); let filtered = sidecars .into_iter() - .filter(|s| blob_hash_indicies.contains(&(s.index as usize))) + .filter(|s| blob_hash_indices.contains(&(s.index as usize))) .collect::>(); // Validate the correct number of blob sidecars were retrieved. diff --git a/crates/derive/src/sources/blobs.rs b/crates/derive/src/sources/blobs.rs index 87bb97b5..f63fad8d 100644 --- a/crates/derive/src/sources/blobs.rs +++ b/crates/derive/src/sources/blobs.rs @@ -86,7 +86,7 @@ where continue; } if tx.tx_type() != TxType::Eip4844 { - let blob_data = BlobData { data: None, calldata: Some(calldata.to_vec().into()) }; + let blob_data = BlobData { data: None, calldata: Some(calldata) }; data.push(blob_data); continue; } diff --git a/crates/proof-sdk/preimage/src/oracle.rs b/crates/proof-sdk/preimage/src/oracle.rs index 500a2188..100b90ef 100644 --- a/crates/proof-sdk/preimage/src/oracle.rs +++ b/crates/proof-sdk/preimage/src/oracle.rs @@ -115,7 +115,7 @@ where where F: PreimageFetcher + Send + Sync, { - // Read the preimage request from the client, and throw early if there isn't is any. + // Read the preimage request from the client, and throw early if there isn't any. let mut buf = [0u8; 32]; self.channel.read_exact(&mut buf).await?; let preimage_key = PreimageKey::try_from(buf)?; From e15754672833fb0a4c78803dffa7715dd1e90ef8 Mon Sep 17 00:00:00 2001 From: Samuel Laferriere Date: Sat, 23 Nov 2024 01:46:23 +0400 Subject: [PATCH 2/4] WIP feat: add altda data source + eigenda implementation --- Cargo.lock | 241 +++++++++++++++++- Cargo.toml | 3 + bin/client/src/kona.rs | 9 + bin/host/src/fetcher/mod.rs | 4 + bin/host/src/providers/eigenda.rs | 1 + crates/derive/Cargo.toml | 16 +- crates/derive/src/sources/calldata.rs | 126 ++++++++- crates/derive/src/sources/ethereum.rs | 47 +++- .../derive/src/test_utils/altda_provider.rs | 49 ++++ crates/derive/src/test_utils/mod.rs | 3 + crates/derive/src/traits/data_sources.rs | 25 ++ crates/derive/src/traits/mod.rs | 4 +- crates/derive/src/traits/providers.rs | 2 + .../proof/src/altda/altda_provider.rs | 58 +++++ .../proof/src/altda/eigenda_provider.rs | 39 +++ crates/proof-sdk/proof/src/altda/mod.rs | 7 + crates/proof-sdk/proof/src/hint.rs | 4 + crates/proof-sdk/proof/src/l1/pipeline.rs | 20 +- crates/proof-sdk/proof/src/lib.rs | 2 + 19 files changed, 622 insertions(+), 38 deletions(-) create mode 100644 bin/host/src/providers/eigenda.rs create mode 100644 crates/derive/src/test_utils/altda_provider.rs create mode 100644 crates/proof-sdk/proof/src/altda/altda_provider.rs create mode 100644 crates/proof-sdk/proof/src/altda/eigenda_provider.rs create mode 100644 crates/proof-sdk/proof/src/altda/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 1912dc61..837b200f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -299,7 +299,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower", + "tower 0.5.1", "tracing", "url", "wasmtimer", @@ -463,7 +463,7 @@ dependencies = [ "serde_json", "thiserror 1.0.69", "tokio", - "tower", + "tower 0.5.1", "tracing", "url", "wasmtimer", @@ -479,7 +479,7 @@ dependencies = [ "alloy-transport", "reqwest", "serde_json", - "tower", + "tower 0.5.1", "tracing", "url", ] @@ -781,6 +781,53 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "axum" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper 1.0.2", + "tower 0.5.1", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 1.0.2", + "tower-layer", + "tower-service", +] + [[package]] name = "backtrace" version = "0.3.74" @@ -1458,6 +1505,17 @@ dependencies = [ "spki", ] +[[package]] +name = "eigenda-protos" +version = "0.1.0" +source = "git+https://github.com/samlaf/eigenda-protos.git#837a71a2abd3dbeb68b3258c7884130dfc9cc503" +dependencies = [ + "prost", + "tokio", + "tonic", + "tonic-build", +] + [[package]] name = "either" version = "1.13.0" @@ -1570,6 +1628,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "fnv" version = "1.0.7" @@ -1893,6 +1957,12 @@ version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "hyper" version = "1.5.1" @@ -1906,6 +1976,7 @@ dependencies = [ "http", "http-body", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -1930,6 +2001,19 @@ dependencies = [ "tower-service", ] +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "hyper-tls" version = "0.6.0" @@ -2364,6 +2448,7 @@ dependencies = [ "alloy-rlp", "alloy-rpc-types-engine", "async-trait", + "eigenda-protos", "op-alloy-consensus", "op-alloy-genesis", "op-alloy-protocol", @@ -2375,6 +2460,7 @@ dependencies = [ "spin", "thiserror 2.0.3", "tokio", + "tonic", "tracing", "tracing-subscriber", ] @@ -2630,6 +2716,12 @@ dependencies = [ "hashbrown 0.15.1", ] +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "memchr" version = "2.7.4" @@ -2678,6 +2770,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "multimap" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" + [[package]] name = "munge" version = "0.4.1" @@ -3138,6 +3236,16 @@ dependencies = [ "ucd-trie", ] +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap 2.6.0", +] + [[package]] name = "pin-project" version = "1.1.7" @@ -3251,6 +3359,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prettyplease" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +dependencies = [ + "proc-macro2", + "syn 2.0.87", +] + [[package]] name = "primeorder" version = "0.13.6" @@ -3342,6 +3460,59 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "prost" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c1318b19085f08681016926435853bbf7858f9c082d0999b80550ff5d9abe15" +dependencies = [ + "bytes", + "heck", + "itertools 0.13.0", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn 2.0.87", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" +dependencies = [ + "anyhow", + "itertools 0.13.0", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "prost-types" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4759aa0d3a6232fb8dbdb97b61de2c20047c68aca932c7ed76da9d788508d670" +dependencies = [ + "prost", +] + [[package]] name = "ptr_meta" version = "0.3.0" @@ -4600,6 +4771,70 @@ dependencies = [ "winnow", ] +[[package]] +name = "tonic" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64", + "bytes", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "prost", + "socket2", + "tokio", + "tokio-stream", + "tower 0.4.13", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-build" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "prost-types", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 661e4a01..ac6f45b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -136,3 +136,6 @@ revm = { version = "16.0.0", default-features = false } # K/V database rocksdb = { version = "0.22", default-features = false } + +# grpc +tonic = "0.12.3" \ No newline at end of file diff --git a/bin/client/src/kona.rs b/bin/client/src/kona.rs index 3ba6e120..81013457 100644 --- a/bin/client/src/kona.rs +++ b/bin/client/src/kona.rs @@ -14,6 +14,7 @@ use kona_common_proc::client_entry; use kona_driver::{Driver, DriverError}; use kona_preimage::{HintWriter, OracleReader}; use kona_proof::{ + altda::{self, OracleAltDAProvider, OracleEigenDAProvider}, executor::KonaExecutorConstructor, l1::{OracleBlobProvider, OracleL1ChainProvider, OraclePipeline}, l2::OracleL2ChainProvider, @@ -70,6 +71,13 @@ fn main() -> Result<(), String> { let l1_provider = OracleL1ChainProvider::new(boot.clone(), oracle.clone()); let l2_provider = OracleL2ChainProvider::new(boot.clone(), oracle.clone()); let beacon = OracleBlobProvider::new(oracle.clone()); + let altda_provider = if boot.rollup_config.is_alt_da_enabled() { + // TODO: altda_provider should be a struct that contains all the altda providers, + // such that a rollup can dynamically switch between da providers if needed. + Some(OracleAltDAProvider::new_from_oracle(oracle.clone())) + } else { + None + }; // If the genesis block is claimed, we can exit early. // The agreed upon prestate is consented to by all parties, and there is no state @@ -108,6 +116,7 @@ fn main() -> Result<(), String> { beacon, l1_provider.clone(), l2_provider.clone(), + altda_provider, ); let executor = KonaExecutorConstructor::new( &cfg, diff --git a/bin/host/src/fetcher/mod.rs b/bin/host/src/fetcher/mod.rs index e635335a..9c9eae93 100644 --- a/bin/host/src/fetcher/mod.rs +++ b/bin/host/src/fetcher/mod.rs @@ -507,6 +507,10 @@ where Ok::<(), anyhow::Error>(()) })?; } + HintType::AltDACommitment => { + todo!("AltDACommitment hint type is not yet implemented."); + // TODO: send commitment to da-server + } } Ok(()) diff --git a/bin/host/src/providers/eigenda.rs b/bin/host/src/providers/eigenda.rs new file mode 100644 index 00000000..9aa66c45 --- /dev/null +++ b/bin/host/src/providers/eigenda.rs @@ -0,0 +1 @@ +// TODO: do we want to have our eigenda client provider here? diff --git a/crates/derive/Cargo.toml b/crates/derive/Cargo.toml index e9c27c7b..02b7d347 100644 --- a/crates/derive/Cargo.toml +++ b/crates/derive/Cargo.toml @@ -29,6 +29,10 @@ op-alloy-consensus = { workspace = true, features = ["k256"] } tracing.workspace = true async-trait.workspace = true thiserror.workspace = true +tonic.workspace = true + +# AltDAs +eigenda-protos = { git = "https://github.com/samlaf/eigenda-protos.git" } # `serde` feature dependencies serde = { workspace = true, optional = true, features = ["derive"] } @@ -44,7 +48,12 @@ serde_json.workspace = true op-alloy-registry.workspace = true tokio = { workspace = true, features = ["full"] } tracing-subscriber = { workspace = true, features = ["fmt"] } -alloy-primitives = { workspace = true, features = ["rlp", "k256", "map", "arbitrary"] } +alloy-primitives = { workspace = true, features = [ + "rlp", + "k256", + "map", + "arbitrary", +] } [features] default = ["serde"] @@ -57,7 +66,4 @@ serde = [ "op-alloy-genesis/serde", "op-alloy-rpc-types-engine/serde", ] -test-utils = [ - "dep:spin", - "dep:tracing-subscriber", -] +test-utils = ["dep:spin", "dep:tracing-subscriber"] diff --git a/crates/derive/src/sources/calldata.rs b/crates/derive/src/sources/calldata.rs index 7c0a32cd..959a3932 100644 --- a/crates/derive/src/sources/calldata.rs +++ b/crates/derive/src/sources/calldata.rs @@ -2,7 +2,7 @@ use crate::{ errors::PipelineError, - traits::{ChainProvider, DataAvailabilityProvider}, + traits::{AltDAProvider, ChainProvider, DataAvailabilityProvider}, types::PipelineResult, }; use alloc::{boxed::Box, collections::VecDeque}; @@ -11,14 +11,20 @@ use alloy_primitives::{Address, Bytes}; use async_trait::async_trait; use op_alloy_protocol::BlockInfo; +// TODO: upstream to op_alloy_protocol +const DERIVATION_VERSION_1: u8 = 1; + /// A data iterator that reads from calldata. #[derive(Debug, Clone)] -pub struct CalldataSource +pub struct CalldataSource where CP: ChainProvider + Send, + AP: AltDAProvider + Send, { /// The chain provider to use for the calldata source. pub chain_provider: CP, + /// The altda provider to use to fetch blobs when the calldata contains an altda commitment. + pub altda_provider: Option, /// The batch inbox address. pub batch_inbox_address: Address, /// The L1 Signer. @@ -29,10 +35,26 @@ where pub open: bool, } -impl CalldataSource { +impl CalldataSource +where + CP: ChainProvider + Send, + AP: AltDAProvider + Send, +{ /// Creates a new calldata source. - pub const fn new(chain_provider: CP, batch_inbox_address: Address, signer: Address) -> Self { - Self { chain_provider, batch_inbox_address, signer, calldata: VecDeque::new(), open: false } + pub const fn new( + chain_provider: CP, + altda_provider: Option, + batch_inbox_address: Address, + signer: Address, + ) -> Self { + Self { + chain_provider, + altda_provider, + batch_inbox_address, + signer, + calldata: VecDeque::new(), + open: false, + } } /// Loads the calldata into the source if it is not open. @@ -44,7 +66,7 @@ impl CalldataSource { let (_, txs) = self.chain_provider.block_info_and_transactions_by_hash(block_ref.hash).await?; - self.calldata = txs + let data_or_commitments = txs .iter() .filter_map(|tx| { let (tx_kind, data) = match tx { @@ -63,8 +85,37 @@ impl CalldataSource { } Some(data.to_vec().into()) }) - .collect::>(); + .collect::>(); + // TODO: refactor this to use an async filter_map to fit in previous filter_map + let mut results = VecDeque::new(); + for data_or_commitment in data_or_commitments { + // We fetch blobs from altda when the version byte is 1. + // See https://specs.optimism.io/experimental/alt-da.html#input-commitment-submission + let data = match data_or_commitment[0] { + DERIVATION_VERSION_1 => { + // altda commitment, we need to fetch the data + match self.altda_provider.as_ref() { + Some(provider) => { + match provider.get_blob(data_or_commitment[1..].to_vec().into()).await { + Ok(blob) => blob, + Err(err) => { + warn!("failed to fetch altda commitment: {}", err); + continue; + } + } + } + None => { + warn!("altda commitment found but no altda provider is set"); + continue; + } + } + } + _ => data_or_commitment.clone(), + }; + results.push_back(data); + } + self.calldata = results; self.open = true; Ok(()) @@ -72,7 +123,11 @@ impl CalldataSource { } #[async_trait] -impl DataAvailabilityProvider for CalldataSource { +impl DataAvailabilityProvider for CalldataSource +where + CP: ChainProvider + Send, + AP: AltDAProvider + Send, +{ type Item = Bytes; async fn next(&mut self, block_ref: &BlockInfo) -> PipelineResult { @@ -89,11 +144,22 @@ impl DataAvailabilityProvider for CalldataSource { #[cfg(test)] mod tests { use super::*; - use crate::{errors::PipelineErrorKind, test_utils::TestChainProvider}; + use crate::{ + errors::PipelineErrorKind, + test_utils::{TestAltDAProvider, TestChainProvider}, + }; use alloc::{vec, vec::Vec}; - use alloy_consensus::{Signed, TxEip2930, TxEip4844, TxEip4844Variant, TxLegacy}; + use alloy_consensus::{Signed, TxEip1559, TxEip2930, TxEip4844, TxEip4844Variant, TxLegacy}; use alloy_primitives::{address, Address, PrimitiveSignature as Signature, TxKind}; + pub(crate) fn init_test_logging() { + use tracing_subscriber::layer::SubscriberExt; + let subscriber = + tracing_subscriber::Registry::default().with(tracing_subscriber::fmt::Layer::default()); + tracing::subscriber::set_global_default(subscriber) + .expect("Failed to set tracing subscriber"); + } + pub(crate) fn test_legacy_tx(to: Address) -> TxEnvelope { let sig = Signature::test_signature(); TxEnvelope::Legacy(Signed::new_unchecked( @@ -112,6 +178,15 @@ mod tests { )) } + pub(crate) fn test_eip1559_tx(to: Address, input: Bytes) -> TxEnvelope { + let sig = Signature::test_signature(); + TxEnvelope::Eip1559(Signed::new_unchecked( + TxEip1559 { to: TxKind::Call(to), input, ..Default::default() }, + sig, + Default::default(), + )) + } + pub(crate) fn test_blob_tx(to: Address) -> TxEnvelope { let sig = Signature::test_signature(); TxEnvelope::Eip4844(Signed::new_unchecked( @@ -121,8 +196,14 @@ mod tests { )) } - pub(crate) fn default_test_calldata_source() -> CalldataSource { - CalldataSource::new(TestChainProvider::default(), Default::default(), Default::default()) + pub(crate) fn default_test_calldata_source( + ) -> CalldataSource { + CalldataSource::new( + TestChainProvider::default(), + Some(TestAltDAProvider::default()), + Default::default(), + Default::default(), + ) } #[tokio::test] @@ -216,6 +297,27 @@ mod tests { assert!(source.open); } + #[tokio::test] + async fn test_load_calldata_valid_altda_tx() { + let batch_inbox_address = address!("0123456789012345678901234567890123456789"); + let mut source = default_test_calldata_source(); + source.batch_inbox_address = batch_inbox_address; + let altda_commitment_tx_input = Bytes::from([DERIVATION_VERSION_1, 1, 2, 3]); + let tx = test_eip1559_tx(batch_inbox_address, altda_commitment_tx_input); + source.signer = tx.recover_signer().unwrap(); + let block_info = BlockInfo::default(); + source.chain_provider.insert_block_with_transactions(0, block_info, vec![tx]); + source + .altda_provider + .as_mut() + .unwrap() + .insert_blob(Bytes::from([1, 2, 3]), Bytes::from([4, 5, 6])); + assert!(!source.open); // Source is not open by default. + assert!(source.load_calldata(&BlockInfo::default()).await.is_ok()); + assert!(!source.calldata.is_empty()); // Calldata is NOT empty. + assert!(source.open); + } + #[tokio::test] async fn test_load_calldata_blob_tx_ignored() { let batch_inbox_address = address!("0123456789012345678901234567890123456789"); diff --git a/crates/derive/src/sources/ethereum.rs b/crates/derive/src/sources/ethereum.rs index 22e01bcd..6a19de0d 100644 --- a/crates/derive/src/sources/ethereum.rs +++ b/crates/derive/src/sources/ethereum.rs @@ -3,7 +3,7 @@ use crate::{ sources::{BlobSource, CalldataSource}, - traits::{BlobProvider, ChainProvider, DataAvailabilityProvider}, + traits::{AltDAProvider, BlobProvider, ChainProvider, DataAvailabilityProvider}, types::PipelineResult, }; use alloc::{boxed::Box, fmt::Debug}; @@ -14,50 +14,68 @@ use op_alloy_protocol::BlockInfo; /// A factory for creating an Ethereum data source provider. #[derive(Debug, Clone)] -pub struct EthereumDataSource +pub struct EthereumDataSource where C: ChainProvider + Send + Clone, B: BlobProvider + Send + Clone, + A: AltDAProvider + Send + Clone, { /// The ecotone timestamp. pub ecotone_timestamp: Option, /// The blob source. pub blob_source: BlobSource, /// The calldata source. - pub calldata_source: CalldataSource, + pub calldata_source: CalldataSource, } -impl EthereumDataSource +impl EthereumDataSource where C: ChainProvider + Send + Clone + Debug, B: BlobProvider + Send + Clone + Debug, + A: AltDAProvider + Send + Clone + Debug, { /// Instantiates a new [EthereumDataSource]. pub const fn new( blob_source: BlobSource, - calldata_source: CalldataSource, + calldata_source: CalldataSource, cfg: &RollupConfig, ) -> Self { Self { ecotone_timestamp: cfg.ecotone_time, blob_source, calldata_source } } /// Instantiates a new [EthereumDataSource] from parts. - pub fn new_from_parts(provider: C, blobs: B, cfg: &RollupConfig) -> Self { + pub fn new_from_parts( + chain_provider: C, + blobs: B, + altda_provider: Option, + cfg: &RollupConfig, + ) -> Self { let signer = cfg.genesis.system_config.as_ref().map(|sc| sc.batcher_address).unwrap_or_default(); Self { ecotone_timestamp: cfg.ecotone_time, - blob_source: BlobSource::new(provider.clone(), blobs, cfg.batch_inbox_address, signer), - calldata_source: CalldataSource::new(provider, cfg.batch_inbox_address, signer), + blob_source: BlobSource::new( + chain_provider.clone(), + blobs, + cfg.batch_inbox_address, + signer, + ), + calldata_source: CalldataSource::new( + chain_provider, + altda_provider, + cfg.batch_inbox_address, + signer, + ), } } } #[async_trait] -impl DataAvailabilityProvider for EthereumDataSource +impl DataAvailabilityProvider for EthereumDataSource where C: ChainProvider + Send + Sync + Clone + Debug, B: BlobProvider + Send + Sync + Clone + Debug, + A: AltDAProvider + Send + Sync + Clone + Debug, { type Item = Bytes; @@ -82,7 +100,7 @@ mod tests { use super::*; use crate::{ sources::BlobData, - test_utils::{TestBlobProvider, TestChainProvider}, + test_utils::{TestAltDAProvider, TestBlobProvider, TestChainProvider}, }; use alloy_consensus::TxEnvelope; use alloy_eips::eip2718::Decodable2718; @@ -103,7 +121,8 @@ mod tests { let chain = TestChainProvider::default(); let blob = TestBlobProvider::default(); let cfg = RollupConfig::default(); - let mut calldata = CalldataSource::new(chain.clone(), Address::ZERO, Address::ZERO); + let mut calldata: CalldataSource<_, TestAltDAProvider> = + CalldataSource::new(chain.clone(), None, Address::ZERO, Address::ZERO); calldata.calldata.insert(0, Default::default()); calldata.open = true; let mut blob = BlobSource::new(chain, blob, Address::ZERO, Address::ZERO); @@ -124,7 +143,8 @@ mod tests { let mut blob = default_test_blob_source(); blob.open = true; blob.data.push(BlobData { data: None, calldata: Some(Bytes::default()) }); - let calldata = CalldataSource::new(chain.clone(), Address::ZERO, Address::ZERO); + let calldata: CalldataSource<_, TestAltDAProvider> = + CalldataSource::new(chain.clone(), None, Address::ZERO, Address::ZERO); let cfg = RollupConfig { ecotone_time: Some(0), ..Default::default() }; // Should successfully retrieve a blob batch from the block @@ -151,7 +171,8 @@ mod tests { chain.insert_block_with_transactions(10, block_ref, alloc::vec![tx]); // Should successfully retrieve a calldata batch from the block - let mut data_source = EthereumDataSource::new_from_parts(chain, blob, &cfg); + let mut data_source: EthereumDataSource<_, _, TestAltDAProvider> = + EthereumDataSource::new_from_parts(chain, blob, None, &cfg); let calldata_batch = data_source.next(&block_ref).await.unwrap(); assert_eq!(calldata_batch.len(), 119823); } diff --git a/crates/derive/src/test_utils/altda_provider.rs b/crates/derive/src/test_utils/altda_provider.rs new file mode 100644 index 00000000..e53cdede --- /dev/null +++ b/crates/derive/src/test_utils/altda_provider.rs @@ -0,0 +1,49 @@ +//! An implementation of the [AltDAProvider] trait for tests. + +use crate::errors::{PipelineError, PipelineErrorKind}; +use crate::traits::AltDAProvider; +use alloc::boxed::Box; +use alloy_primitives::map::HashMap; +use alloy_primitives::Bytes; +use async_trait::async_trait; +use core::fmt::Debug; +use thiserror::Error; + +/// Mock data availability provider +#[derive(Debug, Default, Clone)] +pub struct TestAltDAProvider { + blobs: HashMap, +} + +impl TestAltDAProvider { + pub fn new() -> Self { + Self { blobs: HashMap::new() } + } + + pub fn insert_blob(&mut self, commitment: Bytes, blob: Bytes) { + self.blobs.insert(commitment, blob); + } +} + +/// An error for the [TestChainProvider] and [TestL2ChainProvider]. +#[derive(Error, Debug)] +pub enum TestAltDAProviderError { + /// The blob was not found. + #[error("Blob not found")] + BlobNotFound, +} + +impl From for PipelineErrorKind { + fn from(val: TestAltDAProviderError) -> Self { + PipelineError::Provider(val.to_string()).temp() + } +} + +#[async_trait] +impl AltDAProvider for TestAltDAProvider { + type Error = TestAltDAProviderError; + + async fn get_blob(&self, commitment: Bytes) -> Result { + self.blobs.get(&commitment).cloned().ok_or(TestAltDAProviderError::BlobNotFound) + } +} diff --git a/crates/derive/src/test_utils/mod.rs b/crates/derive/src/test_utils/mod.rs index b0f28b5a..0174877b 100644 --- a/crates/derive/src/test_utils/mod.rs +++ b/crates/derive/src/test_utils/mod.rs @@ -13,6 +13,9 @@ pub use blob_provider::TestBlobProvider; mod chain_providers; pub use chain_providers::{TestChainProvider, TestL2ChainProvider, TestProviderError}; +mod altda_provider; +pub use altda_provider::{TestAltDAProvider, TestAltDAProviderError}; + mod data_availability_provider; pub use data_availability_provider::TestDAP; diff --git a/crates/derive/src/traits/data_sources.rs b/crates/derive/src/traits/data_sources.rs index 027e4218..5f3db389 100644 --- a/crates/derive/src/traits/data_sources.rs +++ b/crates/derive/src/traits/data_sources.rs @@ -23,6 +23,31 @@ pub trait BlobProvider { ) -> Result>, Self::Error>; } +/// The AltDAProvider trait specifies the functionality of a data source that can provide altda blobs. +#[async_trait] +pub trait AltDAProvider { + /// The error type for the [EigenDAProvider]. + type Error: Display + ToString + Into; + + async fn get_blob(&self, commitment: Bytes) -> Result; +} + +/// The EigenDAProvider trait specifies the functionality of a data source that can provide eigenda blobs. +#[async_trait] +pub trait EigenDAProvider { + /// The error type for the [EigenDAProvider]. + type Error: Display + ToString + Into; + + async fn get_blob_v1( + &self, + batch_header_hash: Bytes, + blob_index: u64, + ) -> Result; + + // TODO: should this be in a separate v2 trait? + async fn get_blob_v2(&self, blob_hashes: Bytes) -> Result; +} + /// Describes the functionality of a data source that can provide data availability information. #[async_trait] pub trait DataAvailabilityProvider { diff --git a/crates/derive/src/traits/mod.rs b/crates/derive/src/traits/mod.rs index 91f24c10..b6e14ae4 100644 --- a/crates/derive/src/traits/mod.rs +++ b/crates/derive/src/traits/mod.rs @@ -11,7 +11,9 @@ mod attributes; pub use attributes::{AttributesBuilder, AttributesProvider, NextAttributes}; mod data_sources; -pub use data_sources::{BlobProvider, DataAvailabilityProvider}; +pub use data_sources::{ + AltDAProvider, BlobProvider, DataAvailabilityProvider, EigenDAProvider, +}; mod reset; pub use reset::ResetProvider; diff --git a/crates/derive/src/traits/providers.rs b/crates/derive/src/traits/providers.rs index fad1410d..bf07903e 100644 --- a/crates/derive/src/traits/providers.rs +++ b/crates/derive/src/traits/providers.rs @@ -47,6 +47,8 @@ pub trait L2ChainProvider: BatchValidationProviderDerive { ) -> Result::Error>; } +// TODO: add an EigenDAProvider here? + /// A super-trait for [BatchValidationProvider] that binds `Self::Error` to have a conversion into /// [PipelineErrorKind]. pub trait BatchValidationProviderDerive: BatchValidationProvider {} diff --git a/crates/proof-sdk/proof/src/altda/altda_provider.rs b/crates/proof-sdk/proof/src/altda/altda_provider.rs new file mode 100644 index 00000000..eaac56a3 --- /dev/null +++ b/crates/proof-sdk/proof/src/altda/altda_provider.rs @@ -0,0 +1,58 @@ +use alloc::boxed::Box; +use alloc::sync::Arc; +use alloy_primitives::Bytes; +use async_trait::async_trait; +use kona_derive::traits::AltDAProvider; +use kona_preimage::CommsClient; + +use crate::errors::OracleProviderError; + +use super::OracleEigenDAProvider; + +#[derive(Debug, Clone)] +pub struct OracleAltDAProvider { + /// The oracle eigenda provider. + eigenda_provider: OracleEigenDAProvider, +} + +impl OracleAltDAProvider { + /// Constructs a new oracle-backed AltDA provider. + pub fn new(eigenda_provider: OracleEigenDAProvider) -> Self { + Self { eigenda_provider } + } + + /// Constructs a new oracle-backed AltDA provider by constructing + /// the respective altda providers using the oracle. + pub fn new_from_oracle(oracle: Arc) -> Self { + Self { eigenda_provider: OracleEigenDAProvider::new(oracle) } + } +} + +#[async_trait] +impl AltDAProvider for OracleAltDAProvider { + type Error = OracleProviderError; + /// Retrieves a blob from the oracle. + /// + /// ## Takes + /// - `block_ref`: The block reference. + /// - `blob_hash`: The blob hash. + /// + /// ## Returns + /// - `Ok(blob)`: The blob. + /// - `Err(e)`: The blob could not be retrieved. + async fn get_blob(&self, commitment: Bytes) -> Result { + todo!("implement the EigenDA blob retrieval here"); + // match commitment[1] { + // 0 => todo!("keccak commitments are not implemented yet"), + // 1 => match commitment[2] { + // // generic commitments. See https://github.com/ethereum-optimism/specs/discussions/135 + // // for the byte -> da layer mapping. + // 0 => self.eigenda_provider.get_blobs(commitment[3..].into()).ok(), + // 0x0a => todo!("avail commitments are not implemented yet"), + // 0x0c => todo!("celestia commitments are not implemented yet"), + // _ => return None, + // }, + // _ => return None, + // } + } +} diff --git a/crates/proof-sdk/proof/src/altda/eigenda_provider.rs b/crates/proof-sdk/proof/src/altda/eigenda_provider.rs new file mode 100644 index 00000000..ae3fe028 --- /dev/null +++ b/crates/proof-sdk/proof/src/altda/eigenda_provider.rs @@ -0,0 +1,39 @@ +use alloc::boxed::Box; +use alloc::sync::Arc; +use alloy_primitives::Bytes; +use async_trait::async_trait; +use kona_derive::traits::EigenDAProvider; +use kona_preimage::CommsClient; + +use crate::errors::OracleProviderError; + +/// The oracle-backed EigenDA provider for the client program. +#[derive(Debug, Clone)] +pub struct OracleEigenDAProvider { + /// The preimage oracle client. + oracle: Arc, +} + +impl OracleEigenDAProvider { + /// Constructs a new oracle-backed EigenDA provider. + pub fn new(oracle: Arc) -> Self { + Self { oracle } + } +} + +#[async_trait] +impl EigenDAProvider for OracleEigenDAProvider { + type Error = OracleProviderError; + + async fn get_blob_v1( + &self, + batch_header_hash: Bytes, + blob_index: u64, + ) -> Result { + todo!("implement the EigenDA blob retrieval here"); + } + + async fn get_blob_v2(&self, blob_hashes: Bytes) -> Result { + todo!("implement the EigenDA blob retrieval here"); + } +} diff --git a/crates/proof-sdk/proof/src/altda/mod.rs b/crates/proof-sdk/proof/src/altda/mod.rs new file mode 100644 index 00000000..73be33a3 --- /dev/null +++ b/crates/proof-sdk/proof/src/altda/mod.rs @@ -0,0 +1,7 @@ +//! Contains the AltDA-specific constructs of the client program. + +mod eigenda_provider; +pub use eigenda_provider::OracleEigenDAProvider; + +mod altda_provider; +pub use altda_provider::OracleAltDAProvider; \ No newline at end of file diff --git a/crates/proof-sdk/proof/src/hint.rs b/crates/proof-sdk/proof/src/hint.rs index fce3218c..44c25b30 100644 --- a/crates/proof-sdk/proof/src/hint.rs +++ b/crates/proof-sdk/proof/src/hint.rs @@ -36,6 +36,8 @@ pub enum HintType { /// A hint that specifies the proof on the path to a storage slot in an account within in the /// L2 state trie. L2AccountStorageProof, + /// A hint that specifies a commitment, meant to be requested from a da-server. + AltDACommitment, } impl HintType { @@ -63,6 +65,7 @@ impl TryFrom<&str> for HintType { "l2-state-node" => Ok(Self::L2StateNode), "l2-account-proof" => Ok(Self::L2AccountProof), "l2-account-storage-proof" => Ok(Self::L2AccountStorageProof), + "altda-commitment" => Ok(Self::AltDACommitment), _ => Err(HintParsingError(value.to_string())), } } @@ -83,6 +86,7 @@ impl From for &str { HintType::L2StateNode => "l2-state-node", HintType::L2AccountProof => "l2-account-proof", HintType::L2AccountStorageProof => "l2-account-storage-proof", + HintType::AltDACommitment => "altda-commitment", } } } diff --git a/crates/proof-sdk/proof/src/l1/pipeline.rs b/crates/proof-sdk/proof/src/l1/pipeline.rs index b2e1719b..9089de4e 100644 --- a/crates/proof-sdk/proof/src/l1/pipeline.rs +++ b/crates/proof-sdk/proof/src/l1/pipeline.rs @@ -1,6 +1,11 @@ //! Contains an oracle-backed pipeline. -use crate::{l1::OracleL1ChainProvider, l2::OracleL2ChainProvider, FlushableCache}; +use crate::{ + altda::{OracleAltDAProvider, OracleEigenDAProvider}, + l1::OracleL1ChainProvider, + l2::OracleL2ChainProvider, + FlushableCache, +}; use alloc::{boxed::Box, sync::Arc}; use async_trait::async_trait; use core::fmt::Debug; @@ -29,7 +34,8 @@ pub type OracleDerivationPipeline = DerivationPipeline< >; /// An oracle-backed Ethereum data source. -pub type OracleDataProvider = EthereumDataSource, B>; +pub type OracleDataProvider = + EthereumDataSource, B, OracleAltDAProvider>; /// An oracle-backed payload attributes builder for the `AttributesQueue` stage of the derivation /// pipeline. @@ -67,7 +73,7 @@ where impl OraclePipeline where - O: CommsClient + FlushableCache + FlushableCache + Send + Sync + Debug, + O: CommsClient + FlushableCache + Send + Sync + Debug, B: BlobProvider + Send + Sync + Debug + Clone, { /// Constructs a new oracle-backed derivation pipeline. @@ -78,13 +84,19 @@ where blob_provider: B, chain_provider: OracleL1ChainProvider, l2_chain_provider: OracleL2ChainProvider, + altda_provider: Option>, ) -> Self { let attributes = StatefulAttributesBuilder::new( cfg.clone(), l2_chain_provider.clone(), chain_provider.clone(), ); - let dap = EthereumDataSource::new_from_parts(chain_provider.clone(), blob_provider, &cfg); + let dap = EthereumDataSource::new_from_parts( + chain_provider.clone(), + blob_provider, + altda_provider, + &cfg, + ); let pipeline = PipelineBuilder::new() .rollup_config(cfg) diff --git a/crates/proof-sdk/proof/src/lib.rs b/crates/proof-sdk/proof/src/lib.rs index 25a7dd9f..f0e604bd 100644 --- a/crates/proof-sdk/proof/src/lib.rs +++ b/crates/proof-sdk/proof/src/lib.rs @@ -14,6 +14,8 @@ pub mod l1; pub mod l2; +pub mod altda; + pub mod sync; pub mod errors; From f263bf6003b968d69e9476ca761646b606419999 Mon Sep 17 00:00:00 2001 From: Samuel Laferriere Date: Mon, 25 Nov 2024 13:48:00 +0400 Subject: [PATCH 3/4] docs: fix OracleAltDAProvider's get_blob docstring --- crates/proof-sdk/proof/src/altda/altda_provider.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/proof-sdk/proof/src/altda/altda_provider.rs b/crates/proof-sdk/proof/src/altda/altda_provider.rs index eaac56a3..6bf5bf70 100644 --- a/crates/proof-sdk/proof/src/altda/altda_provider.rs +++ b/crates/proof-sdk/proof/src/altda/altda_provider.rs @@ -34,11 +34,10 @@ impl AltDAProvider for OracleAltDAProvider { /// Retrieves a blob from the oracle. /// /// ## Takes - /// - `block_ref`: The block reference. - /// - `blob_hash`: The blob hash. + /// - `commitment`: The commitment to the blob (specific to each AltDA provider). /// /// ## Returns - /// - `Ok(blob)`: The blob. + /// - `Ok(Bytes)`: The blob. /// - `Err(e)`: The blob could not be retrieved. async fn get_blob(&self, commitment: Bytes) -> Result { todo!("implement the EigenDA blob retrieval here"); From ec8d072f2339c2891d82f68f18fa8c29c5e6bcd5 Mon Sep 17 00:00:00 2001 From: Samuel Laferriere Date: Tue, 26 Nov 2024 01:55:32 +0400 Subject: [PATCH 4/4] wip: added parsing capacity for altda commitments + refactored methods to use the enums --- crates/derive/src/sources/altda_data.rs | 200 ++++++++++++++++++ crates/derive/src/sources/calldata.rs | 39 ++-- crates/derive/src/sources/mod.rs | 3 + .../derive/src/test_utils/altda_provider.rs | 7 +- crates/derive/src/traits/data_sources.rs | 17 +- .../proof/src/altda/altda_provider.rs | 32 +-- .../proof/src/altda/eigenda_provider.rs | 27 ++- crates/proof-sdk/proof/src/errors.rs | 3 + 8 files changed, 272 insertions(+), 56 deletions(-) create mode 100644 crates/derive/src/sources/altda_data.rs diff --git a/crates/derive/src/sources/altda_data.rs b/crates/derive/src/sources/altda_data.rs new file mode 100644 index 00000000..92e12736 --- /dev/null +++ b/crates/derive/src/sources/altda_data.rs @@ -0,0 +1,200 @@ +use alloy_primitives::Bytes; +use alloy_rlp::{BufMut, BytesMut}; +use op_alloy_protocol::DERIVATION_VERSION_0; + +// TODO: upstream to op_alloy_protocol +pub(crate) const DERIVATION_VERSION_1: u8 = 1; + +#[derive(Debug, PartialEq)] +/// Submission represents an op-batcher tx's calldata. +/// See https://specs.optimism.io/experimental/alt-da.html#input-commitment-submission +pub(crate) enum BatcherSubmission { + Frames(Bytes), + // For now a batcher tx can only submit a single commitment. + Commitment(AltDACommitment), +} + +#[derive(Debug, PartialEq)] +pub enum AltDACommitment { + Keccak(Bytes), + EigenDAV1(Bytes), + EigenDAV2(Bytes), + Avail(Bytes), + Celestia(Bytes), +} + +impl AltDACommitment { + /// Returns the payload of the commitment, which is the part of the commitment + /// that is specific to the altda layer. + pub fn payload(&self) -> &Bytes { + match self { + AltDACommitment::Keccak(bytes) => bytes, + AltDACommitment::EigenDAV1(bytes) => bytes, + AltDACommitment::EigenDAV2(bytes) => bytes, + AltDACommitment::Avail(bytes) => bytes, + AltDACommitment::Celestia(bytes) => bytes, + } + } + + /// Converts the commitment to its byte representation, following the format specified in + /// https://specs.optimism.io/experimental/alt-da.html#input-commitment-submission + pub fn to_commitment(&self) -> Bytes { + let mut commitment = BytesMut::new(); + commitment.put_u8(DERIVATION_VERSION_1); + match self { + AltDACommitment::Keccak(bytes) => { + commitment.put_u8(0); + commitment.extend_from_slice(bytes); + } + AltDACommitment::EigenDAV1(bytes) => { + commitment.put_u8(1); // generic commitment + commitment.put_u8(0); // eigenda + commitment.put_u8(0); // v1 + commitment.extend_from_slice(bytes); + } + AltDACommitment::EigenDAV2(bytes) => { + commitment.put_u8(1); // generic commitment + commitment.put_u8(0); // eigenda + commitment.put_u8(1); // v2 + commitment.extend_from_slice(bytes); + } + AltDACommitment::Avail(bytes) => { + commitment.put_u8(1); // generic commitment + commitment.put_u8(0x0a); + commitment.extend_from_slice(bytes); + } + AltDACommitment::Celestia(bytes) => { + commitment.put_u8(1); // generic commitment + commitment.put_u8(0x0c); + commitment.extend_from_slice(bytes); + } + } + commitment.freeze().into() + } +} + +impl BatcherSubmission { + /// Parses the submission from the given bytes, following the format specified in + /// https://specs.optimism.io/experimental/alt-da.html#input-commitment-submission + pub(crate) fn parse(bytes: Bytes) -> Option { + if bytes.is_empty() { + return None; + } + match bytes[0] { + DERIVATION_VERSION_0 => Some(BatcherSubmission::Frames(bytes.slice(1..))), + DERIVATION_VERSION_1 if bytes.len() > 1 => match bytes[1] { + 0 if bytes.len() == 2 + 32 => { + let commitment = AltDACommitment::Keccak(bytes.slice(2..)); + Some(BatcherSubmission::Commitment(commitment)) + } + 1 if bytes.len() > 2 => { + let altda_commitment = match bytes[2] { + // See https://github.com/ethereum-optimism/specs/discussions/135 + 0 if bytes.len() > 3 => match bytes[3] { + 0 => AltDACommitment::EigenDAV1(bytes.slice(4..)), + 1 => AltDACommitment::EigenDAV2(bytes.slice(4..)), + _ => return None, + }, + 0x0a => AltDACommitment::Avail(bytes.slice(3..)), + 0x0c => AltDACommitment::Celestia(bytes.slice(3..)), + _ => return None, + }; + Some(BatcherSubmission::Commitment(altda_commitment)) + } + _ => None, + }, + _ => None, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_to_commitment() { + let keccak_commitment = AltDACommitment::Keccak(Bytes::from_static(b"keccak_commitment")); + let eigenda_v1_commitment = + AltDACommitment::EigenDAV1(Bytes::from_static(b"eigenda_commitment")); + let avail_commitment = AltDACommitment::Avail(Bytes::from_static(b"avail_commitment")); + let celestia_commitment = + AltDACommitment::Celestia(Bytes::from_static(b"celestia_commitment")); + + assert_eq!(keccak_commitment.to_commitment(), { + let mut commitment = BytesMut::new(); + commitment.put_u8(DERIVATION_VERSION_1); + commitment.put_u8(0); + commitment.extend_from_slice(b"keccak_commitment"); + commitment.freeze() + }); + + assert_eq!(eigenda_v1_commitment.to_commitment(), { + let mut commitment = BytesMut::new(); + commitment.put_u8(DERIVATION_VERSION_1); + commitment.put_u8(1); + commitment.put_u8(0); + commitment.put_u8(0); + commitment.extend_from_slice(b"eigenda_commitment"); + commitment.freeze() + }); + + assert_eq!(avail_commitment.to_commitment(), { + let mut commitment = BytesMut::new(); + commitment.put_u8(DERIVATION_VERSION_1); + commitment.put_u8(1); + commitment.put_u8(0x0a); + commitment.extend_from_slice(b"avail_commitment"); + commitment.freeze() + }); + + assert_eq!(celestia_commitment.to_commitment(), { + let mut commitment = BytesMut::new(); + commitment.put_u8(DERIVATION_VERSION_1); + commitment.put_u8(1); + commitment.put_u8(0x0c); + commitment.extend_from_slice(b"celestia_commitment"); + commitment.freeze() + }); + } + + #[test] + fn test_parse() { + let frames = Bytes::from_static(b"\x00frames"); + let keccak_commitment = Bytes::from_static(b"\x01\x0012345678901234567890123456789012"); + let fake_keccak_commitment = Bytes::from_static(b"\x01\x00not_a_keccak_commitment"); + let eigenda_v1_commitment = Bytes::from_static(b"\x01\x01\x00\x00eigenda_commitment"); + let avail_commitment = Bytes::from_static(b"\x01\x01\x0aavail_commitment"); + let celestia_commitment = Bytes::from_static(b"\x01\x01\x0ccelestia_commitment"); + + assert_eq!( + BatcherSubmission::parse(frames.clone()), + Some(BatcherSubmission::Frames(frames.slice(1..))) + ); + assert_eq!(BatcherSubmission::parse(fake_keccak_commitment.clone()), None); + assert_eq!( + BatcherSubmission::parse(keccak_commitment.clone()), + Some(BatcherSubmission::Commitment(AltDACommitment::Keccak( + keccak_commitment.slice(2..) + ))) + ); + assert_eq!( + BatcherSubmission::parse(eigenda_v1_commitment.clone()), + Some(BatcherSubmission::Commitment(AltDACommitment::EigenDAV1( + eigenda_v1_commitment.slice(4..) + ))) + ); + assert_eq!( + BatcherSubmission::parse(avail_commitment.clone()), + Some(BatcherSubmission::Commitment(AltDACommitment::Avail( + avail_commitment.slice(3..) + ))) + ); + assert_eq!( + BatcherSubmission::parse(celestia_commitment.clone()), + Some(BatcherSubmission::Commitment(AltDACommitment::Celestia( + celestia_commitment.slice(3..) + ))) + ); + } +} diff --git a/crates/derive/src/sources/calldata.rs b/crates/derive/src/sources/calldata.rs index 959a3932..7b880ef7 100644 --- a/crates/derive/src/sources/calldata.rs +++ b/crates/derive/src/sources/calldata.rs @@ -11,8 +11,7 @@ use alloy_primitives::{Address, Bytes}; use async_trait::async_trait; use op_alloy_protocol::BlockInfo; -// TODO: upstream to op_alloy_protocol -const DERIVATION_VERSION_1: u8 = 1; +use super::altda_data::BatcherSubmission; /// A data iterator that reads from calldata. #[derive(Debug, Clone)] @@ -90,28 +89,27 @@ where // TODO: refactor this to use an async filter_map to fit in previous filter_map let mut results = VecDeque::new(); for data_or_commitment in data_or_commitments { - // We fetch blobs from altda when the version byte is 1. - // See https://specs.optimism.io/experimental/alt-da.html#input-commitment-submission - let data = match data_or_commitment[0] { - DERIVATION_VERSION_1 => { - // altda commitment, we need to fetch the data - match self.altda_provider.as_ref() { - Some(provider) => { - match provider.get_blob(data_or_commitment[1..].to_vec().into()).await { - Ok(blob) => blob, - Err(err) => { - warn!("failed to fetch altda commitment: {}", err); - continue; - } - } - } - None => { - warn!("altda commitment found but no altda provider is set"); + // use parse() to determine the type of commitment + let submission = BatcherSubmission::parse(data_or_commitment.clone()); + let data = match submission { + None => continue, + // return data_or_commitment (including version byte), because frame queue expects it + Some(BatcherSubmission::Frames(_)) => data_or_commitment, + Some(BatcherSubmission::Commitment(altda_commitment)) => { + let provider = if let Some(p) = self.altda_provider.as_ref() { + p + } else { + warn!("altda commitment found but no altda provider is set"); + continue; + }; + match provider.get_blob(altda_commitment).await { + Ok(blob) => blob, + Err(err) => { + warn!("failed to fetch altda commitment: {}", err); continue; } } } - _ => data_or_commitment.clone(), }; results.push_back(data); } @@ -146,6 +144,7 @@ mod tests { use super::*; use crate::{ errors::PipelineErrorKind, + sources::altda_data::DERIVATION_VERSION_1, test_utils::{TestAltDAProvider, TestChainProvider}, }; use alloc::{vec, vec::Vec}; diff --git a/crates/derive/src/sources/mod.rs b/crates/derive/src/sources/mod.rs index 7ddd4b33..6d4803d4 100644 --- a/crates/derive/src/sources/mod.rs +++ b/crates/derive/src/sources/mod.rs @@ -21,3 +21,6 @@ pub use blobs::BlobSource; mod calldata; pub use calldata::CalldataSource; + +mod altda_data; +pub use altda_data::AltDACommitment; diff --git a/crates/derive/src/test_utils/altda_provider.rs b/crates/derive/src/test_utils/altda_provider.rs index e53cdede..307861b2 100644 --- a/crates/derive/src/test_utils/altda_provider.rs +++ b/crates/derive/src/test_utils/altda_provider.rs @@ -1,6 +1,7 @@ //! An implementation of the [AltDAProvider] trait for tests. use crate::errors::{PipelineError, PipelineErrorKind}; +use crate::prelude::AltDACommitment; use crate::traits::AltDAProvider; use alloc::boxed::Box; use alloy_primitives::map::HashMap; @@ -43,7 +44,9 @@ impl From for PipelineErrorKind { impl AltDAProvider for TestAltDAProvider { type Error = TestAltDAProviderError; - async fn get_blob(&self, commitment: Bytes) -> Result { - self.blobs.get(&commitment).cloned().ok_or(TestAltDAProviderError::BlobNotFound) + async fn get_blob(&self, commitment: AltDACommitment) -> Result { + // We extract the bytes out of the altda commitment, regardless of the altda layer. + // Basically every altda layer is mixed into a single HashMap in this test provider + self.blobs.get(commitment.payload()).cloned().ok_or(TestAltDAProviderError::BlobNotFound) } } diff --git a/crates/derive/src/traits/data_sources.rs b/crates/derive/src/traits/data_sources.rs index 5f3db389..2b77a8c6 100644 --- a/crates/derive/src/traits/data_sources.rs +++ b/crates/derive/src/traits/data_sources.rs @@ -1,7 +1,10 @@ //! Contains traits that describe the functionality of various data sources used in the derivation //! pipeline's stages. -use crate::{errors::PipelineErrorKind, sources::IndexedBlobHash, types::PipelineResult}; +use crate::{ + errors::PipelineErrorKind, sources::AltDACommitment, sources::IndexedBlobHash, + types::PipelineResult, +}; use alloc::{boxed::Box, fmt::Debug, string::ToString, vec::Vec}; use alloy_eips::eip4844::Blob; use alloy_primitives::Bytes; @@ -29,7 +32,8 @@ pub trait AltDAProvider { /// The error type for the [EigenDAProvider]. type Error: Display + ToString + Into; - async fn get_blob(&self, commitment: Bytes) -> Result; + /// Fetches a blob for a given commitment. + async fn get_blob(&self, commitment: AltDACommitment) -> Result; } /// The EigenDAProvider trait specifies the functionality of a data source that can provide eigenda blobs. @@ -38,14 +42,9 @@ pub trait EigenDAProvider { /// The error type for the [EigenDAProvider]. type Error: Display + ToString + Into; - async fn get_blob_v1( - &self, - batch_header_hash: Bytes, - blob_index: u64, - ) -> Result; + async fn get_blob_v1(&self, cert: Bytes) -> Result; - // TODO: should this be in a separate v2 trait? - async fn get_blob_v2(&self, blob_hashes: Bytes) -> Result; + async fn get_blob_v2(&self, cert: Bytes) -> Result; } /// Describes the functionality of a data source that can provide data availability information. diff --git a/crates/proof-sdk/proof/src/altda/altda_provider.rs b/crates/proof-sdk/proof/src/altda/altda_provider.rs index 6bf5bf70..313494e9 100644 --- a/crates/proof-sdk/proof/src/altda/altda_provider.rs +++ b/crates/proof-sdk/proof/src/altda/altda_provider.rs @@ -1,8 +1,10 @@ +use crate::alloc::string::ToString; use alloc::boxed::Box; use alloc::sync::Arc; use alloy_primitives::Bytes; use async_trait::async_trait; -use kona_derive::traits::AltDAProvider; +use kona_derive::sources::AltDACommitment; +use kona_derive::traits::{AltDAProvider, EigenDAProvider}; use kona_preimage::CommsClient; use crate::errors::OracleProviderError; @@ -39,19 +41,19 @@ impl AltDAProvider for OracleAltDAProvider { /// ## Returns /// - `Ok(Bytes)`: The blob. /// - `Err(e)`: The blob could not be retrieved. - async fn get_blob(&self, commitment: Bytes) -> Result { - todo!("implement the EigenDA blob retrieval here"); - // match commitment[1] { - // 0 => todo!("keccak commitments are not implemented yet"), - // 1 => match commitment[2] { - // // generic commitments. See https://github.com/ethereum-optimism/specs/discussions/135 - // // for the byte -> da layer mapping. - // 0 => self.eigenda_provider.get_blobs(commitment[3..].into()).ok(), - // 0x0a => todo!("avail commitments are not implemented yet"), - // 0x0c => todo!("celestia commitments are not implemented yet"), - // _ => return None, - // }, - // _ => return None, - // } + async fn get_blob(&self, commitment: AltDACommitment) -> Result { + match commitment { + AltDACommitment::Keccak(_) => Err(OracleProviderError::AltDA( + "keccak commitments are not implemented yet".to_string(), + )), + AltDACommitment::EigenDAV1(cert) => self.eigenda_provider.get_blob_v1(cert).await, + AltDACommitment::EigenDAV2(cert) => self.eigenda_provider.get_blob_v2(cert).await, + AltDACommitment::Avail(_) => Err(OracleProviderError::AltDA( + "avail commitments are not implemented yet".to_string(), + )), + AltDACommitment::Celestia(_) => Err(OracleProviderError::AltDA( + "celestia commitments are not implemented yet".to_string(), + )), + } } } diff --git a/crates/proof-sdk/proof/src/altda/eigenda_provider.rs b/crates/proof-sdk/proof/src/altda/eigenda_provider.rs index ae3fe028..2cca039b 100644 --- a/crates/proof-sdk/proof/src/altda/eigenda_provider.rs +++ b/crates/proof-sdk/proof/src/altda/eigenda_provider.rs @@ -1,11 +1,13 @@ use alloc::boxed::Box; +use alloc::string::ToString; use alloc::sync::Arc; -use alloy_primitives::Bytes; +use alloy_primitives::{keccak256, Bytes}; use async_trait::async_trait; use kona_derive::traits::EigenDAProvider; -use kona_preimage::CommsClient; +use kona_preimage::{CommsClient, PreimageKey, PreimageKeyType}; use crate::errors::OracleProviderError; +use crate::HintType; /// The oracle-backed EigenDA provider for the client program. #[derive(Debug, Clone)] @@ -25,15 +27,20 @@ impl OracleEigenDAProvider { impl EigenDAProvider for OracleEigenDAProvider { type Error = OracleProviderError; - async fn get_blob_v1( - &self, - batch_header_hash: Bytes, - blob_index: u64, - ) -> Result { - todo!("implement the EigenDA blob retrieval here"); + async fn get_blob_v1(&self, cert: Bytes) -> Result { + self.oracle + .write(&HintType::AltDACommitment.encode_with(&[&cert])) + .await + .map_err(OracleProviderError::Preimage)?; + let data = self + .oracle + .get(PreimageKey::new(*keccak256(cert), PreimageKeyType::GlobalGeneric)) + .await + .map_err(OracleProviderError::Preimage)?; + Ok(data.into()) } - async fn get_blob_v2(&self, blob_hashes: Bytes) -> Result { - todo!("implement the EigenDA blob retrieval here"); + async fn get_blob_v2(&self, _cert: Bytes) -> Result { + Err(OracleProviderError::AltDA("eigenda v2 not implemented".to_string())) } } diff --git a/crates/proof-sdk/proof/src/errors.rs b/crates/proof-sdk/proof/src/errors.rs index 7de8302b..169fe978 100644 --- a/crates/proof-sdk/proof/src/errors.rs +++ b/crates/proof-sdk/proof/src/errors.rs @@ -34,6 +34,9 @@ pub enum OracleProviderError { /// Serde error. #[error("Serde error: {0}")] Serde(serde_json::Error), + /// AltDA error. + #[error("AltDA error: {0}")] + AltDA(String), } impl From for PipelineErrorKind {