add verify_solution

This commit is contained in:
germ3n 2025-05-01 16:37:36 +02:00 committed by FiveMovesAhead
parent 3c5cfe359d
commit f2e67939c0

View File

@ -1,7 +1,7 @@
use anyhow::{anyhow, Result};
use clap::{arg, ArgAction, Command};
use libloading::Library;
use std::{fs, panic, path::PathBuf};
use std::{fs, io::Read, panic, path::PathBuf};
use tig_challenges::*;
use tig_structs::core::{BenchmarkSettings, OutputData, Solution};
use tig_utils::{compress_obj, dejsonify, jsonify};
@ -58,6 +58,32 @@ fn cli() -> Command {
.value_parser(clap::value_parser!(usize)),
),
)
.subcommand(
Command::new("verify_solution")
.about("Verifies a solution")
.arg(
arg!(<SETTINGS> "Settings json string or path to json file")
.value_parser(clap::value_parser!(String)),
)
.arg(
arg!(<RAND_HASH> "A string used in seed generation")
.value_parser(clap::value_parser!(String)),
)
.arg(arg!(<NONCE> "Nonce value").value_parser(clap::value_parser!(u64)))
.arg(
arg!(<SOLUTION> "Solution json string, path to json file, or '-' for stdin")
.value_parser(clap::value_parser!(String)),
)
.arg(
arg!(--ptx [PTX] "Path to a CUDA ptx file")
.value_parser(clap::value_parser!(PathBuf)),
)
.arg(
arg!(--gpu [GPU] "Which GPU device to use")
.default_value("0")
.value_parser(clap::value_parser!(usize)),
),
)
}
fn main() {
@ -75,6 +101,14 @@ fn main() {
sub_m.get_one::<bool>("compress").unwrap().clone(),
sub_m.get_one::<usize>("gpu").unwrap().clone(),
),
Some(("verify_solution", sub_m)) => verify_solution(
sub_m.get_one::<String>("SETTINGS").unwrap().clone(),
sub_m.get_one::<String>("RAND_HASH").unwrap().clone(),
*sub_m.get_one::<u64>("NONCE").unwrap(),
sub_m.get_one::<String>("SOLUTION").unwrap().clone(),
sub_m.get_one::<PathBuf>("ptx").cloned(),
sub_m.get_one::<usize>("gpu").unwrap().clone(),
),
_ => Err(anyhow!("Invalid subcommand")),
} {
eprintln!("Error: {}", e);
@ -161,7 +195,6 @@ pub fn compute_solution(
)?
};
// Set the fuel limit in the PTX file
let ptx_content = std::fs::read_to_string(&ptx_path)
.map_err(|e| anyhow!("Failed to read PTX file: {}", e))?;
let max_fuel_hex = format!("0x{:016x}", max_fuel);
@ -275,6 +308,111 @@ pub fn compute_solution(
Ok(())
}
pub fn verify_solution(
settings: String,
rand_hash: String,
nonce: u64,
solution_path: String,
ptx_path: Option<PathBuf>,
gpu_device: usize,
) -> Result<()> {
let settings = load_settings(&settings);
let solution = load_solution(&solution_path);
let seed = settings.calc_seed(&rand_hash, nonce);
let mut err_msg = Option::<String>::None;
macro_rules! dispatch_challenges {
( $( ($c:ident, $cpu_or_gpu:tt) ),+ $(,)? ) => {{
match settings.challenge_id.as_str() {
$(
stringify!($c) => {
dispatch_challenges!(@expand $c, $cpu_or_gpu);
}
)+
_ => panic!("Unsupported challenge"),
}
}};
(@expand $c:ident, cpu) => {{
let challenge = $c::Challenge::generate_instance(
seed,
&settings.difficulty.into(),
).unwrap();
match $c::Solution::try_from(solution) {
Ok(solution) => {
match challenge.verify_solution(&solution) {
Ok(_) => println!("Solution is valid"),
Err(e) => err_msg = Some(format!("Invalid solution: {}", e)),
}
},
Err(_) => err_msg = Some(format!(
"Invalid solution. Cannot convert to {}::Solution",
stringify!($c)
)),
}
}};
(@expand $c:ident, gpu) => {{
#[cfg(not(feature = "cuda"))]
panic!("tig-runtime was not compiled with '--features cuda'");
#[cfg(feature = "cuda")]
{
if ptx_path.is_none() {
panic!("PTX file is required for GPU challenges.");
}
let ptx_path = ptx_path.unwrap();
let ptx_content = std::fs::read_to_string(&ptx_path)
.map_err(|e| anyhow!("Failed to read PTX file: {}", e))?;
let ptx = cudarc::nvrtc::Ptx::from_src(ptx_content);
let ctx = cudarc::driver::CudaContext::new(gpu_device).unwrap();
ctx.set_blocking_synchronize()?;
let module = ctx.load_module(ptx).unwrap();
let stream = ctx.default_stream();
let prop = cudarc::runtime::result::device::get_device_prop(gpu_device as i32).unwrap();
let challenge = $c::Challenge::generate_instance(
seed,
&settings.difficulty.into(),
module.clone(),
stream.clone(),
&prop,
).unwrap();
match $c::Solution::try_from(solution) {
Ok(solution) => {
match challenge.verify_solution(&solution, module.clone(), stream.clone(), &prop) {
Ok(_) => {
stream.synchronize()?;
ctx.synchronize()?;
println!("Solution is valid");
},
Err(e) => err_msg = Some(format!("Invalid solution: {}", e)),
}
},
Err(_) => err_msg = Some(format!(
"Invalid solution. Cannot convert to {}::Solution",
stringify!($c)
)),
}
}
}};
}
dispatch_challenges!((c001, cpu), (c002, cpu), (c003, cpu), (c004, gpu));
if let Some(err_msg) = err_msg {
eprintln!("Verification error: {}", err_msg);
std::process::exit(1);
}
Ok(())
}
fn load_settings(settings: &str) -> BenchmarkSettings {
let settings = if settings.ends_with(".json") {
fs::read_to_string(settings).unwrap_or_else(|_| {
@ -300,42 +438,27 @@ pub fn load_module(path: &PathBuf) -> Result<Library> {
}
}
/*
fn load_solution(solution: &str) -> Solution {
let solution = if solution == "-" {
let mut buffer = String::new();
std::io::stdin()
.read_to_string(&mut buffer)
.unwrap_or_else(|_| {
eprintln!("Failed to read solution from stdin");
std::process::exit(1);
});
buffer
} else if solution.ends_with(".json") {
fs::read_to_string(&solution).unwrap_or_else(|_| {
eprintln!("Failed to read solution file: {}", solution);
std::process::exit(1);
})
} else {
solution.to_string()
};
#include <stdio.h>
#include <stdint.h>
#include <cuda_runtime.h>
#include <math.h>
#include <float.h>
#define FUELUSAGE_OK (0)
#define FUELUSAGE_EXCEEDED (1)
#define CHECK_FUEL_LIMIT asm("trap;")
__device__ u_int64_t gbl_SIGNATURE = 0; // Run-time signature
__device__ u_int64_t gbl_FUELUSAGE = 0; // Fuel usage
__device__ u_int64_t gbl_ERRORSTAT = 0; // Error status -- set to non-zero if fuel runs out
extern "C" __global__ void initialize_kernel()
{
gbl_ERRORSTAT = FUELUSAGE_OK;
gbl_SIGNATURE = 0;
gbl_FUELUSAGE = 0;
return;
dejsonify::<Solution>(&solution).unwrap_or_else(|_| {
eprintln!("Failed to parse solution");
std::process::exit(1);
})
}
extern "C" __global__ void finalize_kernel(
u_int64_t *fuelusage_ptr, // RETURNED: (64-bit) Fuel usage
u_int64_t *signature_ptr, // RETURNED: (64-bit) Run-time signature
u_int64_t *errorstat_ptr // RETURNED: (64-bit) Error status
)
{
fuelusage_ptr[0] = gbl_FUELUSAGE; // RETURNED: (64-bit) Fuel usage
signature_ptr[0] = gbl_SIGNATURE; // RETURNED: (64-bit) Run-time signature
errorstat_ptr[0] = gbl_ERRORSTAT; // RETURNED: (64-bit) Error status -- set to non-zero if fuel runs out
return;
}
*/