-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(eigen-client-extra-features): Add soft confirmations #322
Changes from 10 commits
226834f
27413ac
0dd2289
7f5d01e
ab03654
c565e84
f15932c
dc5b721
1bac89a
70b10a9
46838fc
d450f8a
ec1a65a
7864534
edafcff
645f15e
3bd5812
860bc28
f892e99
22fd8a2
84cbecd
07f4a22
1476acf
04a144f
0b64699
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,36 +9,27 @@ use zksync_da_client::{ | |
DataAvailabilityClient, | ||
}; | ||
|
||
use super::sdk::RawEigenClient; | ||
use super::{memstore::MemStore, sdk::RawEigenClient, Disperser}; | ||
use crate::utils::to_non_retriable_da_error; | ||
|
||
#[derive(Debug, Clone)] | ||
pub struct EigenClient { | ||
client: Arc<RawEigenClient>, | ||
client: Disperser, | ||
} | ||
|
||
impl EigenClient { | ||
pub async fn new(config: EigenConfig, secrets: EigenSecrets) -> anyhow::Result<Self> { | ||
let private_key = SecretKey::from_str(secrets.private_key.0.expose_secret().as_str()) | ||
.map_err(|e| anyhow::anyhow!("Failed to parse private key: {}", e))?; | ||
|
||
match config { | ||
let disperser: Disperser = match config.clone() { | ||
EigenConfig::Disperser(config) => { | ||
// TODO: add complete config | ||
let client = RawEigenClient::new( | ||
config.disperser_rpc, | ||
config.status_query_interval, | ||
private_key, | ||
) | ||
.await?; | ||
Ok(EigenClient { | ||
client: Arc::new(client), | ||
}) | ||
let client = RawEigenClient::new(private_key, config).await?; | ||
Disperser::Remote(Arc::new(client)) | ||
} | ||
EigenConfig::MemStore(_) => { | ||
todo!() | ||
} | ||
} | ||
EigenConfig::MemStore(config) => Disperser::Memory(MemStore::new(config)), | ||
}; | ||
Ok(Self { client: disperser }) | ||
} | ||
} | ||
|
||
|
@@ -49,13 +40,16 @@ impl DataAvailabilityClient for EigenClient { | |
_: u32, // batch number | ||
data: Vec<u8>, | ||
) -> Result<DispatchResponse, DAError> { | ||
let blob_id = self | ||
.client | ||
.dispatch_blob(data) | ||
.await | ||
.map_err(to_non_retriable_da_error)?; | ||
|
||
Ok(DispatchResponse::from(blob_id)) | ||
match &self.client { | ||
Disperser::Remote(remote_disperser) => { | ||
let blob_id = remote_disperser | ||
.dispatch_blob(data) | ||
.await | ||
.map_err(to_non_retriable_da_error)?; | ||
Ok(DispatchResponse::from(blob_id)) | ||
} | ||
Disperser::Memory(memstore) => memstore.clone().store_blob(data).await, | ||
} | ||
} | ||
|
||
async fn get_inclusion_data(&self, _: &str) -> Result<Option<InclusionData>, DAError> { | ||
|
@@ -70,3 +64,143 @@ impl DataAvailabilityClient for EigenClient { | |
Some(1920 * 1024) // 2mb - 128kb as a buffer | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
impl EigenClient { | ||
pub async fn get_blob_data(&self, blob_id: &str) -> anyhow::Result<Option<Vec<u8>>, DAError> { | ||
match &self.client { | ||
Disperser::Remote(remote_client) => remote_client.get_blob_data(blob_id).await, | ||
Disperser::Memory(memstore) => memstore.clone().get_blob_data(blob_id).await, | ||
} | ||
} | ||
} | ||
|
||
pub fn to_retriable_error(error: anyhow::Error) -> DAError { | ||
DAError { | ||
error, | ||
is_retriable: true, | ||
} | ||
} | ||
#[cfg(test)] | ||
mod tests { | ||
use zksync_config::configs::da_client::eigen::{DisperserConfig, MemStoreConfig}; | ||
use zksync_types::secrets::PrivateKey; | ||
|
||
use super::*; | ||
use crate::eigen::blob_info::BlobInfo; | ||
|
||
#[tokio::test] | ||
async fn test_eigenda_memory_disperser() { | ||
let config = EigenConfig::MemStore(MemStoreConfig { | ||
max_blob_size_bytes: 2 * 1024 * 1024, // 2MB, | ||
blob_expiration: 60 * 2, | ||
get_latency: 0, | ||
put_latency: 0, | ||
}); | ||
let secrets = EigenSecrets { | ||
private_key: PrivateKey::from_str( | ||
"d08aa7ae1bb5ddd46c3c2d8cdb5894ab9f54dec467233686ca42629e826ac4c6", | ||
) | ||
.unwrap(), | ||
}; | ||
let client = EigenClient::new(config, secrets).await.unwrap(); | ||
let data = vec![1u8; 100]; | ||
let result = client.dispatch_blob(0, data.clone()).await.unwrap(); | ||
|
||
let blob_info: BlobInfo = | ||
rlp::decode(&hex::decode(result.blob_id.clone()).unwrap()).unwrap(); | ||
// TODO: once get inclusion data is added, check it | ||
|
||
let retrieved_data = client.get_blob_data(&result.blob_id).await.unwrap(); | ||
assert_eq!(retrieved_data.unwrap(), data); | ||
} | ||
|
||
#[tokio::test] | ||
async fn test_non_auth_dispersal() { | ||
let config = EigenConfig::Disperser(DisperserConfig { | ||
custom_quorum_numbers: None, | ||
disperser_rpc: "https://disperser-holesky.eigenda.xyz:443".to_string(), | ||
eth_confirmation_depth: -1, | ||
eigenda_eth_rpc: String::default(), | ||
eigenda_svc_manager_address: "0xD4A7E1Bd8015057293f0D0A557088c286942e84b".to_string(), | ||
blob_size_limit: 2 * 1024 * 1024, // 2MB | ||
status_query_timeout: 1800, // 30 minutes | ||
status_query_interval: 5, // 5 ms | ||
wait_for_finalization: false, | ||
authenticaded: false, | ||
}); | ||
let secrets = EigenSecrets { | ||
private_key: PrivateKey::from_str( | ||
"d08aa7ae1bb5ddd46c3c2d8cdb5894ab9f54dec467233686ca42629e826ac4c6", | ||
) | ||
.unwrap(), | ||
}; | ||
let client = EigenClient::new(config, secrets).await.unwrap(); | ||
let data = vec![1; 20]; | ||
let result = client.dispatch_blob(0, data.clone()).await.unwrap(); | ||
let blob_info: BlobInfo = | ||
rlp::decode(&hex::decode(result.blob_id.clone()).unwrap()).unwrap(); | ||
// TODO: once get inclusion data is added, check it | ||
let retrieved_data = client.get_blob_data(&result.blob_id).await.unwrap(); | ||
assert_eq!(retrieved_data.unwrap(), data); | ||
} | ||
#[tokio::test] | ||
async fn test_auth_dispersal() { | ||
let config = EigenConfig::Disperser(DisperserConfig { | ||
custom_quorum_numbers: None, | ||
disperser_rpc: "https://disperser-holesky.eigenda.xyz:443".to_string(), | ||
eth_confirmation_depth: -1, | ||
eigenda_eth_rpc: String::default(), | ||
eigenda_svc_manager_address: "0xD4A7E1Bd8015057293f0D0A557088c286942e84b".to_string(), | ||
blob_size_limit: 2 * 1024 * 1024, // 2MB | ||
status_query_timeout: 1800, // 30 minutes | ||
status_query_interval: 5, // 5 ms | ||
wait_for_finalization: false, | ||
authenticaded: true, | ||
}); | ||
let secrets = EigenSecrets { | ||
private_key: PrivateKey::from_str( | ||
"d08aa7ae1bb5ddd46c3c2d8cdb5894ab9f54dec467233686ca42629e826ac4c6", | ||
) | ||
.unwrap(), | ||
}; | ||
let client = EigenClient::new(config, secrets).await.unwrap(); | ||
let data = vec![1; 20]; | ||
let result = client.dispatch_blob(0, data.clone()).await.unwrap(); | ||
let blob_info: BlobInfo = | ||
rlp::decode(&hex::decode(result.blob_id.clone()).unwrap()).unwrap(); | ||
// TODO: once get inclusion data is added, check it | ||
let retrieved_data = client.get_blob_data(&result.blob_id).await.unwrap(); | ||
assert_eq!(retrieved_data.unwrap(), data); | ||
} | ||
|
||
#[tokio::test] | ||
async fn test_wait_for_finalization() { | ||
let config = EigenConfig::Disperser(DisperserConfig { | ||
custom_quorum_numbers: None, | ||
disperser_rpc: "https://disperser-holesky.eigenda.xyz:443".to_string(), | ||
eth_confirmation_depth: -1, | ||
eigenda_eth_rpc: String::default(), | ||
eigenda_svc_manager_address: "0xD4A7E1Bd8015057293f0D0A557088c286942e84b".to_string(), | ||
blob_size_limit: 2 * 1024 * 1024, // 2MB | ||
status_query_timeout: 1800, // 30 minutes | ||
status_query_interval: 5000, // 5000 ms | ||
wait_for_finalization: true, | ||
authenticaded: true, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typo: authenticaded -> authenticated |
||
}); | ||
let secrets = EigenSecrets { | ||
private_key: PrivateKey::from_str( | ||
"d08aa7ae1bb5ddd46c3c2d8cdb5894ab9f54dec467233686ca42629e826ac4c6", | ||
) | ||
.unwrap(), | ||
}; | ||
let client = EigenClient::new(config, secrets).await.unwrap(); | ||
let data = vec![1; 20]; | ||
let result = client.dispatch_blob(0, data.clone()).await.unwrap(); | ||
let blob_info: BlobInfo = | ||
rlp::decode(&hex::decode(result.blob_id.clone()).unwrap()).unwrap(); | ||
// TODO: once get inclusion data is added, check it | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think inclusion was already added. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done! |
||
let retrieved_data = client.get_blob_data(&result.blob_id).await.unwrap(); | ||
assert_eq!(retrieved_data.unwrap(), data); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is the configuration documented somewhere? I found confusing that the numbers in status_query_timeout and status_query_interval (both related to the status_query) have different units.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done