safepkt_backend/application/command/
verify_program.rs

1use crate::domain;
2use crate::infra;
3use anyhow::Result;
4use clap::{App, Arg, ArgMatches};
5use color_eyre::Report;
6use domain::program_verification::*;
7use infra::display;
8use infra::file_system::save_content_in_file_system;
9use infra::PROGRAM_FUZZING;
10use infra::PROGRAM_VERIFICATION;
11use std::fs;
12use std::path::Path;
13use std::{thread, time};
14
15pub const ARGUMENT_SOURCE: &str = "source";
16pub const OPTION_WITH_FUZZING: &str = "fuzz";
17
18pub const SUBCOMMAND_NAME_VERIFY_PROGRAM: &str = "verify_program";
19
20pub fn verify_program_subcommand(version: &str) -> App {
21    return App::new(SUBCOMMAND_NAME_VERIFY_PROGRAM)
22        .about("Verify program")
23        .version(version)
24        .arg(
25            Arg::new(OPTION_WITH_FUZZING)
26                .short('f')
27                .long(OPTION_WITH_FUZZING)
28                .about("Fuzz test program by relying on \"project-oak/rvt\" propverify crate")
29                .takes_value(false),
30        )
31        .arg(
32            Arg::new(ARGUMENT_SOURCE)
33                .short('s')
34                .long(ARGUMENT_SOURCE)
35                .about("Path to rust-based smart contract (e.g. https://github.com/paritytech/ink/blob/v2.1.0/examples/erc721/src/lib.rs)")
36                .takes_value(true),
37        );
38}
39
40async fn verify_program(source_path: &str, optional_fuzzing: Option<bool>) -> Result<(), Report> {
41    let content = fs::read_to_string(source_path)?;
42
43    let (_, project_id) = save_content_in_file_system(base64::encode(content).as_bytes())
44        .expect("Can not save rust-based source in the file system.");
45
46    let with_fuzzing = optional_fuzzing.unwrap();
47    let mut step: String = String::from(PROGRAM_VERIFICATION).clone();
48    if with_fuzzing {
49        step = String::from(PROGRAM_FUZZING).clone()
50    }
51
52    let target = VerificationTarget::new(step.as_str(), project_id.as_str());
53    let verification = SmartContractVerification::new(target);
54
55    verification.run_step().await?;
56
57    display::output::print("{}", vec![""], None);
58
59    loop {
60        let progress = verification.step_progress().await?;
61
62        if progress.get("raw_status").unwrap() != "running" {
63            display::output::print("{}", vec![""], None);
64
65            break;
66        } else {
67            display::output::print("{}", vec!["."], Some(true));
68        }
69
70        let duration = time::Duration::from_millis(2000);
71        thread::sleep(duration);
72    }
73
74    verification.step_report().await?;
75
76    Ok(())
77}
78
79pub async fn run_verify_program_subcommand(source_path_matches: &ArgMatches) -> Result<(), Report> {
80    if !source_path_matches.is_present(ARGUMENT_SOURCE) {
81        display::output::eprint(
82            "A --{} argument (absolute path to smart contract) is required.",
83            vec![ARGUMENT_SOURCE],
84            None,
85        );
86    }
87
88    let with_fuzzing = source_path_matches.is_present(OPTION_WITH_FUZZING);
89
90    if let Some(source_path) = source_path_matches.value_of(ARGUMENT_SOURCE) {
91        let source = Path::new(source_path);
92        if !source.exists() || source.is_dir() {
93            display::output::eprint("Invalid path to rust-based smart contract.", vec![], None);
94        } else {
95            verify_program(source_path, Some(with_fuzzing)).await?;
96        }
97    }
98
99    Ok(())
100}