diff --git a/tig-runtime/src/main.rs b/tig-runtime/src/main.rs index dabffdd..fd4eb8b 100644 --- a/tig-runtime/src/main.rs +++ b/tig-runtime/src/main.rs @@ -1,7 +1,7 @@ use anyhow::{anyhow, Result}; use clap::{arg, Command}; use libloading::Library; -use serde_json::{Map, Value}; +use serde_json::{Map, Value, from_str as json_from_str}; use std::{fs, panic, path::PathBuf}; use tig_challenges::*; use tig_structs::core::{BenchmarkSettings, CPUArchitecture, OutputData}; @@ -16,21 +16,64 @@ use { std::sync::Arc, }; +#[derive(serde::Deserialize, Debug)] +struct StdinConfig { + settings: String, + rand_hash: String, + nonce: u64, + binary: String, + #[serde(default)] + hyperparameters: Option, + #[serde(default)] + ptx: Option, + #[serde(default = "default_fuel")] + fuel: u64, + #[serde(default)] + output: Option, + #[serde(default)] + gpu: Option, +} + +fn default_fuel() -> u64 { + 2000000000 +} + fn cli() -> Command { Command::new("tig-runtime") .about("Executes an algorithm on a single challenge instance") - .arg_required_else_help(true) + .arg( + arg!(--stdin "Read configuration from stdin as JSON") + .action(clap::ArgAction::SetTrue) + .conflicts_with_all(&[ + "SETTINGS", + "RAND_HASH", + "NONCE", + "BINARY", + "hyperparameters", + "ptx", + "fuel", + "output", + "gpu", + ]), + ) .arg( arg!( "Settings json string or path to json file") + .required_unless_present("stdin") .value_parser(clap::value_parser!(String)), ) .arg( arg!( "A string used in seed generation") + .required_unless_present("stdin") .value_parser(clap::value_parser!(String)), ) - .arg(arg!( "Nonce value").value_parser(clap::value_parser!(u64))) + .arg( + arg!( "Nonce value") + .required_unless_present("stdin") + .value_parser(clap::value_parser!(u64)) + ) .arg( arg!( "Path to a shared object (*.so) file") + .required_unless_present("stdin") .value_parser(clap::value_parser!(PathBuf)), ) .arg( @@ -58,21 +101,50 @@ fn cli() -> Command { fn main() { let matches = cli().get_matches(); + let result = if matches.get_flag("stdin") { + let mut stdin_content = String::new(); + if let Err(e) = std::io::Read::read_to_string(&mut std::io::stdin(), &mut stdin_content) { + eprintln!("Failed to read from stdin: {}", e); + std::process::exit(1); + } - if let Err(e) = compute_solution( - matches.get_one::("SETTINGS").unwrap().clone(), - matches.get_one::("RAND_HASH").unwrap().clone(), - *matches.get_one::("NONCE").unwrap(), - matches.get_one::("BINARY").unwrap().clone(), - matches.get_one("hyperparameters").cloned(), - matches.get_one::("ptx").cloned(), - *matches.get_one::("fuel").unwrap(), - matches.get_one::("output").cloned(), - matches.get_one::("gpu").cloned(), - ) { - eprintln!("Runtime Error: {}", e); - std::process::exit(84); - } + let config: StdinConfig = match json_from_str(&stdin_content) { + Ok(cfg) => cfg, + Err(e) => { + eprintln!("Failed to parse JSON from stdin: {}", e); + std::process::exit(1); + } + }; + + compute_solution( + config.settings, + config.rand_hash, + config.nonce, + PathBuf::from(config.binary), + config.hyperparameters, + config.ptx.map(PathBuf::from), + config.fuel, + config.output.map(PathBuf::from), + config.gpu, + ) + } else { + compute_solution( + matches.get_one::("SETTINGS").unwrap().clone(), + matches.get_one::("RAND_HASH").unwrap().clone(), + *matches.get_one::("NONCE").unwrap(), + matches.get_one::("BINARY").unwrap().clone(), + matches.get_one("hyperparameters").cloned(), + matches.get_one::("ptx").cloned(), + *matches.get_one::("fuel").unwrap(), + matches.get_one::("output").cloned(), + matches.get_one::("gpu").cloned(), + ) + }; + + if let Err(e) = result { + eprintln!("Runtime Error: {}", e); + std::process::exit(84); + } } pub fn compute_solution( diff --git a/tig-verifier/src/main.rs b/tig-verifier/src/main.rs index ddf5c7d..fc421a4 100644 --- a/tig-verifier/src/main.rs +++ b/tig-verifier/src/main.rs @@ -1,6 +1,6 @@ use anyhow::{anyhow, Result}; use clap::{arg, Command}; -use serde_json::{Map, Value}; +use serde_json::{Map, Value, from_str as json_from_str}; use std::{fs, io::Read, panic, path::PathBuf}; use tig_challenges::*; use tig_structs::core::BenchmarkSettings; @@ -9,21 +9,54 @@ use tig_utils::dejsonify; #[cfg(feature = "cuda")] use cudarc::{driver::CudaContext, nvrtc::Ptx, runtime::result::device::get_device_prop}; +#[derive(serde::Deserialize, Debug)] +struct StdinConfig { + settings: String, + rand_hash: String, + nonce: u64, + solution: String, + #[serde(default)] + ptx: Option, + #[serde(default)] + gpu: Option, + #[serde(default)] + verbose: bool, +} + fn cli() -> Command { Command::new("tig-verifier") .about("Verifies a solution") - .arg_required_else_help(true) + .arg( + arg!(--stdin "Read configuration from stdin as JSON") + .action(clap::ArgAction::SetTrue) + .conflicts_with_all(&[ + "SETTINGS", + "RAND_HASH", + "NONCE", + "SOLUTION", + "ptx", + "gpu", + "verbose", + ]), + ) .arg( arg!( "Settings json string or path to json file") + .required_unless_present("stdin") .value_parser(clap::value_parser!(String)), ) .arg( arg!( "A string used in seed generation") + .required_unless_present("stdin") .value_parser(clap::value_parser!(String)), ) - .arg(arg!( "Nonce value").value_parser(clap::value_parser!(u64))) + .arg( + arg!( "Nonce value") + .required_unless_present("stdin") + .value_parser(clap::value_parser!(u64)) + ) .arg( arg!( "Solution base64 string, path to json file with solution field, or '-' for stdin") + .required_unless_present("stdin") .value_parser(clap::value_parser!(String)), ) .arg(arg!(--ptx [PTX] "Path to a CUDA ptx file").value_parser(clap::value_parser!(PathBuf))) @@ -34,15 +67,43 @@ fn cli() -> Command { fn main() { let matches = cli().get_matches(); - if let Err(e) = verify_solution( - matches.get_one::("SETTINGS").unwrap().clone(), - matches.get_one::("RAND_HASH").unwrap().clone(), - *matches.get_one::("NONCE").unwrap(), - matches.get_one::("SOLUTION").unwrap().clone(), - matches.get_one::("ptx").cloned(), - matches.get_one::("gpu").cloned(), - matches.get_one::("verbose").cloned().unwrap_or(false), - ) { + let result = if matches.get_flag("stdin") { + let mut stdin_content = String::new(); + if let Err(e) = std::io::Read::read_to_string(&mut std::io::stdin(), &mut stdin_content) { + eprintln!("Failed to read from stdin: {}", e); + std::process::exit(1); + } + + let config: StdinConfig = match json_from_str(&stdin_content) { + Ok(cfg) => cfg, + Err(e) => { + eprintln!("Failed to parse JSON from stdin: {}", e); + std::process::exit(1); + } + }; + + verify_solution( + config.settings, + config.rand_hash, + config.nonce, + config.solution, + config.ptx.map(PathBuf::from), + config.gpu, + config.verbose, + ) + } else { + verify_solution( + matches.get_one::("SETTINGS").unwrap().clone(), + matches.get_one::("RAND_HASH").unwrap().clone(), + *matches.get_one::("NONCE").unwrap(), + matches.get_one::("SOLUTION").unwrap().clone(), + matches.get_one::("ptx").cloned(), + matches.get_one::("gpu").cloned(), + matches.get_one::("verbose").cloned().unwrap_or(false), + ) + }; + + if let Err(e) = result { eprintln!("Error: {}", e); std::process::exit(1); }