use crate::pipeline::{Pipeline, PipelineConfig};
use crate::scene::Scene;
use crate::NvsimError;
pub struct Proof;
impl Proof {
pub const N_SAMPLES: usize = 256;
pub const SEED: u64 = 42;
pub const REFERENCE_SCENE_JSON: &'static str = r#"{
"dipoles": [
{"position": [0.0, 0.0, 0.5], "moment": [0.0, 0.0, 1.0e-3]},
{"position": [0.3, 0.0, 0.4], "moment": [1.0e-4, 5.0e-5, 0.0]}
],
"loops": [
{"centre": [0.0, 0.2, 0.6], "normal": [0.0, 1.0, 0.0], "radius": 0.05, "current": 0.5, "n_segments": 64}
],
"ferrous": [
{"position": [0.5, 0.0, 0.0], "volume": 1.0e-4, "susceptibility": 5000.0}
],
"eddy": [],
"sensors": [[0.0, 0.0, 0.0]],
"ambient_field": [1.0e-6, 0.0, 0.0]
}"#;
pub fn reference_scene() -> Result<Scene, NvsimError> {
Ok(serde_json::from_str(Self::REFERENCE_SCENE_JSON)?)
}
pub fn generate() -> Result<[u8; 32], NvsimError> {
let scene = Self::reference_scene()?;
let cfg = PipelineConfig::default();
let pipeline = Pipeline::new(scene, cfg, Self::SEED);
let (_, witness) = pipeline.run_with_witness(Self::N_SAMPLES);
Ok(witness)
}
pub fn verify(expected: &[u8; 32]) -> Result<(), [u8; 32]> {
let actual = Self::generate().map_err(|_| [0u8; 32])?;
if &actual == expected {
Ok(())
} else {
Err(actual)
}
}
pub fn hex(witness: &[u8; 32]) -> String {
let mut s = String::with_capacity(64);
for b in witness {
s.push_str(&format!("{b:02x}"));
}
s
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reference_scene_parses() {
let scene = Proof::reference_scene().expect("reference scene must parse");
assert_eq!(scene.dipoles.len(), 2);
assert_eq!(scene.loops.len(), 1);
assert_eq!(scene.ferrous.len(), 1);
assert_eq!(scene.sensors.len(), 1);
assert_eq!(scene.ambient_field, [1.0e-6, 0.0, 0.0]);
}
#[test]
fn proof_generate_is_deterministic_across_runs() {
let w1 = Proof::generate().unwrap();
let w2 = Proof::generate().unwrap();
assert_eq!(w1, w2);
}
#[test]
fn proof_witness_changes_when_seed_changes() {
let w1 = Proof::generate().unwrap();
let scene = Proof::reference_scene().unwrap();
let cfg = PipelineConfig::default();
let p = Pipeline::new(scene, cfg, Proof::SEED + 1);
let (_, w2) = p.run_with_witness(Proof::N_SAMPLES);
assert_ne!(w1, w2);
}
#[test]
fn proof_hex_formats_64_chars() {
let bytes = [0xAB_u8; 32];
let hex = Proof::hex(&bytes);
assert_eq!(hex.len(), 64);
assert_eq!(hex, "ab".repeat(32));
}
#[test]
fn proof_witness_publishes_a_known_value() {
let actual = Proof::generate().unwrap();
let actual_hex = Proof::hex(&actual);
let published_hex = include_published_witness();
assert_eq!(
actual_hex, published_hex,
"Proof witness drifted. Audit the change, then update PUBLISHED_WITNESS_HEX."
);
}
fn include_published_witness() -> &'static str {
PUBLISHED_WITNESS_HEX
}
const PUBLISHED_WITNESS_HEX: &str =
"cc8de9b01b0ff5bd97a6c17848a3f156c174ea7589d0888164a441584ec593b4";
}