use std::fs;
use std::path::PathBuf;
use clap::Parser;
#[derive(Debug, Parser)]
pub struct Args {
pub cert_path: PathBuf,
}
pub fn run(args: Args) -> Result<(), Box<dyn std::error::Error>> {
let path = args.cert_path;
let contents = fs::read_to_string(&path)
.map_err(|err| format!("Fix: cannot read {}: {err}", path.display()))?;
let doc: serde_json::Value = serde_json::from_str(&contents)
.map_err(|err| format!("Fix: invalid JSON in {}: {err}", path.display()))?;
let cert_hash = match doc.get("registry_hash").and_then(|v| v.as_array()) {
Some(arr) if arr.len() == 32 => {
let mut bytes = [0u8; 32];
for (i, val) in arr.iter().enumerate() {
let n = val.as_u64().ok_or_else(|| {
format!(
"Fix: registry_hash[{i}] in {} is not an integer.",
path.display()
)
})?;
bytes[i] = u8::try_from(n).map_err(|_| {
format!("Fix: registry_hash[{i}] in {} exceeds u8.", path.display())
})?;
}
bytes
}
_ => return Err("Fix: certificate missing or malformed 'registry_hash' field.".into()),
};
let backend_name = doc
.get("backend_name")
.and_then(|v| v.as_str())
.unwrap_or("<unknown>");
let backend_version = doc
.get("backend_version")
.and_then(|v| v.as_str())
.unwrap_or("<unknown>");
let timestamp = doc
.get("timestamp")
.and_then(|v| v.as_str())
.unwrap_or("<unknown>");
let specs = crate::op_registry::compiled_specs();
let recomputed = crate::pipeline::certify::compute_registry_hash(&specs);
println!("Certificate : {}", path.display());
println!("Backend : {backend_name} {backend_version}");
println!("Timestamp : {timestamp}");
let cert_hash_hex = vyre_reference::hash::hex::bytes_to_hex(&cert_hash);
let recomputed_hex = vyre_reference::hash::hex::bytes_to_hex(&recomputed);
println!("Cert hash : {cert_hash_hex}");
println!("Local hash : {recomputed_hex}");
if cert_hash == recomputed {
println!("Result : PASS - registry hash matches.");
} else {
println!("Result : FAIL - registry hash mismatch.");
return Err(
"Fix: certificate was produced against a different compiled spec registry.".into(),
);
}
Ok(())
}