rust_eigenda_v2_client/
lib.rs

1pub mod accountant;
2pub mod cert_verifier;
3pub mod commitment_utils;
4pub mod core;
5pub mod disperser_client;
6pub mod errors;
7pub mod payload_disperser;
8pub mod relay_client;
9pub mod relay_payload_retriever;
10pub mod relay_registry;
11pub mod utils;
12// So users can use the client without having to depend on the signers crate as well.
13pub use rust_eigenda_signers;
14
15#[allow(clippy::all)]
16pub(crate) mod generated {
17    pub(crate) mod common {
18        include!("generated/common.rs");
19
20        pub(crate) mod v2 {
21            include!("generated/common.v2.rs");
22        }
23    }
24
25    pub(crate) mod disperser {
26        pub(crate) mod v2 {
27            include!("generated/disperser.v2.rs");
28        }
29    }
30
31    pub(crate) mod encoder {
32        pub(crate) mod v2 {
33            include!("generated/encoder.v2.rs");
34        }
35    }
36
37    pub(crate) mod retriever {
38        pub(crate) mod v2 {
39            include!("generated/retriever.v2.rs");
40        }
41    }
42
43    pub(crate) mod validator {
44        include!("generated/validator.rs");
45    }
46
47    pub(crate) mod relay {
48        include!("generated/relay.rs");
49    }
50
51    pub(crate) mod contract_bindings {
52        alloy::sol! {
53            #[sol(rpc)]
54            IEigenDACertVerifier, concat!(env!("CARGO_MANIFEST_DIR"), "/src/generated/abi/IEigenDACertVerifier.json"),
55        }
56
57        alloy::sol! {
58            #[sol(rpc)]
59            IEigenDACertVerifierBase, concat!(env!("CARGO_MANIFEST_DIR"), "/src/generated/abi/IEigenDACertVerifierBase.json"),
60        }
61
62        alloy::sol! {
63            #[sol(rpc)]
64            IRelayRegistry, concat!(env!("CARGO_MANIFEST_DIR"), "/src/generated/abi/IEigenDARelayRegistry.json"),
65        }
66
67        alloy::sol! {
68            #[sol(rpc)]
69            IEigenDACertVerifierRouter, concat!(env!("CARGO_MANIFEST_DIR"), "/src/generated/abi/IEigenDACertVerifierRouter.json"),
70        }
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use alloy::primitives::Address;
77    use dotenv::dotenv;
78    use rust_eigenda_v2_common::{EigenDACert, Payload, PayloadForm};
79    use std::{env, str::FromStr, time::Duration};
80    use url::Url;
81
82    use crate::{
83        core::BlobKey,
84        payload_disperser::{PayloadDisperser, PayloadDisperserConfig},
85        relay_client::RelayClient,
86        relay_payload_retriever::{RelayPayloadRetriever, RelayPayloadRetrieverConfig, SRSConfig},
87        utils::SecretUrl,
88    };
89
90    use rust_eigenda_signers::signers::private_key::Signer as PrivateKeySigner;
91
92    const TEST_BLOB_FINALIZATION_TIMEOUT: u64 = 180;
93    const TEST_PAYLOAD_DATA: &[u8] = &[1, 2, 3, 4, 5];
94    pub const HOLESKY_ETH_RPC_URL: &str = "https://ethereum-holesky-rpc.publicnode.com";
95    pub const HOLESKY_DISPERSER_RPC_URL: &str = "https://disperser-testnet-holesky.eigenda.xyz";
96    pub const HOLESKY_RELAY_REGISTRY_ADDRESS: &str = "0xac8c6c7ee7572975454e2f0b5c720f9e74989254";
97    pub const CERT_VERIFIER_ROUTER_ADDRESS: &str = "0xdd735affe77a5ed5b21ed47219f95ed841f8ffbd";
98    pub const REGISTRY_COORDINATOR_ADDRESS: &str = "0x53012C69A189cfA2D9d29eb6F19B32e0A2EA3490";
99    pub const OPERATOR_STATE_RETRIEVER_ADDRESS: &str = "0xB4baAfee917fb4449f5ec64804217bccE9f46C67";
100
101    pub fn get_test_private_key_signer() -> PrivateKeySigner {
102        dotenv().ok();
103        let private_key = env::var("SIGNER_PRIVATE_KEY")
104            .expect("SIGNER_PRIVATE_KEY must be set")
105            .parse()
106            .expect("valid secret key");
107        PrivateKeySigner::new(private_key)
108    }
109
110    fn get_test_payload_disperser_config() -> PayloadDisperserConfig {
111        PayloadDisperserConfig {
112            polynomial_form: PayloadForm::Coeff,
113            blob_version: 0,
114            cert_verifier_router_address: CERT_VERIFIER_ROUTER_ADDRESS.to_string(),
115            eth_rpc_url: get_test_holesky_rpc_url(),
116            disperser_rpc: HOLESKY_DISPERSER_RPC_URL.to_string(),
117            use_secure_grpc_flag: false,
118            registry_coordinator_addr: REGISTRY_COORDINATOR_ADDRESS.to_string(),
119            operator_state_retriever_addr: OPERATOR_STATE_RETRIEVER_ADDRESS.to_string(),
120        }
121    }
122
123    pub fn get_relay_payload_retriever_test_config() -> RelayPayloadRetrieverConfig {
124        RelayPayloadRetrieverConfig {
125            payload_form: PayloadForm::Coeff,
126            retrieval_timeout_secs: Duration::from_secs(10),
127        }
128    }
129
130    pub fn get_srs_test_config() -> SRSConfig {
131        SRSConfig {
132            source_path: "../../resources/g1.point".to_string(),
133            order: 9999999,
134            points_to_load: 9999999,
135        }
136    }
137
138    pub fn get_relay_client_test_config() -> crate::relay_client::RelayClientConfig {
139        crate::relay_client::RelayClientConfig {
140            max_grpc_message_size: 9999999,
141            relay_clients_keys: vec![0, 1, 2],
142            relay_registry_address: Address::from_str(HOLESKY_RELAY_REGISTRY_ADDRESS).unwrap(),
143            eth_rpc_url: get_test_holesky_rpc_url(),
144        }
145    }
146
147    pub fn get_test_holesky_rpc_url() -> SecretUrl {
148        SecretUrl::new(Url::from_str(HOLESKY_ETH_RPC_URL).unwrap())
149    }
150
151    pub async fn get_test_relay_client() -> RelayClient {
152        RelayClient::new(get_relay_client_test_config())
153            .await
154            .unwrap()
155    }
156
157    async fn wait_for_blob_finalization_and_verification(
158        payload_disperser: &PayloadDisperser,
159        blob_key: &BlobKey,
160    ) -> EigenDACert {
161        let timeout = tokio::time::Duration::from_secs(TEST_BLOB_FINALIZATION_TIMEOUT);
162
163        let start_time = tokio::time::Instant::now();
164        loop {
165            let cert = payload_disperser.get_cert(blob_key).await.unwrap();
166            match cert {
167                Some(cert) => {
168                    return cert;
169                }
170                None => {
171                    let elapsed = start_time.elapsed();
172                    assert!(elapsed < timeout, "Timeout waiting for certificate");
173                    tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
174                }
175            }
176        }
177    }
178
179    #[ignore = "depends on external RPC"]
180    #[tokio::test]
181    async fn test_disperse_and_retrieve_blob() {
182        let payload_data = TEST_PAYLOAD_DATA.to_vec();
183        let payload = Payload::new(payload_data.clone());
184
185        // First we disperse a blob using a Payload Disperser
186        let payload_disperser = PayloadDisperser::new(
187            get_test_payload_disperser_config(),
188            get_test_private_key_signer(),
189        )
190        .await
191        .unwrap();
192        let blob_key = payload_disperser.send_payload(payload).await.unwrap();
193
194        // Then we wait for the blob to be finalized and verified
195        let eigenda_cert =
196            wait_for_blob_finalization_and_verification(&payload_disperser, &blob_key).await;
197
198        // Finally we retrieve the blob using a Relay Payload Retriever
199        let relay_config = get_relay_payload_retriever_test_config();
200        let srs_config = get_srs_test_config();
201        let relay_client = get_test_relay_client().await;
202        let mut client =
203            RelayPayloadRetriever::new(relay_config, srs_config, relay_client).unwrap();
204
205        let result = client.get_payload(eigenda_cert).await;
206        let retrieved_payload = result.unwrap().serialize();
207        assert_eq!(payload_data, retrieved_payload);
208    }
209}