safepkt_backend/application/command/
verify_program.rs1use 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}