cargo_tangle/command/run/
tangle.rs1use alloy_signer_local::PrivateKeySigner;
2use blueprint_crypto::tangle_pair_signer::TanglePairSigner;
3use blueprint_manager::config::{BlueprintManagerConfig, DEFAULT_DOCKER_HOST};
4use blueprint_manager::executor::run_blueprint_manager;
5use blueprint_runner::config::BlueprintEnvironment;
6use color_eyre::eyre::{Result, eyre};
7use dialoguer::console::style;
8use indicatif::{ProgressBar, ProgressStyle};
9use sp_core::sr25519;
10use std::path::PathBuf;
11use std::time::Duration;
12use tokio::signal;
13use url::Url;
14
15#[derive(Clone)]
16pub struct RunOpts {
17 pub http_rpc_url: String,
19 pub ws_rpc_url: String,
21 pub signer: Option<TanglePairSigner<sr25519::Pair>>,
23 pub signer_evm: Option<PrivateKeySigner>,
25 pub blueprint_id: Option<u64>,
27 pub keystore_path: Option<String>,
29 pub data_dir: Option<PathBuf>,
31 pub allow_unchecked_attestations: bool,
35 pub podman_host: Option<Url>,
37}
38
39#[allow(clippy::missing_panics_doc)]
52pub async fn run_blueprint(opts: RunOpts) -> Result<()> {
53 let blueprint_id = opts
54 .blueprint_id
55 .ok_or_else(|| eyre!("Blueprint ID is required"))?;
56
57 let mut blueprint_config = BlueprintEnvironment::default();
58 blueprint_config.http_rpc_endpoint = opts.http_rpc_url.clone();
59 blueprint_config.ws_rpc_endpoint = opts.ws_rpc_url.clone();
60
61 if let Some(keystore_path) = opts.keystore_path {
62 blueprint_config.keystore_uri = keystore_path;
63 }
64
65 blueprint_config.keystore_uri = std::path::absolute(&blueprint_config.keystore_uri)?
66 .display()
67 .to_string();
68
69 blueprint_config.data_dir = opts.data_dir;
70
71 let blueprint_manager_config = BlueprintManagerConfig {
72 keystore_uri: blueprint_config.keystore_uri.clone(),
73 data_dir: blueprint_config
74 .data_dir
75 .clone()
76 .unwrap_or_else(|| PathBuf::from("./data")),
77 verbose: 2,
78 pretty: true,
79 instance_id: Some(format!("Blueprint-{}", blueprint_id)),
80 allow_unchecked_attestations: opts.allow_unchecked_attestations,
81 podman_host: opts.podman_host.unwrap_or(DEFAULT_DOCKER_HOST.clone()),
82 ..Default::default()
83 };
84
85 println!(
86 "{}",
87 style(format!(
88 "Starting blueprint manager for blueprint ID: {}",
89 blueprint_id
90 ))
91 .cyan()
92 .bold()
93 );
94
95 let shutdown_signal = async move {
96 let _ = signal::ctrl_c().await;
97 println!(
98 "{}",
99 style("Received shutdown signal, stopping blueprint manager")
100 .yellow()
101 .bold()
102 );
103 };
104
105 println!(
106 "{}",
107 style("Preparing Blueprint to run, this may take a few minutes...").cyan()
108 );
109
110 let pb = ProgressBar::new_spinner();
111 pb.set_style(
112 ProgressStyle::default_spinner()
113 .tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈ ")
114 .template("{spinner:.blue} {msg}")
115 .unwrap(),
116 );
117 pb.set_message("Initializing Blueprint");
118 pb.enable_steady_tick(Duration::from_millis(100));
119
120 let mut handle =
121 run_blueprint_manager(blueprint_manager_config, blueprint_config, shutdown_signal).await?;
122
123 pb.finish_with_message("Blueprint initialized successfully!");
124
125 println!(
126 "{}",
127 style("Starting blueprint execution...").green().bold()
128 );
129 handle.start()?;
130
131 println!(
132 "{}",
133 style("Blueprint is running. Press Ctrl+C to stop.").cyan()
134 );
135 handle.await?;
136
137 println!("{}", style("Blueprint manager has stopped").green());
138 Ok(())
139}