cargo_tangle/command/run/
eigenlayer.rs

1use blueprint_runner::config::{BlueprintEnvironment, SupportedChains};
2use blueprint_std::fs;
3use blueprint_std::path::PathBuf;
4use color_eyre::eyre::{Result, eyre};
5use tokio::io::AsyncBufReadExt;
6use tokio::io::BufReader;
7use tokio::process::{Child, Command};
8use toml::Value;
9use tracing::info;
10
11fn get_binary_name() -> Result<String> {
12    let cargo_toml = fs::read_to_string("Cargo.toml")?;
13    let cargo_data: Value = toml::from_str(&cargo_toml)?;
14
15    // First check for [[bin]] section
16    if let Some(Value::Array(bins)) = cargo_data.get("bin") {
17        if let Some(first_bin) = bins.first() {
18            if let Some(name) = first_bin.get("name").and_then(|n| n.as_str()) {
19                return Ok(name.to_string());
20            }
21        }
22    }
23
24    // If no [[bin]] section, try package name
25    if let Some(package) = cargo_data.get("package") {
26        if let Some(name) = package.get("name").and_then(|n| n.as_str()) {
27            return Ok(name.to_string());
28        }
29    }
30
31    Err(eyre!("Could not find binary name in Cargo.toml"))
32}
33
34/// Run a compiled Eigenlayer AVS binary with the provided options
35///
36/// # Errors
37///
38/// * Failed to build the binary (if needed)
39/// * The binary fails to run, for any reason
40#[allow(clippy::missing_panics_doc)]
41pub async fn run_eigenlayer_avs(
42    config: BlueprintEnvironment,
43    chain: SupportedChains,
44    binary_path: Option<PathBuf>,
45) -> Result<Child> {
46    let binary_path = if let Some(path) = binary_path {
47        path
48    } else {
49        let target_dir = PathBuf::from("./target/release");
50        let binary_name = get_binary_name()?;
51        target_dir.join(&binary_name)
52    };
53
54    println!(
55        "Attempting to run Eigenlayer AVS binary at: {}",
56        binary_path.display()
57    );
58
59    // Build only if binary doesn't exist or no path was provided
60    if !binary_path.exists() {
61        info!("Binary not found at {}, building...", binary_path.display());
62        let status = Command::new("cargo")
63            .arg("build")
64            .arg("--release")
65            .status()
66            .await?;
67
68        if !status.success() {
69            return Err(eyre!("Failed to build AVS binary"));
70        }
71    }
72
73    // Get contract addresses
74    let contract_addresses = config
75        .protocol_settings
76        .eigenlayer()
77        .map_err(|_| eyre!("Missing Eigenlayer contract addresses"))?;
78
79    // Run the AVS binary with the provided options
80    println!("Starting AVS...");
81    let mut command = Command::new(&binary_path);
82
83    // Add the run subcommand
84    command.arg("run");
85
86    // Required arguments
87    command
88        .arg("--http-rpc-url")
89        .arg(&config.http_rpc_endpoint)
90        .arg("--ws-rpc-url")
91        .arg(&config.ws_rpc_endpoint)
92        .arg("--keystore-uri")
93        .arg(&config.keystore_uri)
94        .arg("--chain")
95        .arg(chain.to_string())
96        .arg("--protocol")
97        .arg("eigenlayer");
98
99    // Optional arguments
100    // TODO: Implement Keystore Password
101    // if let Some(password) = &config.keystore_password {
102    //     command.arg("--keystore-password").arg(password);
103    // }
104
105    // Contract addresses
106    command
107        .arg("--registry-coordinator")
108        .arg(contract_addresses.registry_coordinator_address.to_string())
109        .arg("--operator-state-retriever")
110        .arg(
111            contract_addresses
112                .operator_state_retriever_address
113                .to_string(),
114        )
115        .arg("--delegation-manager")
116        .arg(contract_addresses.delegation_manager_address.to_string())
117        .arg("--strategy-manager")
118        .arg(contract_addresses.strategy_manager_address.to_string())
119        .arg("--service-manager")
120        .arg(contract_addresses.service_manager_address.to_string())
121        .arg("--stake-registry")
122        .arg(contract_addresses.stake_registry_address.to_string())
123        .arg("--avs-directory")
124        .arg(contract_addresses.avs_directory_address.to_string())
125        .arg("--rewards-coordinator")
126        .arg(contract_addresses.rewards_coordinator_address.to_string())
127        .arg("--allocation-manager")
128        .arg(contract_addresses.allocation_manager_address.to_string())
129        .arg("--permission-controller")
130        .arg(contract_addresses.permission_controller_address.to_string())
131        .arg("--strategy")
132        .arg(contract_addresses.strategy_address.to_string());
133
134    assert!(binary_path.exists(), "Binary path does not exist");
135
136    let mut child = command
137        .stdout(std::process::Stdio::piped())
138        .stderr(std::process::Stdio::piped())
139        .spawn()?;
140
141    // Handle stdout
142    let stdout = child.stdout.take().expect("Failed to capture stdout");
143    let mut stdout_reader = BufReader::new(stdout).lines();
144
145    // Handle stderr
146    let stderr = child.stderr.take().expect("Failed to capture stderr");
147    let mut stderr_reader = BufReader::new(stderr).lines();
148
149    // Spawn tasks to handle stdout and stderr
150    let stdout_task = tokio::spawn(async move {
151        while let Some(line) = stdout_reader
152            .next_line()
153            .await
154            .expect("Failed to read stdout")
155        {
156            println!("{}", line);
157        }
158    });
159
160    let stderr_task = tokio::spawn(async move {
161        while let Some(line) = stderr_reader
162            .next_line()
163            .await
164            .expect("Failed to read stderr")
165        {
166            eprintln!("{}", line);
167        }
168    });
169
170    // Wait for both tasks to complete
171    let _ = tokio::join!(stdout_task, stderr_task);
172
173    println!(
174        "AVS is running with PID: {}",
175        child.id().unwrap_or_default()
176    );
177    Ok(child)
178}