warriors/trisha/cli/deploy.rs

use std::path::PathBuf;
use std::process;

use clap::{Args, Subcommand};

use trident::runtime::Deployer;
use trisha_rs::Warrior;

use crate::batch;
use crate::compile::compile_source;
use crate::error::TrishaError;
use crate::NetworkArgs;

use super::load_proof_data;

#[derive(Args)]
#[command(args_conflicts_with_subcommands = true)]
pub struct DeployArgs {
    #[command(subcommand)]
    pub mode: Option<DeployMode>,

    pub input: Option<PathBuf>,
    #[arg(long, default_value = "triton")]
    pub vm: String,
    #[arg(long, default_value = "release")]
    pub profile: String,
    #[arg(long)]
    pub proof: Option<PathBuf>,
    #[arg(long)]
    pub dry_run: bool,
    #[command(flatten)]
    pub network: NetworkArgs,
}

#[derive(Subcommand)]
pub enum DeployMode {
    Batch(DeployBatchArgs),
}

#[derive(Args)]
pub struct DeployBatchArgs {
    pub inputs: Vec<PathBuf>,
    #[arg(long, default_value = "triton")]
    pub vm: String,
    #[arg(long, default_value = "release")]
    pub profile: String,
    #[arg(long)]
    pub dry_run: bool,
    #[arg(long, default_value = "4")]
    pub max_parallel: usize,
    #[command(flatten)]
    pub network: NetworkArgs,
}

pub fn cmd_deploy(args: DeployArgs) {
    let (state, _rpc_port) = args.network.resolve().unwrap_or_else(|e| {
        eprintln!("error: {}", e);
        process::exit(1);
    });
    match args.mode {
        Some(DeployMode::Batch(batch_args)) => cmd_deploy_batch(batch_args),
        None => {
            let input = match args.input {
                Some(p) => p,
                None => {
                    eprintln!("error: no input file specified");
                    process::exit(1);
                }
            };
            cmd_deploy_single(
                input,
                &args.vm,
                state.union,
                state.name,
                &args.profile,
                args.proof,
                args.dry_run,
            );
        }
    }
}

fn cmd_deploy_single(
    input: PathBuf,
    vm: &str,
    union: &str,
    state: &str,
    profile: &str,
    proof_path: Option<PathBuf>,
    dry_run: bool,
) {
    let bundle = match compile_source(&input, vm, profile) {
        Ok(b) => b,
        Err(e) => {
            eprintln!("error: {}", e);
            process::exit(1);
        }
    };
    let proof_data = if let Some(ref pp) = proof_path {
        match load_proof_data(pp) {
            Ok(p) => Some(p),
            Err(e) => {
                eprintln!("error: cannot load proof: {}", e);
                process::exit(1);
            }
        }
    } else {
        None
    };
    let digest = trident::poseidon2::hash_bytes(bundle.assembly.as_bytes());
    let digest_hex = trident::hash::ContentHash(digest).to_hex();
    if dry_run {
        eprintln!("Dry run โ€” would deploy:");
        eprintln!("  Program : {}", bundle.name);
        eprintln!("  Union   : {}", union);
        eprintln!("  State   : {}", state);
        eprintln!("  Digest  : {}", digest_hex);
        eprintln!(
            "  Proof   : {}",
            if proof_path.is_some() { "attached" } else { "none" }
        );
        return;
    }
    let warrior = Warrior::new();
    match warrior.deploy(&bundle, proof_data.as_ref()) {
        Ok(result) => println!("{}", result),
        Err(e) => {
            eprintln!("error: {}", e);
            process::exit(1);
        }
    }
}

fn cmd_deploy_batch(args: DeployBatchArgs) {
    if args.inputs.is_empty() {
        eprintln!("error: no input files specified");
        process::exit(1);
    }
    let (state, _rpc_port) = args.network.resolve().unwrap_or_else(|e| {
        eprintln!("error: {}", e);
        process::exit(1);
    });
    let vm = args.vm.clone();
    let profile = args.profile.clone();
    let dry_run = args.dry_run;
    let union = state.union;
    let state_name = state.name;
    let count = args.inputs.len();
    eprintln!(
        "Deploying {} programs to {}/{} (max {} parallel)...",
        count, union, state_name, args.max_parallel
    );
    let results = batch::run_batch(args.inputs, args.max_parallel, |path| {
        let bundle = compile_source(&path, &vm, &profile)?;
        let digest = trident::poseidon2::hash_bytes(bundle.assembly.as_bytes());
        let digest_hex = trident::hash::ContentHash(digest).to_hex();
        if dry_run {
            return Ok::<String, TrishaError>(format!("dry-run: {} ({})", bundle.name, digest_hex));
        }
        let warrior = Warrior::new();
        warrior.deploy(&bundle, None).map_err(TrishaError::Deploy)
    });
    let mut failures = 0;
    for r in &results {
        match &r.result {
            Ok(msg) => eprintln!("  [{}] {} ({} ms)", r.index, msg, r.elapsed_ms),
            Err(e) => {
                eprintln!("  [{}] FAIL: {}", r.index, e);
                failures += 1;
            }
        }
    }
    eprintln!("{}/{} deployed", count - failures, count);
    if failures > 0 {
        process::exit(1);
    }
}

Homonyms

neural/trident/src/cli/deploy.rs

Graph