use crate::Error;
use crate::env::setup_eigenlayer_test_environment;
use alloy_primitives::address;
use alloy_primitives::{Address, U256};
use alloy_provider::Provider;
use blueprint_auth::db::RocksDb;
use blueprint_chain_setup::anvil::keys::inject_anvil_key;
use blueprint_chain_setup::anvil::{AnvilTestnet, Container};
use blueprint_core::{error, info};
use blueprint_evm_extra::util::get_provider_http;
use blueprint_manager_bridge::server::{Bridge, BridgeHandle};
use blueprint_runner::config::{BlueprintEnvironment, ContextConfig, SupportedChains};
use blueprint_runner::eigenlayer::config::EigenlayerProtocolSettings;
use eigenlayer_contract_deployer::core::{
DelegationManagerConfig, DeployedCoreContracts, DeploymentConfigData, EigenPodManagerConfig,
RewardsCoordinatorConfig, StrategyFactoryConfig, StrategyManagerConfig,
};
use std::future::Future;
use std::net::Ipv4Addr;
use std::path::PathBuf;
use tempfile::TempDir;
use tokio::task::JoinHandle;
use url::Url;
pub struct EigenlayerTestHarness {
env: BlueprintEnvironment,
_temp_dir: TempDir,
_container: Container,
_auth_proxy: JoinHandle<Result<(), Error>>,
_bridge: BridgeHandle,
}
impl EigenlayerTestHarness {
pub async fn setup(
owner_private_key: &str,
test_dir: TempDir,
testnet: AnvilTestnet,
eigenlayer_protocol_settings: Option<EigenlayerProtocolSettings>,
) -> Result<Self, Error> {
Self::setup_with_context(
owner_private_key,
test_dir,
testnet,
eigenlayer_protocol_settings,
)
.await
}
}
impl EigenlayerTestHarness {
pub async fn setup_with_context(
owner_private_key: &str,
test_dir: TempDir,
testnet: AnvilTestnet,
eigenlayer_protocol_settings: Option<EigenlayerProtocolSettings>,
) -> Result<Self, Error> {
let keystore_path = test_dir.path().join("keystore");
inject_anvil_key(&keystore_path, owner_private_key)?;
let eigenlayer_protocol_settings = match eigenlayer_protocol_settings {
Some(settings) => settings,
None => setup_eigenlayer_test_environment(testnet.http_endpoint.clone()).await,
};
let data_dir = test_dir.path().join("data");
tokio::fs::create_dir_all(&data_dir).await?;
const DEFAULT_AUTH_PROXY_PORT: u16 = 50051;
let (auth_proxy_db, auth_proxy_task) =
run_auth_proxy(test_dir.path().to_path_buf(), DEFAULT_AUTH_PROXY_PORT).await?;
let auth_proxy = tokio::spawn(auth_proxy_task);
let runtime_dir = test_dir.path().join("runtime");
tokio::fs::create_dir_all(&runtime_dir).await?;
let bridge = Bridge::new(
runtime_dir,
String::from("service"),
auth_proxy_db,
true,
None,
);
let bridge_socket_path = bridge.base_socket_path();
let (bridge_handle, _alive_rx) = bridge.spawn()?;
let context_config = ContextConfig::create_eigenlayer_config(
testnet.http_endpoint.clone(),
testnet.ws_endpoint.clone(),
keystore_path.to_string_lossy().into_owned(),
None,
data_dir,
None,
SupportedChains::LocalTestnet,
eigenlayer_protocol_settings,
);
let mut env = BlueprintEnvironment::load_with_config(context_config)
.map_err(|e| Error::Setup(e.to_string()))?;
env.bridge_socket_path = Some(bridge_socket_path);
env.test_mode = true;
Ok(Self {
env,
_temp_dir: test_dir,
_container: testnet.container,
_auth_proxy: auth_proxy,
_bridge: bridge_handle,
})
}
#[must_use]
pub fn env(&self) -> &BlueprintEnvironment {
&self.env
}
}
#[must_use]
pub async fn get_accounts(http_endpoint: Url) -> Vec<Address> {
let provider = get_provider_http(http_endpoint.clone());
provider.get_accounts().await.unwrap()
}
#[must_use]
pub fn get_owner_account(accounts: &[Address]) -> Address {
accounts[0]
}
#[must_use]
pub fn get_aggregator_account(accounts: &[Address]) -> Address {
accounts[9]
}
#[must_use]
pub fn get_task_generator_account(accounts: &[Address]) -> Address {
accounts[4]
}
async fn run_auth_proxy(
data_dir: PathBuf,
auth_proxy_port: u16,
) -> Result<(RocksDb, impl Future<Output = Result<(), Error>>), Error> {
let db_path = data_dir.join("private").join("auth-proxy").join("db");
tokio::fs::create_dir_all(&db_path).await?;
let proxy = blueprint_auth::proxy::AuthenticatedProxy::new(&db_path)?;
let db = proxy.db();
let task = async move {
let router = proxy.router();
let listener =
tokio::net::TcpListener::bind((Ipv4Addr::LOCALHOST, auth_proxy_port)).await?;
info!(
"Auth proxy listening on {}:{}",
Ipv4Addr::LOCALHOST,
auth_proxy_port
);
let result = axum::serve(listener, router).await;
if let Err(err) = result {
error!("Auth proxy error: {err}");
}
Ok(())
};
Ok((db, task))
}
pub async fn deploy_eigenlayer_core_contracts(
http_endpoint: &str,
owner_private_key: &str,
owner_account: Address,
) -> Result<DeployedCoreContracts, Error> {
let core_config = DeploymentConfigData {
strategy_manager: StrategyManagerConfig {
init_paused_status: U256::from(0),
init_withdrawal_delay_blocks: 1u32,
},
delegation_manager: DelegationManagerConfig {
init_paused_status: U256::from(0),
withdrawal_delay_blocks: 0u32,
},
eigen_pod_manager: EigenPodManagerConfig {
init_paused_status: U256::from(0),
},
rewards_coordinator: RewardsCoordinatorConfig {
init_paused_status: U256::from(0),
max_rewards_duration: 864_000u32,
max_retroactive_length: 432_000u32,
max_future_length: 86_400u32,
genesis_rewards_timestamp: 1_672_531_200_u32,
updater: owner_account,
activation_delay: 0u32,
calculation_interval_seconds: 86_400u32,
global_operator_commission_bips: 1_000u16,
},
strategy_factory: StrategyFactoryConfig {
init_paused_status: U256::from(0),
},
};
Ok(eigenlayer_contract_deployer::core::deploy_core_contracts(
http_endpoint,
owner_private_key,
owner_account,
core_config,
Some(address!("00000000219ab540356cBB839Cbe05303d7705Fa")),
Some(1_564_000),
)
.await
.unwrap())
}