cargo_tangle/command/run/
tangle.rs

1use 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    /// The HTTP RPC URL of the Tangle Network
18    pub http_rpc_url: String,
19    /// The WS RPC URL of the Tangle Network
20    pub ws_rpc_url: String,
21    /// The signer for Tangle operations
22    pub signer: Option<TanglePairSigner<sr25519::Pair>>,
23    /// The signer for EVM operations
24    pub signer_evm: Option<PrivateKeySigner>,
25    /// The blueprint ID to run
26    pub blueprint_id: Option<u64>,
27    /// The keystore path
28    pub keystore_path: Option<String>,
29    /// The data directory path
30    pub data_dir: Option<PathBuf>,
31    /// Whether to allow invalid GitHub attestations (binary integrity checks)
32    ///
33    /// This will also allow for running the manager without the GitHub CLI installed.
34    pub allow_unchecked_attestations: bool,
35    /// The Podman host to use for containerized blueprints
36    pub podman_host: Option<Url>,
37}
38
39/// Runs a blueprint using the blueprint manager
40///
41/// # Arguments
42///
43/// * `opts` - Options for running the blueprint
44///
45/// # Errors
46///
47/// Returns an error if:
48/// * Blueprint ID is not provided
49/// * Failed to create or configure the blueprint manager
50/// * Failed to run the blueprint
51#[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}