triton-cli 3.0.0

Command Line Interface to run, prove, and verify programs written for Triton VM.
use std::process::ExitCode;

use anyhow::Result;
use clap::Parser;
use itertools::Itertools;
use triton_vm::prelude::Claim;
use triton_vm::prelude::Stark;
use triton_vm::prelude::VM;

use crate::args::Args;
use crate::args::Command;
use crate::args::Flags;
use crate::args::ProofArtifacts;
use crate::args::RunArgs;

const SUCCESS: ExitCode = ExitCode::SUCCESS;
const FAILURE: ExitCode = ExitCode::FAILURE;

mod args;

fn main() -> Result<ExitCode> {
    human_panic::setup_panic!();

    let Args { flags, command } = Args::parse();
    match command {
        Command::Run(args) => run(flags, args),
        Command::Prove { args, artifacts } => prove(flags, args, artifacts),
        Command::Verify(artifacts) => verify(flags, artifacts),
    }
}

fn run(flags: Flags, args: RunArgs) -> Result<ExitCode> {
    let (program, input, non_determinism) = args.parse()?;

    let output = if flags.profile {
        let (output, profile) = VM::profile(program, input, non_determinism)?;
        println!("{profile}\n");
        output
    } else {
        VM::run(program, input, non_determinism)?
    };
    if !output.is_empty() {
        println!("{}", output.iter().join(", "));
    }

    Ok(SUCCESS)
}

fn prove(flags: Flags, args: RunArgs, artifacts: ProofArtifacts) -> Result<ExitCode> {
    let (program, input, non_determinism) = args.parse()?;

    triton_vm::profiler::start("Triton VM – Prove");
    let claim = Claim::about_program(&program).with_input(input.clone());
    let (aet, public_output) = VM::trace_execution(program, input, non_determinism)?;
    let claim = claim.with_output(public_output);
    let proof = Stark::default().prove(&claim, &aet)?;

    if flags.profile {
        let padded_height = aet.padded_height();
        let profile = triton_vm::profiler::finish()
            .with_cycle_count(aet.processor_trace.nrows())
            .with_padded_height(padded_height)
            .with_fri_domain_len(fri_domain_length(padded_height)?);
        println!("{profile}");
    }

    artifacts.write(&claim, &proof)?;

    Ok(SUCCESS)
}

fn verify(flags: Flags, artifacts: ProofArtifacts) -> Result<ExitCode> {
    let (claim, proof) = artifacts.read()?;

    triton_vm::profiler::start("Triton VM – Verify");
    let verdict = triton_vm::verify(Stark::default(), &claim, &proof);
    if flags.profile {
        let padded_height = proof.padded_height()?;
        let profile = triton_vm::profiler::finish()
            .with_padded_height(padded_height)
            .with_fri_domain_len(fri_domain_length(padded_height)?);
        println!("{profile}");
    }

    let exit_code = if verdict { SUCCESS } else { FAILURE };
    Ok(exit_code)
}

fn fri_domain_length(padded_height: usize) -> Result<usize> {
    let fri = Stark::default().fri(padded_height)?;
    Ok(fri.domain.len())
}

#[cfg(test)]
mod tests {
    use strum::IntoEnumIterator;
    use triton_vm::prelude::TableId;

    #[test]
    fn max_table_label_len_is_9() {
        let max_table_label_len = TableId::iter()
            .map(|id| id.to_string().len())
            .max()
            .unwrap();
        assert_eq!(9, max_table_label_len);
    }
}