diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..5b1dec4f59 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "optimism/ethereum-optimism"] + path = optimism/ethereum-optimism + url = https://github.com/ethereum-optimism/optimism.git diff --git a/Cargo.lock b/Cargo.lock index c8b7f9249a..03396e2f2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,6 +46,54 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "anstream" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +dependencies = [ + "anstyle", + "windows-sys", +] + [[package]] name = "ark-algebra-test-templates" version = "0.3.0" @@ -417,7 +465,7 @@ dependencies = [ "atty", "bitflags 1.3.2", "clap_derive", - "clap_lex", + "clap_lex 0.2.4", "indexmap", "once_cell", "strsim 0.10.0", @@ -425,6 +473,27 @@ dependencies = [ "textwrap 0.16.0", ] +[[package]] +name = "clap" +version = "4.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" +dependencies = [ + "anstream", + "anstyle", + "clap_lex 0.5.1", + "strsim 0.10.0", +] + [[package]] name = "clap_derive" version = "3.2.25" @@ -447,6 +516,18 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "clap_lex" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "colored" version = "2.0.4" @@ -727,6 +808,12 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +[[package]] +name = "elf" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6e7d85896690fe195447717af8eceae0593ac2196fd42fe88c184e904406ce" + [[package]] name = "entities" version = "1.0.1" @@ -1159,6 +1246,27 @@ dependencies = [ "tinytemplate", ] +[[package]] +name = "kimchi_optimism" +version = "0.1.0" +dependencies = [ + "ark-ff", + "ark-poly", + "clap 4.4.6", + "elf", + "groupmap", + "hex", + "kimchi", + "mina-curves", + "mina-poseidon", + "poly-commitment", + "regex", + "rmp-serde", + "serde", + "serde_json", + "serde_with", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -1218,9 +1326,9 @@ checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memoffset" @@ -1993,25 +2101,25 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.1" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", "regex-automata", - "regex-syntax 0.7.3", + "regex-syntax 0.8.2", ] [[package]] name = "regex-automata" -version = "0.3.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83d3daa6976cffb758ec878f108ba0e062a45b2d6ca3a2cca965338855476caf" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.3", + "regex-syntax 0.8.2", ] [[package]] @@ -2022,9 +2130,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.3" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab07dc67230e4a4718e70fd5c20055a4334b121f1f9db8fe63ef39ce9b8c846" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "rmp" @@ -2636,6 +2744,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "vec_map" version = "0.8.2" diff --git a/Cargo.toml b/Cargo.toml index c50da37068..851713bf6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "groupmap", "hasher", "kimchi", + "optimism", "poseidon", "poseidon/export_test_vectors", "poly-commitment", diff --git a/optimism/.gitignore b/optimism/.gitignore new file mode 100644 index 0000000000..53df36bb78 --- /dev/null +++ b/optimism/.gitignore @@ -0,0 +1 @@ +rpcs.sh diff --git a/optimism/Cargo.toml b/optimism/Cargo.toml new file mode 100644 index 0000000000..f1edb563cd --- /dev/null +++ b/optimism/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "kimchi_optimism" +version = "0.1.0" +description = "MIPS demo" +repository = "https://github.com/o1-labs/proof-systems" +homepage = "https://o1-labs.github.io/proof-systems/" +documentation = "https://o1-labs.github.io/proof-systems/rustdoc/" +readme = "README.md" +edition = "2021" +license = "Apache-2.0" + +[lib] +path = "src/lib.rs" + +[dependencies] +kimchi = { path = "../kimchi", version = "0.1.0" } +poly-commitment = { path = "../poly-commitment", version = "0.1.0" } +groupmap = { path = "../groupmap", version = "0.1.0" } +mina-curves = { path = "../curves", version = "0.1.0" } +mina-poseidon = { path = "../poseidon", version = "0.1.0" } +elf = "0.7.2" +rmp-serde = "1.1.1" +serde_json = "1.0.91" +serde = "1.0.130" +serde_with = "1.10.0" +ark-poly = { version = "0.3.0", features = [ "parallel" ] } +ark-ff = { version = "0.3.0", features = [ "parallel" ] } +clap = "4.4.6" +hex = "0.4.3" +regex = "1.10.2" diff --git a/optimism/README.md b/optimism/README.md new file mode 100644 index 0000000000..2dbf25c375 --- /dev/null +++ b/optimism/README.md @@ -0,0 +1,18 @@ +To run the demo: +* create an executable file `rpcs.sh` that looks like + ```bash + #!/usr/bin/env bash + export L1RPC=http://xxxxxxxxx + export L2RPC=http://xxxxxxxxx + ``` +* run the `run-code.sh` script. + +This will +* generate the initial state, +* execute the OP program, +* execute the OP program through the cannon MIPS VM, +* execute the OP program through the kimchi MIPS VM prover. + +The initial state will be output to a file with format `YYYY-MM-DD-HH-MM-SS-op-program-data-log.sh`. + +If you want to re-run against an existing state, pass the environment variable `FILENAME=YYYY-MM-DD-HH-MM-SS-op-program-data-log.sh` to the `run-code.sh` script. diff --git a/optimism/ethereum-optimism b/optimism/ethereum-optimism new file mode 160000 index 0000000000..c83cd947d4 --- /dev/null +++ b/optimism/ethereum-optimism @@ -0,0 +1 @@ +Subproject commit c83cd947d419aa2c213583a32872bc350a69e566 diff --git a/optimism/generate-config.sh b/optimism/generate-config.sh new file mode 100755 index 0000000000..3e8dae0221 --- /dev/null +++ b/optimism/generate-config.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +set -euo pipefail + +source rpcs.sh + +# L2 output oracle on Goerli +# L2_OUTPUT_ORACLE=0xE6Dfba0953616Bacab0c9A8ecb3a9BBa77FC15c0 +# L2 output oracle on Sepolia +L2_OUTPUT_ORACLE=0x90E9c4f8a994a250F6aEfd61CAFb4F2e895D458F + +L2_FINALIZED_NUMBER=$(cast block finalized --rpc-url "${L2RPC}" -f number) +echo "Finalize number: ${L2_FINALIZED_NUMBER}" 1>&2 +L2_FINALIZED_HASH=$(cast block "${L2_FINALIZED_NUMBER}" --rpc-url "${L2RPC}" -f hash) + +L1_FINALIZED_NUMBER=$(cast block finalized --rpc-url "${L1RPC}" -f number) +L1_FINALIZED_HASH=$(cast block "${L1_FINALIZED_NUMBER}" --rpc-url "${L1RPC}" -f hash) + +OUTPUT_INDEX=$(cast call --rpc-url "${L1RPC}" "${L2_OUTPUT_ORACLE}" 'getL2OutputIndexAfter(uint256) returns(uint256)' "${L2_FINALIZED_NUMBER}") +OUTPUT_INDEX=$((OUTPUT_INDEX-1)) + +OUTPUT=$(cast call --rpc-url "${L1RPC}" "${L2_OUTPUT_ORACLE}" 'getL2Output(uint256) returns(bytes32,uint128,uint128)' "${OUTPUT_INDEX}") +OUTPUT_ROOT=$(echo ${OUTPUT} | cut -d' ' -f 1) +OUTPUT_TIMESTAMP=$(echo ${OUTPUT} | cut -d' ' -f 2) +OUTPUT_L2BLOCK_NUMBER=$(echo ${OUTPUT} | cut -d' ' -f 3) + +L1_HEAD=$L1_FINALIZED_HASH +L2_CLAIM=$OUTPUT_ROOT +L2_BLOCK_NUMBER=$OUTPUT_L2BLOCK_NUMBER + +STARTING_L2BLOCK_NUMBER=$((L2_BLOCK_NUMBER-100)) +STARTING_OUTPUT_INDEX=$(cast call --rpc-url "${L1RPC}" "${L2_OUTPUT_ORACLE}" 'getL2OutputIndexAfter(uint256) returns(uint256)' "${STARTING_L2BLOCK_NUMBER}") +STARTING_OUTPUT=$(cast call --rpc-url "${L1RPC}" "${L2_OUTPUT_ORACLE}" 'getL2Output(uint256) returns(bytes32,uint128,uint128)' "${STARTING_OUTPUT_INDEX}") +STARTING_OUTPUT_ROOT=$(echo ${OUTPUT} | cut -d' ' -f 1) +L2_HEAD_NUMBER=$(echo ${OUTPUT} | cut -d' ' -f 3) +L2_HEAD=$(cast block "${L2_HEAD_NUMBER}" --rpc-url "${L2RPC}" -f hash) + +TODAY=$(date +"%Y-%m-%d-%H-%M-%S") +FILENAME=${TODAY}-op-program-data-log.sh +OP_PROGRAM_DATA_DIR=$(pwd)/op-program-db-sepolia-${TODAY} + +echo "export L1_HEAD=${L1_HEAD}" >> ${FILENAME} +echo "export L2_HEAD=${L2_HEAD}" >> ${FILENAME} +echo "export L2_BLOCK_NUMBER=${L2_BLOCK_NUMBER}" >> ${FILENAME} +echo "export STARTING_OUTPUT_ROOT=${STARTING_OUTPUT_ROOT}" >> ${FILENAME} +echo "export L2_CLAIM=${L2_CLAIM}" >> ${FILENAME} +echo "export OP_PROGRAM_DATA_DIR=${OP_PROGRAM_DATA_DIR}" >> ${FILENAME} +echo "export L1RPC=${L1RPC}" >> ${FILENAME} +echo "export L2RPC=${L2RPC}" >> ${FILENAME} + +echo "${FILENAME}" diff --git a/optimism/run-code.sh b/optimism/run-code.sh new file mode 100755 index 0000000000..c664e58025 --- /dev/null +++ b/optimism/run-code.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -euo pipefail + +source rpcs.sh + +set +u +if [ -z "${FILENAME}" ]; then + FILENAME="$(./generate-config.sh)" +fi +set -u + +source $FILENAME + +./run-op-program.sh + +./run-vm.sh diff --git a/optimism/run-op-program.sh b/optimism/run-op-program.sh new file mode 100755 index 0000000000..c16072e8c9 --- /dev/null +++ b/optimism/run-op-program.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +set -euo pipefail + +make -C ./ethereum-optimism/op-program op-program +make -C ./ethereum-optimism/cannon cannon + +set -x +./ethereum-optimism/op-program/bin/op-program \ + --log.level DEBUG \ + --l1 $L1RPC \ + --l2 $L2RPC \ + --network sepolia \ + --datadir ${OP_PROGRAM_DATA_DIR} \ + --l1.head $L1_HEAD \ + --l2.head $L2_HEAD \ + --l2.outputroot $STARTING_OUTPUT_ROOT \ + --l2.claim $L2_CLAIM \ + --l2.blocknumber $L2_BLOCK_NUMBER + +./ethereum-optimism/cannon/bin/cannon load-elf --path=./ethereum-optimism/op-program/bin/op-program-client.elf + +./ethereum-optimism/cannon/bin/cannon run \ + --pprof.cpu \ + --info-at '%10000000' \ + --proof-at never \ + --input ./state.json \ + -- \ + ./ethereum-optimism/op-program/bin/op-program \ + --log.level DEBUG \ + --l1 ${L1RPC} \ + --l2 ${L2RPC} \ + --network sepolia \ + --datadir ${OP_PROGRAM_DATA_DIR} \ + --l1.head ${L1_HEAD} \ + --l2.head ${L2_HEAD} \ + --l2.outputroot ${STARTING_OUTPUT_ROOT} \ + --l2.claim ${L2_CLAIM} \ + --l2.blocknumber ${L2_BLOCK_NUMBER} \ + --server diff --git a/optimism/run-vm.sh b/optimism/run-vm.sh new file mode 100755 index 0000000000..4f24506b4e --- /dev/null +++ b/optimism/run-vm.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +set -euo pipefail + +cargo run -p kimchi_optimism -- \ + --pprof-cpu \ + --info-at '%10000000' \ + --proof-at never \ + --input ./state.json \ + -- \ + ./ethereum-optimism/op-program/bin/op-program \ + --log.level DEBUG \ + --l1 ${L1RPC} \ + --l2 ${L2RPC} \ + --network sepolia \ + --datadir ${OP_PROGRAM_DATA_DIR} \ + --l1.head ${L1_HEAD} \ + --l2.head ${L2_HEAD} \ + --l2.outputroot ${STARTING_OUTPUT_ROOT} \ + --l2.claim ${L2_CLAIM} \ + --l2.blocknumber ${L2_BLOCK_NUMBER} \ + --server diff --git a/optimism/src/cannon.rs b/optimism/src/cannon.rs new file mode 100644 index 0000000000..c7693f3cab --- /dev/null +++ b/optimism/src/cannon.rs @@ -0,0 +1,118 @@ +// Data structure and stuff for compatibility with Cannon + +use regex::Regex; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct Page { + pub index: u32, + pub data: String, +} + +// The renaming below keeps compatibility with OP Cannon's state format +#[derive(Serialize, Deserialize, Debug)] +pub struct State { + pub memory: Vec, + #[serde(rename = "preimageKey")] + pub preimage_key: String, + #[serde(rename = "preimageOffset")] + pub preimage_offset: u32, + pub pc: u32, + #[serde(rename = "nextPC")] + next_pc: u32, // + pub lo: u32, + pub hi: u32, + pub heap: u32, + exit: u8, + pub exited: bool, + pub step: u64, + pub registers: [u32; 32], + pub last_hint: Option>, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum StepFrequency { + Never, + Always, + Exactly(u64), + Every(u64), +} + +// Simple parser for Cannon's "frequency format" +// A frequency input is either +// - never/always +// - = (only at step n) +// - % (every steps multiple of n) +pub fn step_frequency_parser(s: &str) -> std::result::Result { + use StepFrequency::*; + + let mod_re = Regex::new(r"%([0-9]+)").unwrap(); + let eq_re = Regex::new(r"=([0-9]+)").unwrap(); + + match s { + "never" => Ok(Never), + "always" => Ok(Always), + s => { + if let Some(m) = mod_re.captures(s) { + Ok(Every(m[1].parse::().unwrap())) + } else if let Some(m) = eq_re.captures(s) { + Ok(Exactly(m[1].parse::().unwrap())) + } else { + Err(format!("Unknown frequency format {}", s)) + } + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn sp_parser() { + use StepFrequency::*; + assert_eq!(step_frequency_parser("never"), Ok(Never)); + assert_eq!(step_frequency_parser("always"), Ok(Always)); + assert_eq!(step_frequency_parser("=123"), Ok(Exactly(123))); + assert_eq!(step_frequency_parser("%123"), Ok(Every(123))); + assert!(step_frequency_parser("@123").is_err()); + } +} + +impl ToString for State { + // A very debatable and incomplete, but serviceable, `to_string` implementation. + fn to_string(&self) -> String { + format!( + "memory_size (length): {}\nfirst page size: {}\npreimage key: {}\npreimage offset:{}\npc: {}\nlo: {}\nhi: {}\nregisters:{:#?} ", + self.memory.len(), + self.memory[0].data.len(), + self.preimage_key, + self.preimage_offset, + self.pc, + self.lo, + self.hi, + self.registers + ) + } +} + +#[derive(Debug)] +pub struct HostProgram { + pub name: String, + pub arguments: Vec, +} + +#[derive(Debug)] +pub struct VmConfiguration { + pub input_state_file: String, + pub output_state_file: String, + pub metadata_file: String, + pub proof_at: StepFrequency, + pub stop_at: StepFrequency, + pub info_at: StepFrequency, + pub proof_fmt: String, + pub snapshot_fmt: String, + pub pprof_cpu: bool, + pub host: Option, +} diff --git a/optimism/src/lib.rs b/optimism/src/lib.rs new file mode 100644 index 0000000000..3bec498068 --- /dev/null +++ b/optimism/src/lib.rs @@ -0,0 +1 @@ +pub mod cannon; diff --git a/optimism/src/main.rs b/optimism/src/main.rs new file mode 100644 index 0000000000..32c86b9f9e --- /dev/null +++ b/optimism/src/main.rs @@ -0,0 +1,134 @@ +use clap::{arg, value_parser, Arg, ArgAction, Command}; +use kimchi_optimism::cannon::{State, VmConfiguration}; +use std::{fs::File, io::BufReader, process::ExitCode}; + +fn cli() -> VmConfiguration { + use kimchi_optimism::cannon::*; + + let app_name = "zkvm"; + let cli = Command::new(app_name) + .version("0.1") + .about("MIPS-based zkvm") + .arg(arg!(--input "initial state file").default_value("state.json")) + .arg(arg!(--output "output state file").default_value("out.json")) + .arg(arg!(--meta "metadata file").default_value("meta.json")) + // The CLI arguments below this line are ignored at this point + .arg( + Arg::new("proof-at") + .short('p') + .long("proof-at") + .value_name("FREQ") + .default_value("never") + .value_parser(step_frequency_parser), + ) + .arg( + Arg::new("proof-fmt") + .long("proof-fmt") + .value_name("FORMAT") + .default_value("proof-%d.json"), + ) + .arg( + Arg::new("snapshot-fmt") + .long("snapshot-fmt") + .value_name("FORMAT") + .default_value("state-%d.json"), + ) + .arg( + Arg::new("stop-at") + .long("stop-at") + .value_name("FREQ") + .default_value("never") + .value_parser(step_frequency_parser), + ) + .arg( + Arg::new("info-at") + .long("info-at") + .value_name("FREQ") + .default_value("never") + .value_parser(step_frequency_parser), + ) + .arg( + Arg::new("pprof-cpu") + .long("pprof-cpu") + .action(ArgAction::SetTrue), + ) + .arg( + arg!(host: [HOST] "host program specification [host program arguments]") + .num_args(1..) + .last(true) + .value_parser(value_parser!(String)), + ); + + let cli = cli.get_matches(); + + let input_state_file = cli.get_one::("input").unwrap(); + + let output_state_file = cli.get_one::("output").unwrap(); + + let metadata_file = cli.get_one::("meta").unwrap(); + + let proof_at = cli.get_one::("proof-at").unwrap(); + let info_at = cli.get_one::("info-at").unwrap(); + let stop_at = cli.get_one::("stop-at").unwrap(); + + let proof_fmt = cli.get_one::("proof-fmt").unwrap(); + let snapshot_fmt = cli.get_one::("snapshot-fmt").unwrap(); + let pprof_cpu = cli.get_one::("pprof-cpu").unwrap(); + + let host_spec = cli + .get_many::("host") + .map(|vals| vals.collect::>()) + .unwrap_or_default(); + + let host = if host_spec.is_empty() { + None + } else { + Some(HostProgram { + name: host_spec[0].to_string(), + arguments: host_spec[1..] + .to_vec() + .iter() + .map(|x| x.to_string()) + .collect(), + }) + }; + + VmConfiguration { + input_state_file: input_state_file.to_string(), + output_state_file: output_state_file.to_string(), + metadata_file: metadata_file.to_string(), + proof_at: proof_at.clone(), + stop_at: stop_at.clone(), + info_at: info_at.clone(), + proof_fmt: proof_fmt.to_string(), + snapshot_fmt: snapshot_fmt.to_string(), + pprof_cpu: *pprof_cpu, + host, + } +} + +pub fn main() -> ExitCode { + let configuration = cli(); + + println!("configuration\n{:#?}", configuration); + + let file = File::open(configuration.input_state_file).expect("Error opening input state file "); + + let reader = BufReader::new(file); + // Read the JSON contents of the file as an instance of `State`. + let state: State = serde_json::from_reader(reader).expect("Error reading input state file"); + + if let Some(host_program) = configuration.host { + println!("Launching host program {}", host_program.name); + + let _child = std::process::Command::new(host_program.name) + .args(host_program.arguments) + .spawn() + .expect("Could not spawn host process"); + }; + + println!("{}", state.to_string()); + + // TODO: Logic + ExitCode::FAILURE +}