cargo_tangle/command/run/
tangle.rs

1use alloy_signer_local::PrivateKeySigner;
2use blueprint_crypto::tangle_pair_signer::TanglePairSigner;
3use blueprint_manager::config::BlueprintManagerConfig;
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;
13
14#[derive(Clone)]
15pub struct RunOpts {
16    /// The HTTP RPC URL of the Tangle Network
17    pub http_rpc_url: String,
18    /// The WS RPC URL of the Tangle Network
19    pub ws_rpc_url: String,
20    /// The signer for Tangle operations
21    pub signer: Option<TanglePairSigner<sr25519::Pair>>,
22    /// The signer for EVM operations
23    pub signer_evm: Option<PrivateKeySigner>,
24    /// The blueprint ID to run
25    pub blueprint_id: Option<u64>,
26    /// The keystore path
27    pub keystore_path: Option<String>,
28    /// The data directory path
29    pub data_dir: Option<PathBuf>,
30}
31
32/// Runs a blueprint using the blueprint manager
33///
34/// # Arguments
35///
36/// * `opts` - Options for running the blueprint
37///
38/// # Errors
39///
40/// Returns an error if:
41/// * Blueprint ID is not provided
42/// * Failed to create or configure the blueprint manager
43/// * Failed to run the blueprint
44#[allow(clippy::missing_panics_doc)]
45pub async fn run_blueprint(opts: RunOpts) -> Result<()> {
46    let blueprint_id = opts
47        .blueprint_id
48        .ok_or_else(|| eyre!("Blueprint ID is required"))?;
49
50    let mut gadget_config = BlueprintEnvironment::default();
51    gadget_config.http_rpc_endpoint = opts.http_rpc_url.clone();
52    gadget_config.ws_rpc_endpoint = opts.ws_rpc_url.clone();
53
54    if let Some(keystore_path) = opts.keystore_path {
55        gadget_config.keystore_uri = keystore_path;
56    }
57
58    gadget_config.data_dir = opts.data_dir;
59
60    let blueprint_manager_config = BlueprintManagerConfig {
61        gadget_config: None,
62        keystore_uri: gadget_config.keystore_uri.clone(),
63        data_dir: gadget_config
64            .data_dir
65            .clone()
66            .unwrap_or_else(|| PathBuf::from("./data")),
67        verbose: 2,
68        pretty: true,
69        instance_id: Some(format!("Blueprint-{}", blueprint_id)),
70        test_mode: false,
71    };
72
73    println!(
74        "{}",
75        style(format!(
76            "Starting blueprint manager for blueprint ID: {}",
77            blueprint_id
78        ))
79        .cyan()
80        .bold()
81    );
82
83    let shutdown_signal = async move {
84        let _ = signal::ctrl_c().await;
85        println!(
86            "{}",
87            style("Received shutdown signal, stopping blueprint manager")
88                .yellow()
89                .bold()
90        );
91    };
92
93    println!(
94        "{}",
95        style("Preparing Blueprint to run, this may take a few minutes...").cyan()
96    );
97
98    let pb = ProgressBar::new_spinner();
99    pb.set_style(
100        ProgressStyle::default_spinner()
101            .tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈ ")
102            .template("{spinner:.blue} {msg}")
103            .unwrap(),
104    );
105    pb.set_message("Initializing Blueprint");
106    pb.enable_steady_tick(Duration::from_millis(100));
107
108    let mut handle =
109        run_blueprint_manager(blueprint_manager_config, gadget_config, shutdown_signal).await?;
110
111    pb.finish_with_message("Blueprint initialized successfully!");
112
113    println!(
114        "{}",
115        style("Starting blueprint execution...").green().bold()
116    );
117    handle.start()?;
118
119    println!(
120        "{}",
121        style("Blueprint is running. Press Ctrl+C to stop.").cyan()
122    );
123    handle.await?;
124
125    println!("{}", style("Blueprint manager has stopped").green());
126    Ok(())
127}