diff --git a/core/bin/zksync_server/src/node_builder.rs b/core/bin/zksync_server/src/node_builder.rs index caaf603b60b..2b0a88bc448 100644 --- a/core/bin/zksync_server/src/node_builder.rs +++ b/core/bin/zksync_server/src/node_builder.rs @@ -296,7 +296,13 @@ impl MainNodeBuilder { } fn add_eigenda_proxy_layer(mut self) -> anyhow::Result { - self.node.add_layer(EigenDAProxyLayer::new()); + let da_config = try_load_config!(self.configs.da_client_config); + match da_config { + DAClientConfig::EigenDA(config) => { + self.node.add_layer(EigenDAProxyLayer::new(config)); + } + _ => {} + } Ok(self) } diff --git a/core/lib/config/src/configs/da_client/eigen_da.rs b/core/lib/config/src/configs/da_client/eigen_da.rs index b9222618dc0..5c77d32846b 100644 --- a/core/lib/config/src/configs/da_client/eigen_da.rs +++ b/core/lib/config/src/configs/da_client/eigen_da.rs @@ -1,8 +1,30 @@ use serde::Deserialize; +#[derive(Clone, Debug, PartialEq, Deserialize)] + +pub enum EigenDAConfig { + MemStore(MemStoreConfig), + Disperser(DisperserConfig), +} + +#[derive(Clone, Debug, PartialEq, Deserialize, Default)] +pub struct MemStoreConfig { + pub api_node_url: String, // todo: This should be removed once eigenda proxy is no longer used + pub custom_quorum_numbers: Option>, // todo: This should be removed once eigenda proxy is no longer used + pub account_id: Option, // todo: This should be removed once eigenda proxy is no longer used + pub max_blob_size_bytes: u64, + pub blob_expiration: u64, + pub get_latency: u64, + pub put_latency: u64, +} + #[derive(Clone, Debug, PartialEq, Deserialize, Default)] -pub struct EigenDAConfig { - pub api_node_url: String, - pub custom_quorum_numbers: Option>, - pub account_id: Option, +pub struct DisperserConfig { + pub api_node_url: String, // todo: This should be removed once eigenda proxy is no longer used + pub custom_quorum_numbers: Option>, // todo: This should be removed once eigenda proxy is no longer used + pub account_id: Option, // todo: This should be removed once eigenda proxy is no longer used + pub disperser_rpc: String, + pub eth_confirmation_depth: i32, + pub eigenda_eth_rpc: String, + pub eigenda_svc_manager_addr: String, } diff --git a/core/lib/protobuf_config/src/da_client.rs b/core/lib/protobuf_config/src/da_client.rs index 597557db886..f6798bd27d3 100644 --- a/core/lib/protobuf_config/src/da_client.rs +++ b/core/lib/protobuf_config/src/da_client.rs @@ -1,11 +1,11 @@ use anyhow::Context; use zksync_config::{ configs::{ + self, da_client::{ - eigen_da::EigenDAConfig, + eigen_da::{DisperserConfig, EigenDAConfig, MemStoreConfig}, DAClientConfig::{Avail, EigenDA, ObjectStore}, }, - {self}, }, AvailConfig, }; @@ -34,13 +34,54 @@ impl ProtoRepr for proto::DataAvailabilityClient { proto::data_availability_client::Config::ObjectStore(conf) => { ObjectStore(object_store_proto::ObjectStore::read(conf)?) } - proto::data_availability_client::Config::EigenDa(conf) => EigenDA(EigenDAConfig { - api_node_url: required(&conf.api_node_url) - .context("api_node_url")? - .clone(), - custom_quorum_numbers: Some(conf.custom_quorum_numbers.clone()), - account_id: conf.account_id.clone(), - }), + proto::data_availability_client::Config::EigenDa(conf) => { + let config = required(&conf.config).context("config")?; + let eigenda_config = match config { + proto::eigen_da_config::Config::MemStore(conf) => { + EigenDAConfig::MemStore(MemStoreConfig { + api_node_url: required(&conf.api_node_url) + .context("api_node_url")? + .clone(), + custom_quorum_numbers: Some(conf.custom_quorum_numbers.clone()), + account_id: conf.account_id.clone(), + max_blob_size_bytes: required(&conf.max_blob_size_bytes) + .context("max_blob_size_bytes")? + .clone(), + blob_expiration: required(&conf.blob_expiration) + .context("blob_expiration")? + .clone(), + get_latency: required(&conf.get_latency) + .context("get_latency")? + .clone(), + put_latency: required(&conf.put_latency) + .context("put_latency")? + .clone(), + }) + } + proto::eigen_da_config::Config::Disperser(conf) => { + EigenDAConfig::Disperser(DisperserConfig { + api_node_url: required(&conf.api_node_url) + .context("api_node_url")? + .clone(), + custom_quorum_numbers: Some(conf.custom_quorum_numbers.clone()), + account_id: conf.account_id.clone(), + disperser_rpc: required(&conf.disperser_rpc) + .context("disperser_rpc")? + .clone(), + eth_confirmation_depth: required(&conf.eth_confirmation_depth) + .context("eth_confirmation_depth")? + .clone(), + eigenda_eth_rpc: required(&conf.eigenda_eth_rpc) + .context("eigenda_eth_rpc")? + .clone(), + eigenda_svc_manager_addr: required(&conf.eigenda_svc_manager_addr) + .context("eigenda_svc_manager_addr")? + .clone(), + }) + } + }; + EigenDA(eigenda_config) + } }; Ok(client) @@ -64,17 +105,49 @@ impl ProtoRepr for proto::DataAvailabilityClient { object_store_proto::ObjectStore::build(config), )), }, - EigenDA(config) => Self { - config: Some(proto::data_availability_client::Config::EigenDa( - proto::EigenDaConfig { - api_node_url: Some(config.api_node_url.clone()), - custom_quorum_numbers: config - .custom_quorum_numbers - .clone() - .unwrap_or_default(), - account_id: config.account_id.clone(), - }, - )), + EigenDA(config) => match config { + EigenDAConfig::MemStore(config) => Self { + config: Some(proto::data_availability_client::Config::EigenDa( + proto::EigenDaConfig { + config: Some(proto::eigen_da_config::Config::MemStore( + proto::MemStoreConfig { + api_node_url: Some(config.api_node_url.clone()), + custom_quorum_numbers: config + .custom_quorum_numbers + .clone() + .unwrap_or_default(), + account_id: config.account_id.clone(), + max_blob_size_bytes: Some(config.max_blob_size_bytes), + blob_expiration: Some(config.blob_expiration), + get_latency: Some(config.get_latency), + put_latency: Some(config.put_latency), + }, + )), + }, + )), + }, + EigenDAConfig::Disperser(config) => Self { + config: Some(proto::data_availability_client::Config::EigenDa( + proto::EigenDaConfig { + config: Some(proto::eigen_da_config::Config::Disperser( + proto::DisperserConfig { + api_node_url: Some(config.api_node_url.clone()), + custom_quorum_numbers: config + .custom_quorum_numbers + .clone() + .unwrap_or_default(), + account_id: config.account_id.clone(), + disperser_rpc: Some(config.disperser_rpc.clone()), + eth_confirmation_depth: Some(config.eth_confirmation_depth), + eigenda_eth_rpc: Some(config.eigenda_eth_rpc.clone()), + eigenda_svc_manager_addr: Some( + config.eigenda_svc_manager_addr.clone(), + ), + }, + )), + }, + )), + }, }, } } diff --git a/core/lib/protobuf_config/src/proto/config/da_client.proto b/core/lib/protobuf_config/src/proto/config/da_client.proto index 04e00e985e7..18c1dc94f90 100644 --- a/core/lib/protobuf_config/src/proto/config/da_client.proto +++ b/core/lib/protobuf_config/src/proto/config/da_client.proto @@ -13,10 +13,31 @@ message AvailConfig { reserved 3; reserved "seed"; } +message MemStoreConfig { + optional string api_node_url = 1; // TODO: This should be removed once eigenda proxy is no longer used + repeated uint32 custom_quorum_numbers = 2; // TODO: This should be removed once eigenda proxy is no longer used + optional string account_id = 3; // TODO: This should be removed once eigenda proxy is no longer used + optional uint64 max_blob_size_bytes = 5; + optional uint64 blob_expiration = 6; + optional uint64 get_latency = 7; + optional uint64 put_latency = 8; +} + +message DisperserConfig { + optional string api_node_url = 1; // TODO: This should be removed once eigenda proxy is no longer used + repeated uint32 custom_quorum_numbers = 2; // TODO: This should be removed once eigenda proxy is no longer used + optional string account_id = 3; // TODO: This should be removed once eigenda proxy is no longer used + optional string disperser_rpc = 4; + optional int32 eth_confirmation_depth = 5; + optional string eigenda_eth_rpc = 6; + optional string eigenda_svc_manager_addr = 7; +} + message EigenDaConfig { - optional string api_node_url = 1; - repeated uint32 custom_quorum_numbers = 2; - optional string account_id = 3; + oneof config { + MemStoreConfig mem_store = 1; + DisperserConfig disperser = 2; + } } message DataAvailabilityClient { diff --git a/core/node/da_clients/src/eigen_da.rs b/core/node/da_clients/src/eigen_da.rs index f2eff70faa3..a2ddf83e592 100644 --- a/core/node/da_clients/src/eigen_da.rs +++ b/core/node/da_clients/src/eigen_da.rs @@ -31,9 +31,13 @@ impl DataAvailabilityClient for EigenDAClient { _batch_number: u32, blob_data: Vec, ) -> Result { + let api_node_url = match &self.config { + EigenDAConfig::MemStore(config) => &config.api_node_url, + EigenDAConfig::Disperser(config) => &config.api_node_url, + }; // TODO: This should be removed once eigenda proxy is no longer used let response = self .client - .post(format!("{}/put/", self.config.api_node_url)) + .post(format!("{}/put/", api_node_url)) .header(http::header::CONTENT_TYPE, "application/octetstream") .body(blob_data) .send() @@ -53,9 +57,13 @@ impl DataAvailabilityClient for EigenDAClient { &self, blob_id: &str, ) -> anyhow::Result, types::DAError> { + let api_node_url = match &self.config { + EigenDAConfig::MemStore(config) => &config.api_node_url, + EigenDAConfig::Disperser(config) => &config.api_node_url, + }; // TODO: This should be removed once eigenda proxy is no longer used let response = self .client - .get(format!("{}/get/0x{}", self.config.api_node_url, blob_id)) + .get(format!("{}/get/0x{}", api_node_url, blob_id)) .send() .await .map_err(to_retriable_error)?; diff --git a/core/node/node_framework/src/implementations/layers/da_clients/eigen_da.rs b/core/node/node_framework/src/implementations/layers/da_clients/eigen_da.rs index b7c1025309c..460fe9e47b9 100644 --- a/core/node/node_framework/src/implementations/layers/da_clients/eigen_da.rs +++ b/core/node/node_framework/src/implementations/layers/da_clients/eigen_da.rs @@ -8,7 +8,7 @@ use crate::{ IntoContext, }; -#[derive(Debug, Default)] +#[derive(Debug)] pub struct EigenDAWiringLayer { config: EigenDAConfig, } diff --git a/core/node/node_framework/src/implementations/layers/eigenda_proxy.rs b/core/node/node_framework/src/implementations/layers/eigenda_proxy.rs index ae0f1affc5a..2820030a843 100644 --- a/core/node/node_framework/src/implementations/layers/eigenda_proxy.rs +++ b/core/node/node_framework/src/implementations/layers/eigenda_proxy.rs @@ -1,3 +1,5 @@ +use zksync_config::configs::da_client::eigen_da::EigenDAConfig; + use crate::{ implementations::resources::{ object_store::ObjectStoreResource, @@ -11,7 +13,9 @@ use crate::{ /// Wiring layer for eigenda server. #[derive(Debug)] -pub struct EigenDAProxyLayer {} +pub struct EigenDAProxyLayer { + eigenda_config: EigenDAConfig, +} #[derive(Debug, FromContext)] #[context(crate = crate)] @@ -28,8 +32,8 @@ pub struct Output { } impl EigenDAProxyLayer { - pub fn new() -> Self { - Self {} + pub fn new(eigenda_config: EigenDAConfig) -> Self { + Self { eigenda_config } } } diff --git a/eigenda-integration.md b/eigenda-integration.md index 4020a61a522..36c88c328dd 100644 --- a/eigenda-integration.md +++ b/eigenda-integration.md @@ -1,13 +1,17 @@ # Zksync-era <> EigenDA Integration -EigenDA is as a high-throughput data availability layer for rollups. It is an EigenLayer AVS (Actively Validated Service), so it leverages Ethereum's economic security instead of bootstrapping a new network with its own validators. +EigenDA is as a high-throughput data availability layer for rollups. It is an EigenLayer AVS (Actively Validated +Service), so it leverages Ethereum's economic security instead of bootstrapping a new network with its own validators. For more information you can check the [docs](https://docs.eigenda.xyz/). ## Scope -The scope of this first milestone is to spin up a local EigenDA dev environment, spin up a local zksync-era dev environment and integrate them. Instead of sending 4844 blobs, the zksync-era sends blobs to EigenDA. EigenDA provides a high level client called [eigenda-proxy](https://github.com/Layr-Labs/eigenda-proxy), and it is used to communicate with the EigenDA disperser in a secury and easy way. -On L1, mock the verification logic, such that blocks continue building. Increase the blob size from 4844 size to 2MiB blob. -Deploy the integration to Holesky testnet and provide scripts to setup a network using EigenDA as DA provider. +The scope of this first milestone is to spin up a local EigenDA dev environment, spin up a local zksync-era dev +environment and integrate them. Instead of sending 4844 blobs, the zksync-era sends blobs to EigenDA. EigenDA provides a +high level client called [eigenda-proxy](https://github.com/Layr-Labs/eigenda-proxy), and it is used to communicate with +the EigenDA disperser in a secury and easy way. On L1, mock the verification logic, such that blocks continue building. +Increase the blob size from 4844 size to 2MiB blob. Deploy the integration to Holesky testnet and provide scripts to +setup a network using EigenDA as DA provider. ## Common changes @@ -15,10 +19,30 @@ Changes needed both for local and mainnet/testnet setup. 1. Add `da_client` to `etc/env/file_based/general.yaml`: +If you want to use memstore: + +```yaml +da_client: + eigen_da: + memstore: + api_node_url: http://127.0.0.1:4242 # TODO: This should be removed once eigenda proxy is no longer used + max_blob_size_bytes: 2097152 + blob_expiration: 100000 + get_latency: 100 + put_latency: 100 +``` + +If you want to use disperser: + ```yaml da_client: eigen_da: - api_node_url: http://127.0.0.1:4242 + disperser: + api_node_url: http://127.0.0.1:4242 # TODO: This should be removed once eigenda proxy is no longer used + disperser_rpc: + eth_confirmation_depth: -1 + eigenda_eth_rpc: + eigenda_svc_manager_addr: '0xD4A7E1Bd8015057293f0D0A557088c286942e84b' ``` 2. Add `eigenda-proxy` to the `docker-compose.yml` file: @@ -27,7 +51,7 @@ da_client: eigenda-proxy: image: ghcr.io/layr-labs/eigenda-proxy ports: - - "4242:4242" + - '4242:4242' command: ./eigenda-proxy --addr 0.0.0.0 --port 4242 --memstore.enabled --eigenda-max-blob-length "2MiB" ```