|
| 1 | +use indexmap::{IndexMap, IndexSet}; |
| 2 | +use serde::{Deserialize, Serialize}; |
| 3 | +use std::{collections::HashMap, process::Command}; |
| 4 | + |
| 5 | +use crate::{Kind, SerFunction}; |
| 6 | + |
| 7 | +/// Output of `kani list` command. |
| 8 | +#[derive(Debug, Serialize, Deserialize, Clone)] |
| 9 | +#[serde(rename_all = "kebab-case")] |
| 10 | +pub struct KaniList { |
| 11 | + pub kani_version: String, |
| 12 | + pub file_version: String, |
| 13 | + pub standard_harnesses: IndexMap<String, IndexSet<String>>, |
| 14 | + pub contract_harnesses: IndexMap<String, IndexSet<String>>, |
| 15 | + pub contracts: IndexSet<ContractedFunction>, |
| 16 | + pub totals: Total, |
| 17 | +} |
| 18 | + |
| 19 | +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] |
| 20 | +pub struct ContractedFunction { |
| 21 | + pub function: String, |
| 22 | + pub file: String, |
| 23 | + pub harnesses: Vec<String>, |
| 24 | +} |
| 25 | + |
| 26 | +#[derive(Debug, Serialize, Deserialize, Clone)] |
| 27 | +#[serde(rename_all = "kebab-case")] |
| 28 | +pub struct Total { |
| 29 | + pub standard_harnesses: usize, |
| 30 | + pub contract_harnesses: usize, |
| 31 | + pub functions_under_contract: usize, |
| 32 | +} |
| 33 | + |
| 34 | +/// Get kani list and check if it complies with Vec<SerFunction>. |
| 35 | +pub fn check(file: &str, v_ser_fun: &[SerFunction]) { |
| 36 | + let list = get_kani_list(file); |
| 37 | + check_proofs(&list, v_ser_fun); |
| 38 | +} |
| 39 | + |
| 40 | +pub fn get_kani_list(file: &str) -> KaniList { |
| 41 | + // kani list -Zlist -Zfunction-contracts --format=json file.rs |
| 42 | + let args = ["list", "-Zlist", "-Zfunction-contracts", "--format=json", file]; |
| 43 | + let output = Command::new("kani").args(args).output().unwrap(); |
| 44 | + assert!( |
| 45 | + output.status.success(), |
| 46 | + "Failed to run `kani list -Zlist -Zfunction-contracts --format=json {file}`:\n{}", |
| 47 | + std::str::from_utf8(&output.stderr).unwrap() |
| 48 | + ); |
| 49 | + |
| 50 | + // read kani-list.json |
| 51 | + let file_json = std::fs::File::open("kani-list.json").unwrap(); |
| 52 | + serde_json::from_reader(file_json).unwrap() |
| 53 | +} |
| 54 | + |
| 55 | +pub fn check_proofs(list: &KaniList, v_ser_fun: &[SerFunction]) { |
| 56 | + // sanity check |
| 57 | + let totals = &list.totals; |
| 58 | + assert_eq!(v_ser_fun.len(), totals.standard_harnesses + totals.contract_harnesses); |
| 59 | + assert_eq!( |
| 60 | + list.standard_harnesses.values().map(|s| s.len()).sum::<usize>(), |
| 61 | + totals.standard_harnesses |
| 62 | + ); |
| 63 | + assert_eq!( |
| 64 | + list.contract_harnesses.values().map(|s| s.len()).sum::<usize>(), |
| 65 | + totals.contract_harnesses |
| 66 | + ); |
| 67 | + |
| 68 | + let map: HashMap<_, _> = v_ser_fun |
| 69 | + .iter() |
| 70 | + .enumerate() |
| 71 | + .map(|(idx, f)| ((&*f.func.file, &*f.func.name), (idx, f.kind))) |
| 72 | + .collect(); |
| 73 | + |
| 74 | + // check all standard proofs are in distributed-verification json |
| 75 | + for (path, proofs) in &list.standard_harnesses { |
| 76 | + for proof in proofs { |
| 77 | + let key = (path.as_str(), proof.as_str()); |
| 78 | + let val = map.get(&key).unwrap(); |
| 79 | + dbg!(val); |
| 80 | + } |
| 81 | + } |
| 82 | + |
| 83 | + // check all contract proofs are in distributed-verification json |
| 84 | + for (path, proofs) in &list.contract_harnesses { |
| 85 | + for proof in proofs { |
| 86 | + let key = (path.as_str(), proof.as_str()); |
| 87 | + let idx = map.get(&key).unwrap(); |
| 88 | + dbg!(idx); |
| 89 | + } |
| 90 | + } |
| 91 | + |
| 92 | + // double check |
| 93 | + for (&(path, proof), &(_, kind)) in &map { |
| 94 | + let harnesses = match kind { |
| 95 | + Kind::Standard => &list.standard_harnesses[path], |
| 96 | + Kind::Contract => &list.contract_harnesses[path], |
| 97 | + }; |
| 98 | + _ = harnesses.get(proof).unwrap(); |
| 99 | + } |
| 100 | +} |
0 commit comments