cargo-tangle 0.5.0-alpha.13

A command-line tool to create and deploy blueprints on Tangle Network
use alloy_primitives::Address;
use blueprint_manager::config::{
    BlueprintManagerConfig, BlueprintManagerContext, Paths, SourceType,
};
use blueprint_manager::executor::run_blueprint_manager;
use blueprint_runner::config::{BlueprintEnvironment, ProtocolSettings};
use blueprint_runner::tangle::config::TangleProtocolSettings;
use color_eyre::eyre::Result;
use dialoguer::console::style;
use indicatif::{ProgressBar, ProgressStyle};
use std::path::PathBuf;
use std::time::Duration;
use tokio::signal;
use url::Url;

#[derive(Clone)]
pub struct RunOpts {
    pub http_rpc_url: Url,
    pub ws_rpc_url: Url,
    pub blueprint_id: u64,
    pub service_id: Option<u64>,
    pub tangle_contract: Address,
    pub staking_contract: Address,
    pub status_registry_contract: Address,
    pub keystore_path: String,
    pub data_dir: Option<PathBuf>,
    pub allow_unchecked_attestations: bool,
    pub registration_mode: bool,
    pub registration_capture_only: bool,
    pub preferred_source: SourceType,
    pub use_vm: bool,
    pub dry_run: bool,
    pub shutdown_after: Option<Duration>,
}

pub async fn run_blueprint(opts: RunOpts) -> Result<()> {
    let mut blueprint_config = BlueprintEnvironment::default();
    blueprint_config.http_rpc_endpoint = opts.http_rpc_url;
    blueprint_config.ws_rpc_endpoint = opts.ws_rpc_url;
    blueprint_config.keystore_uri = opts.keystore_path;
    blueprint_config.data_dir = opts.data_dir.unwrap_or_else(|| PathBuf::from("./data"));
    blueprint_config.registration_mode = opts.registration_mode;
    blueprint_config.registration_capture_only = opts.registration_capture_only;
    blueprint_config.dry_run = opts.dry_run;
    blueprint_config.protocol_settings = ProtocolSettings::Tangle(TangleProtocolSettings {
        blueprint_id: opts.blueprint_id,
        service_id: opts.service_id,
        tangle_contract: opts.tangle_contract,
        staking_contract: opts.staking_contract,
        status_registry_contract: opts.status_registry_contract,
    });

    #[allow(unused_mut)]
    let mut blueprint_manager_config = BlueprintManagerConfig {
        paths: Paths {
            keystore_uri: blueprint_config.keystore_uri.clone(),
            data_dir: blueprint_config.data_dir.clone(),
            ..Default::default()
        },
        verbose: 2,
        pretty: true,
        instance_id: Some(format!("Blueprint-{}", opts.blueprint_id)),
        allow_unchecked_attestations: opts.allow_unchecked_attestations,
        preferred_source: opts.preferred_source,
        ..Default::default()
    };

    #[cfg(feature = "vm-debug")]
    {
        blueprint_manager_config.vm_sandbox_options.no_vm = !opts.use_vm;
    }

    let ctx = BlueprintManagerContext::new(blueprint_manager_config).await?;

    println!(
        "{}",
        style(format!(
            "Starting blueprint manager for blueprint ID: {}",
            opts.blueprint_id
        ))
        .cyan()
        .bold()
    );

    let shutdown_after = opts.shutdown_after;
    let shutdown_signal = async move {
        if let Some(duration) = shutdown_after {
            tokio::select! {
                _ = signal::ctrl_c() => {
                    println!(
                        "{}",
                        style("Received shutdown signal, stopping blueprint manager")
                            .yellow()
                            .bold()
                    );
                }
                _ = tokio::time::sleep(duration) => {
                    println!(
                        "{}",
                        style("Auto-shutdown window elapsed, stopping blueprint manager")
                            .yellow()
                            .bold()
                    );
                }
            }
        } else {
            let _ = signal::ctrl_c().await;
            println!(
                "{}",
                style("Received shutdown signal, stopping blueprint manager")
                    .yellow()
                    .bold()
            );
        }
    };

    println!(
        "{}",
        style("Preparing Blueprint to run, this may take a few minutes...").cyan()
    );

    let pb = ProgressBar::new_spinner();
    pb.set_style(
        ProgressStyle::default_spinner()
            .tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈ ")
            .template("{spinner:.blue} {msg}")
            .unwrap(),
    );
    pb.set_message("Initializing Blueprint");
    pb.enable_steady_tick(Duration::from_millis(100));

    let mut handle = run_blueprint_manager(ctx, blueprint_config, shutdown_signal).await?;

    pb.finish_with_message("Blueprint initialized successfully!");

    println!(
        "{}",
        style("Starting blueprint execution...").green().bold()
    );
    handle.start()?;

    println!(
        "{}",
        style("Blueprint is running. Press Ctrl+C to stop.").cyan()
    );
    handle.await?;

    println!("{}", style("Blueprint manager has stopped").green());
    Ok(())
}