blueprint_eigenlayer_testing_utils/
harness.rs1use crate::Error;
2use crate::env::{EigenlayerTestEnvironment, setup_eigenlayer_test_environment};
3use alloy_primitives::Address;
4use alloy_provider::RootProvider;
5use blueprint_auth::db::RocksDb;
6use blueprint_chain_setup::anvil::keys::{ANVIL_PRIVATE_KEYS, inject_anvil_key};
7use blueprint_chain_setup::anvil::{Container, start_empty_anvil_testnet};
8use blueprint_core::{error, info};
9use blueprint_evm_extra::util::get_provider_http;
10use blueprint_manager_bridge::server::{Bridge, BridgeHandle};
11use blueprint_runner::config::{BlueprintEnvironment, ContextConfig, SupportedChains};
12use blueprint_runner::eigenlayer::config::EigenlayerProtocolSettings;
13use std::future::Future;
14use std::marker::PhantomData;
15use std::net::Ipv4Addr;
16use std::path::PathBuf;
17use tempfile::TempDir;
18use tokio::task::JoinHandle;
19use url::Url;
20
21#[derive(Default)]
23pub struct EigenlayerTestConfig {
24 pub http_endpoint: Option<Url>,
25 pub ws_endpoint: Option<Url>,
26 pub eigenlayer_contract_addresses: Option<EigenlayerProtocolSettings>,
27}
28
29pub struct EigenlayerTestHarness<Ctx> {
31 env: BlueprintEnvironment,
32 config: EigenlayerTestConfig,
33 pub http_endpoint: Url,
34 pub ws_endpoint: Url,
35 pub accounts: Vec<Address>,
36 pub eigenlayer_contract_addresses: EigenlayerProtocolSettings,
37 _temp_dir: TempDir,
38 _container: Container,
39 _phantom: PhantomData<Ctx>,
40 _auth_proxy: JoinHandle<Result<(), Error>>,
41 _bridge: BridgeHandle,
42}
43
44impl EigenlayerTestHarness<()> {
45 pub async fn setup(test_dir: TempDir) -> Result<Self, Error> {
54 Self::setup_with_context(test_dir, ()).await
55 }
56}
57
58impl<Ctx> EigenlayerTestHarness<Ctx>
59where
60 Ctx: Clone + Send + Sync + 'static,
61{
62 pub async fn setup_with_context(test_dir: TempDir, _context: Ctx) -> Result<Self, Error> {
70 let testnet = start_empty_anvil_testnet(true).await;
72
73 let EigenlayerTestEnvironment {
75 accounts,
76 http_endpoint,
77 ws_endpoint,
78 eigenlayer_contract_addresses,
79 } = setup_eigenlayer_test_environment(testnet.http_endpoint, testnet.ws_endpoint).await;
80
81 let keystore_path = test_dir.path().join("keystore");
83 inject_anvil_key(&keystore_path, ANVIL_PRIVATE_KEYS[0])?;
84
85 let data_dir = test_dir.path().join("data");
86 tokio::fs::create_dir_all(&data_dir).await?;
87
88 const DEFAULT_AUTH_PROXY_PORT: u16 = 50051;
90 let (auth_proxy_db, auth_proxy_task) =
91 run_auth_proxy(test_dir.path().to_path_buf(), DEFAULT_AUTH_PROXY_PORT).await?;
92
93 let auth_proxy = tokio::spawn(auth_proxy_task);
94
95 let runtime_dir = test_dir.path().join("runtime");
97 tokio::fs::create_dir_all(&runtime_dir).await?;
98
99 let bridge = Bridge::new(runtime_dir, String::from("service"), auth_proxy_db, true);
100 let bridge_socket_path = bridge.base_socket_path();
101
102 let (bridge_handle, _alive_rx) = bridge.spawn()?;
103
104 let context_config = ContextConfig::create_eigenlayer_config(
106 Url::parse(&http_endpoint)?,
107 Url::parse(&ws_endpoint)?,
108 keystore_path.to_string_lossy().into_owned(),
109 None,
110 data_dir,
111 None,
112 SupportedChains::LocalTestnet,
113 eigenlayer_contract_addresses,
114 );
115
116 let mut env = BlueprintEnvironment::load_with_config(context_config)
118 .map_err(|e| Error::Setup(e.to_string()))?;
119
120 env.bridge_socket_path = Some(bridge_socket_path);
121 env.test_mode = true;
122
123 let config = EigenlayerTestConfig {
125 http_endpoint: Some(Url::parse(&http_endpoint)?),
126 ws_endpoint: Some(Url::parse(&ws_endpoint)?),
127 eigenlayer_contract_addresses: Some(eigenlayer_contract_addresses),
128 };
129
130 Ok(Self {
131 env,
132 config,
133 http_endpoint: Url::parse(&http_endpoint)?,
134 ws_endpoint: Url::parse(&ws_endpoint)?,
135 accounts,
136 eigenlayer_contract_addresses,
137 _temp_dir: test_dir,
138 _container: testnet.container,
139 _phantom: core::marker::PhantomData,
140 _auth_proxy: auth_proxy,
141 _bridge: bridge_handle,
142 })
143 }
144
145 #[must_use]
146 #[allow(clippy::used_underscore_binding)]
147 pub fn set_context<Ctx2: Clone + Send + Sync + 'static>(
148 self,
149 _context: Ctx2,
150 ) -> EigenlayerTestHarness<Ctx2> {
151 EigenlayerTestHarness {
152 env: self.env,
153 config: self.config,
154 http_endpoint: self.http_endpoint,
155 ws_endpoint: self.ws_endpoint,
156 accounts: self.accounts,
157 eigenlayer_contract_addresses: self.eigenlayer_contract_addresses,
158 _temp_dir: self._temp_dir,
159 _container: self._container,
160 _phantom: PhantomData::<Ctx2>,
161 _auth_proxy: self._auth_proxy,
162 _bridge: self._bridge,
163 }
164 }
165
166 #[must_use]
167 pub fn env(&self) -> &BlueprintEnvironment {
168 &self.env
169 }
170}
171
172impl<Ctx> EigenlayerTestHarness<Ctx> {
173 #[must_use]
175 pub fn provider(&self) -> RootProvider {
176 get_provider_http(self.http_endpoint.as_str())
177 }
178
179 #[must_use]
181 pub fn accounts(&self) -> &[Address] {
182 &self.accounts
183 }
184
185 #[must_use]
187 pub fn owner_account(&self) -> Address {
188 self.accounts[0]
189 }
190
191 #[must_use]
193 pub fn aggregator_account(&self) -> Address {
194 self.accounts[9]
195 }
196
197 #[must_use]
199 pub fn task_generator_account(&self) -> Address {
200 self.accounts[4]
201 }
202}
203
204async fn run_auth_proxy(
221 data_dir: PathBuf,
222 auth_proxy_port: u16,
223) -> Result<(RocksDb, impl Future<Output = Result<(), Error>>), Error> {
224 let db_path = data_dir.join("private").join("auth-proxy").join("db");
225 tokio::fs::create_dir_all(&db_path).await?;
226
227 let proxy = blueprint_auth::proxy::AuthenticatedProxy::new(&db_path)?;
228 let db = proxy.db();
229
230 let task = async move {
231 let router = proxy.router();
232 let listener =
233 tokio::net::TcpListener::bind((Ipv4Addr::LOCALHOST, auth_proxy_port)).await?;
234 info!(
235 "Auth proxy listening on {}:{}",
236 Ipv4Addr::LOCALHOST,
237 auth_proxy_port
238 );
239 let result = axum::serve(listener, router).await;
240 if let Err(err) = result {
241 error!("Auth proxy error: {err}");
242 }
243
244 Ok(())
245 };
246
247 Ok((db, task))
248}