An unpinned resolve (and a pinned one with a key log) already hashes every member the decode reads and their listing, for check 5. IntParams keeps that listing's SHA-256 with the member names it was formed over, and the key of the loaded set uses it only for a capability that reads exactly those members; any other key hashes the bytes (IntParams::read_digest). The bytes are borrowed for as long as the parameters live. Measured before this change: the key's SHA-256 of the pretrained set (24 MB, software SHA-256) was about 58 ms of a ~110 ms decode with its set held. Co-Authored-By: Claude Opus 5.5 <[email protected]>
846 lines
49 KiB
Rust
846 lines
49 KiB
Rust
//! `int_payload`: the binary forms against the JSON they are converted from, the
|
|
//! integer decode path without the neural sink, the closed refusal list, and SPEC
|
|
//! §3.1's check 5 through [`super::resolve`].
|
|
|
|
use std::collections::BTreeMap;
|
|
use std::path::PathBuf;
|
|
|
|
use sha2::{Digest, Sha256};
|
|
|
|
use super::*;
|
|
use crate::int_kernels::{with_policy, Policy};
|
|
|
|
const POLICIES: [Policy; 5] = [Policy::reference(), Policy::fast(1), Policy::fast(2), Policy::fast(5), Policy::fast(0)];
|
|
|
|
fn vroot() -> PathBuf {
|
|
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/vectors")
|
|
}
|
|
fn read(rel: &str) -> Vec<u8> {
|
|
let p = vroot().join(rel);
|
|
std::fs::read(&p).unwrap_or_else(|e| panic!("read {}: {e}", p.display()))
|
|
}
|
|
fn jload(rel: &str) -> serde_json::Value {
|
|
serde_json::from_slice(&read(rel)).unwrap_or_else(|e| panic!("parse {rel}: {e}"))
|
|
}
|
|
fn hex(b: &[u8]) -> String {
|
|
b.iter().map(|x| format!("{x:02x}")).collect()
|
|
}
|
|
fn sha(b: &[u8]) -> String {
|
|
hex(&Sha256::digest(b))
|
|
}
|
|
|
|
/// Decode under every kernel policy; all outputs must be identical.
|
|
fn same_under_every_policy<T: PartialEq + std::fmt::Debug>(what: &str, f: impl Fn() -> T) -> T {
|
|
let want = with_policy(POLICIES[0], &f);
|
|
for p in &POLICIES[1..] {
|
|
assert!(with_policy(*p, &f) == want, "{what}: output under {p:?} differs from the reference kernels");
|
|
}
|
|
want
|
|
}
|
|
|
|
/// An unpinned decode's parameters over `held`, at a sink with no key log.
|
|
fn unpinned<'a>(cap: &str, held: HeldBytes<'a>) -> IntParams<'a> {
|
|
resolve(cap, None, &[held], None).expect("an unpinned set resolves")
|
|
}
|
|
|
|
/// The tiny conditional codec's set, by member name.
|
|
fn int_hyper_set() -> BTreeMap<String, Vec<u8>> {
|
|
[("entropy.json", "int_hyper/tiny_entropy.json"), ("g_s.whm", "int_hyper/tiny_g_s.bin"), ("h_s.whm", "int_hyper/tiny_h_s.bin")]
|
|
.into_iter().map(|(k, v)| (k.to_string(), read(v))).collect()
|
|
}
|
|
fn view(m: &BTreeMap<String, Vec<u8>>) -> BTreeMap<String, &[u8]> {
|
|
m.iter().map(|(k, v)| (k.clone(), v.as_slice())).collect()
|
|
}
|
|
|
|
/// Every frame's SHA-256 and the clip's, against the sidecar entry `key`.
|
|
#[cfg(feature = "video_int")]
|
|
fn assert_frames_sidecar(frames: &[Vec<u8>], key: &str) {
|
|
let side = jload("int_video/sha256.json");
|
|
let want = side[key]["frames_sha256"].as_array().unwrap();
|
|
assert_eq!(want.len(), frames.len(), "{key}: frame count");
|
|
let per: Vec<[u8; 32]> = frames.iter().map(|f| Sha256::digest(f).into()).collect();
|
|
for (i, d) in per.iter().enumerate() {
|
|
assert_eq!(hex(d), want[i].as_str().unwrap(), "{key}: frame {i}");
|
|
}
|
|
assert_eq!(sha(&per.concat()), side[key]["clip_sha256"].as_str().unwrap(), "{key}: clip");
|
|
}
|
|
|
|
/// §6.1 test 17, `WIV1`: the converter writes the committed payload; the payload's
|
|
/// fields are the JSON's; the payload decodes, under every kernel policy, to the
|
|
/// frames the JSON form decodes to through the same typed decoder, and those frames
|
|
/// are anchored to output recorded before this revision: the sidecar's SHA-256 and the
|
|
/// FNV-1a hashes the encode tools recorded (`int_video.json`, `video_real.json`,
|
|
/// `video_nk.json`). The keyframe-1 clip is also checked against the unchecked
|
|
/// conditional decode of its keyframe.
|
|
#[cfg(feature = "video_int")]
|
|
#[test]
|
|
fn wiv1_json_equivalence() {
|
|
use crate::int_video::{decode, decode_pframes, fnv1a, VideoJson};
|
|
for (name, json, bin, conf) in [
|
|
("int_video", "int_video/int_video_bitstream.json", "int_video/int_video_bitstream.wiv1", "int_video/int_video.json"),
|
|
("video_real", "int_video/video_real_bitstream.json", "int_video/video_real_bitstream.wiv1", "int_video/video_real.json"),
|
|
] {
|
|
let js = jload(json);
|
|
let payload = read(bin);
|
|
assert_eq!(wiv1_from_json(&js, None).unwrap(), payload, "{name}: the converter writes the committed payload");
|
|
let (v, j) = (wire::parse_wiv1(&payload, Some(0)).unwrap(), VideoJson::from_value(&js).unwrap());
|
|
assert_eq!((v.n_frames as usize, v.h as usize, v.w as usize, v.block as usize, v.q as i64, v.offset, v.mv_precision),
|
|
(j.n_frames, j.h, j.w, j.block, j.q, j.offset, j.mv_precision), "{name}: header");
|
|
assert_eq!((&v.cdf, &v.mvs, v.rans), (&j.cdf, &j.mvs, j.rans.as_slice()), "{name}: tables and stream");
|
|
let frames = same_under_every_policy(name, || match decode_int(crate::codecs::CAP_VIDEO_INT_MOTION, &payload, &IntParams::none()) {
|
|
Ok(IntDecoded::Video { frames, .. }) => frames,
|
|
other => panic!("{name}: {other:?}"),
|
|
});
|
|
assert_eq!(frames, decode(&js).unwrap().frames, "{name}: the binary and JSON forms decode alike");
|
|
assert_frames_sidecar(&frames, name);
|
|
let want = jload(conf);
|
|
for (f, w) in frames.iter().zip(want["frame_hashes"].as_array().unwrap()) {
|
|
assert_eq!(format!("{:016x}", fnv1a(f)), w.as_str().unwrap(), "{name}: FNV-1a");
|
|
}
|
|
}
|
|
// Keyframe 1: the nested WIH1 is the tiny conditional codec's payload.
|
|
let (set, wih1) = (int_hyper_set(), read("int_hyper/tiny_bitstream.wih1"));
|
|
let js = jload("int_video/video_nk_bitstream.json");
|
|
let payload = read("int_video/video_nk_bitstream.wiv1");
|
|
assert_eq!(wiv1_from_json(&js, Some(&wih1)).unwrap(), payload, "video_nk: the converter writes the committed payload");
|
|
assert_eq!(wire::parse_wiv1(&payload, Some(1)).unwrap().wih1, Some(wih1.as_slice()));
|
|
let files = view(&set);
|
|
let params = unpinned(crate::codecs::CAP_VIDEO_INT_HYPER, HeldBytes::Set { listing: b"", files: &files });
|
|
let frames = same_under_every_policy("video_nk", || match decode_int(crate::codecs::CAP_VIDEO_INT_HYPER, &payload, ¶ms) {
|
|
Ok(IntDecoded::Video { frames, .. }) => frames,
|
|
other => panic!("video_nk: {other:?}"),
|
|
});
|
|
let legacy = {
|
|
use crate::int_hyper_synth::{decode_conditional_checked, CondBitstream, CondEntropy, IntModel};
|
|
let g_s = IntModel::from_json(&jload("int_hyper/tiny_g_s.json"));
|
|
let h_s = IntModel::from_json(&jload("int_hyper/tiny_h_s.json"));
|
|
let ent = CondEntropy::from_value(&jload("int_hyper/tiny_entropy.json"));
|
|
let (f0, h, w) = decode_conditional_checked(&g_s, &h_s, &ent, &CondBitstream::from_value(&jload("int_hyper/tiny_bitstream.json")).stream()).unwrap();
|
|
decode_pframes(&f0, h, w, &js).unwrap().frames
|
|
};
|
|
assert_eq!(frames, legacy, "video_nk: the binary and JSON forms decode alike");
|
|
assert_frames_sidecar(&frames, "video_nk");
|
|
let want = jload("int_video/video_nk.json");
|
|
let want = want["frame_hashes"].as_array().unwrap();
|
|
assert_eq!(frames.len(), want.len(), "video_nk: frame count");
|
|
for (f, w) in frames.iter().zip(want) {
|
|
assert_eq!(format!("{:016x}", crate::int_video::fnv1a(f)), w.as_str().unwrap(), "video_nk: the recorded FNV-1a");
|
|
}
|
|
}
|
|
|
|
/// §6.1 test 17, `WIH1`.
|
|
#[test]
|
|
fn wih1_json_equivalence() {
|
|
use crate::int_hyper_synth::{decode_conditional_checked, fnv1a, CondBitstream, CondEntropy, IntModel};
|
|
let js = jload("int_hyper/tiny_bitstream.json");
|
|
let payload = read("int_hyper/tiny_bitstream.wih1");
|
|
assert_eq!(wih1_from_json(&js).unwrap(), payload, "the converter writes the committed payload");
|
|
let (k, b) = (wire::parse_wih1(&payload).unwrap(), CondBitstream::from_value(&js));
|
|
assert_eq!((k.m as usize, k.sh as usize, k.sw as usize, k.n_z as usize, k.zh as usize, k.zw as usize),
|
|
(b.m, b.sh, b.sw, b.n_z, b.zh, b.zw));
|
|
assert_eq!((k.z_rans, k.rans), (b.z_rans.as_slice(), b.rans.as_slice()));
|
|
let set = int_hyper_set();
|
|
let files = view(&set);
|
|
let params = unpinned(crate::codecs::CAP_NEURAL_INT_HYPER, HeldBytes::Set { listing: b"", files: &files });
|
|
let rgb = same_under_every_policy("tiny", || match decode_int(crate::codecs::CAP_NEURAL_INT_HYPER, &payload, ¶ms) {
|
|
Ok(IntDecoded::Image { rgb, width: 16, height: 16 }) => rgb,
|
|
other => panic!("tiny: {other:?}"),
|
|
});
|
|
let g_s = IntModel::from_json(&jload("int_hyper/tiny_g_s.json"));
|
|
let h_s = IntModel::from_json(&jload("int_hyper/tiny_h_s.json"));
|
|
let ent = CondEntropy::from_value(&jload("int_hyper/tiny_entropy.json"));
|
|
assert_eq!(rgb, decode_conditional_checked(&g_s, &h_s, &ent, &b.stream()).unwrap().0, "the payload and the JSON form decode alike");
|
|
assert_eq!(sha(&rgb), jload("int_hyper/sha256.json")["tiny"]["sha256"].as_str().unwrap());
|
|
assert_eq!(format!("{:016x}", fnv1a(&rgb)), jload("int_hyper/tiny.json")["rgb_hash"].as_str().unwrap());
|
|
// The checked entropy reader accepts the committed tables and gives what the
|
|
// unchecked one gives.
|
|
let checked = CondEntropy::try_from_value(&jload("int_hyper/tiny_entropy.json")).unwrap();
|
|
assert_eq!((checked.edges, checked.cdfs, checked.z_median_q), (ent.edges, ent.cdfs, ent.z_median_q));
|
|
}
|
|
|
|
/// §6.1 test 17, `WIA1`: the payload and its JSON form decode alike through the typed
|
|
/// decoder, to the PCM recorded before this revision.
|
|
#[cfg(feature = "audio_int")]
|
|
#[test]
|
|
fn wia1_json_equivalence() {
|
|
use crate::int_audio::{decode, pcm_fnv1a, AudioJson, AudioModel};
|
|
let (js, conf) = (jload("int_audio/audio_bitstream.json"), jload("int_audio/audio.json"));
|
|
let sr = conf["sample_rate"].as_u64().unwrap() as u32;
|
|
let payload = read("int_audio/audio_bitstream.wia1");
|
|
assert_eq!(wia1_from_json(&js, sr).unwrap(), payload, "the converter writes the committed payload");
|
|
let (a, j) = (wire::parse_wia1(&payload).unwrap(), AudioJson::from_value(&js).unwrap());
|
|
assert_eq!((a.afrac as u32, a.sr, a.n_ch as usize, a.lat_len as usize, a.t_out as usize, a.offset),
|
|
(j.afrac, sr, j.n_ch, j.lat_len, j.t_out, j.offset));
|
|
assert_eq!((&a.median_q, &a.cdfs, a.rans), (&j.median_q, &j.cdfs, j.rans.as_slice()));
|
|
let model = read("int_audio/audio_model.bin");
|
|
let params = unpinned(crate::codecs::CAP_AUDIO_INT_CODEC, HeldBytes::File(&model));
|
|
let pcm = same_under_every_policy("audio", || match decode_int(crate::codecs::CAP_AUDIO_INT_CODEC, &payload, ¶ms) {
|
|
Ok(IntDecoded::Pcm { samples, sample_rate: 16_000, channels: 1 }) => samples,
|
|
other => panic!("audio: {other:?}"),
|
|
});
|
|
assert_eq!(pcm, decode(&AudioModel::from_bytes(&model).unwrap(), &js).unwrap(), "the binary and JSON forms decode alike");
|
|
// Anchored below to output recorded before this revision: the sidecar and the
|
|
// export's FNV-1a (`audio.json`).
|
|
let s16le: Vec<u8> = pcm.iter().flat_map(|s| s.to_le_bytes()).collect();
|
|
assert_eq!(sha(&s16le), jload("int_audio/sha256.json")["audio"]["sha256"].as_str().unwrap());
|
|
assert_eq!(format!("{:016x}", pcm_fnv1a(&pcm)), conf["pcm_fnv1a"].as_str().unwrap());
|
|
}
|
|
|
|
/// §6.1 test 17, `WIS1`, with the presentation ranges the JSON states: the payload and
|
|
/// its JSON form decode alike through the typed decoder, to the attributes recorded
|
|
/// before this revision.
|
|
#[cfg(feature = "splat_int")]
|
|
#[test]
|
|
fn wis1_json_equivalence() {
|
|
use crate::int_hyper_synth::{fnv1a, IntModel};
|
|
use crate::int_splat::{decode, SplatJson};
|
|
let js = jload("int_splat/splat.json");
|
|
let payload = read("int_splat/splat.wis1");
|
|
assert_eq!(wis1_from_json(&js).unwrap(), payload, "the converter writes the committed payload");
|
|
let (v, j) = (wire::parse_wis1(&payload).unwrap(), SplatJson::from_value(&js).unwrap());
|
|
assert_eq!((v.afrac as u32, v.c as usize, v.sh as usize, v.sw as usize, v.s as usize, v.attr as usize, v.n_gaussians as usize),
|
|
(j.afrac, j.c, j.sh, j.sw, j.s, j.n_attr, j.n_gaussians));
|
|
assert_eq!((&v.cdfs, &v.offsets, &v.median_q, v.rans), (&j.cdfs, &j.offsets, &j.median_q, j.rans.as_slice()));
|
|
for (&(lo, hi), &(jl, jh)) in v.ranges.iter().zip(&j.ranges) {
|
|
assert_eq!((lo, hi), (jl as f32, jh as f32), "the ranges are the JSON's binary32 values");
|
|
}
|
|
let gs = read("int_splat/splat_gs.bin");
|
|
let params = unpinned(crate::codecs::CAP_SPLAT_INT_CODEC, HeldBytes::File(&gs));
|
|
let (bytes, ranges) = same_under_every_policy("splat", || match decode_int(crate::codecs::CAP_SPLAT_INT_CODEC, &payload, ¶ms) {
|
|
Ok(IntDecoded::Attr { bytes, n_attr: 14, s: 32, n_gaussians: 1024, ranges }) => (bytes, ranges),
|
|
other => panic!("splat: {other:?}"),
|
|
});
|
|
assert_eq!(ranges, v.ranges);
|
|
assert_eq!(bytes, decode(&IntModel::from_bytes(&gs).unwrap(), &js).unwrap().attr, "the binary and JSON forms decode alike");
|
|
// Anchored below to output recorded before this revision: the sidecar and the
|
|
// export's FNV-1a (`attr_hash`).
|
|
assert_eq!(sha(&bytes), jload("int_splat/sha256.json")["splat"]["sha256"].as_str().unwrap());
|
|
assert_eq!(format!("{:016x}", fnv1a(&bytes)), js["attr_hash"].as_str().unwrap());
|
|
}
|
|
|
|
/// §6.1 test 18: every integer capability decodes through `decode_int` in a build
|
|
/// without the neural sink (`neural`, which pulls the ONNX runtime), to its pinned
|
|
/// hashes: the five pinned formats, `wai.neural.int_mlicpp`'s committed envelopes (the
|
|
/// form-less pin and the listing pin, verified in memory) and the `wai.neural.int_synth`
|
|
/// corpus.
|
|
#[cfg(all(feature = "video_int", feature = "audio_int", feature = "splat_int", not(feature = "neural")))]
|
|
#[test]
|
|
fn decode_int_without_neural() {
|
|
use crate::container::Wai;
|
|
use crate::codecs as c;
|
|
let set = int_hyper_set();
|
|
let files = view(&set);
|
|
let hyp = unpinned(c::CAP_NEURAL_INT_HYPER, HeldBytes::Set { listing: b"", files: &files });
|
|
let (model, gs) = (read("int_audio/audio_model.bin"), read("int_splat/splat_gs.bin"));
|
|
let cases: [(&str, &str, IntParams<'_>); 6] = [
|
|
(c::CAP_VIDEO_INT_MOTION, "int_video/int_video_bitstream.wiv1", IntParams::none()),
|
|
(c::CAP_VIDEO_INT_MOTION, "int_video/video_real_bitstream.wiv1", IntParams::none()),
|
|
(c::CAP_VIDEO_INT_HYPER, "int_video/video_nk_bitstream.wiv1", hyp.clone()),
|
|
(c::CAP_NEURAL_INT_HYPER, "int_hyper/tiny_bitstream.wih1", hyp.clone()),
|
|
(c::CAP_AUDIO_INT_CODEC, "int_audio/audio_bitstream.wia1", unpinned(c::CAP_AUDIO_INT_CODEC, HeldBytes::File(&model))),
|
|
(c::CAP_SPLAT_INT_CODEC, "int_splat/splat.wis1", unpinned(c::CAP_SPLAT_INT_CODEC, HeldBytes::File(&gs))),
|
|
];
|
|
for (cap, rel, params) in cases {
|
|
assert!(decode_int(cap, &read(rel), ¶ms).is_ok(), "{cap} {rel}");
|
|
}
|
|
|
|
// wai.neural.int_mlicpp: the form-less pin over the bundle, and the listing pin,
|
|
// each verified in memory by resolve, decode to the committed image.
|
|
let d = vroot().join("int_mlicpp");
|
|
let rd = |n: &str| std::fs::read(d.join(n)).unwrap();
|
|
let want: serde_json::Value = serde_json::from_slice(&rd("expected.json")).unwrap();
|
|
let (bundle, listing) = (rd("model.wmb"), rd("model.sha256"));
|
|
let set_files: BTreeMap<String, &[u8]> = [("model.wmb".to_string(), bundle.as_slice())].into_iter().collect();
|
|
for (env, held) in [("stream.wai", HeldBytes::File(&bundle)), ("stream_set.wai", HeldBytes::Set { listing: &listing, files: &set_files })] {
|
|
let env = Wai::from_bytes(&rd(env)).unwrap();
|
|
let held = [held];
|
|
let params = resolve(c::CAP_NEURAL_INT_MLICPP, env.prior(), &held, None).expect("the pin verifies");
|
|
assert!(params.is_pinned());
|
|
match decode_int(c::CAP_NEURAL_INT_MLICPP, &env.payload, ¶ms) {
|
|
Ok(IntDecoded::Image { rgb, .. }) => assert_eq!(sha(&rgb), want["rgb_sha256"].as_str().unwrap()),
|
|
other => panic!("int_mlicpp: {other:?}"),
|
|
}
|
|
}
|
|
|
|
// wai.neural.int_synth: the neural-conformance corpus, by its blake3 RGB hash.
|
|
let dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../neural-conformance");
|
|
let manifest: serde_json::Value = serde_json::from_slice(&std::fs::read(dir.join("expected_hashes.json")).unwrap()).unwrap();
|
|
let mut n = 0;
|
|
for (name, spec) in manifest.as_object().unwrap() {
|
|
let vd = dir.join("vectors").join(name);
|
|
let (payload, model) = (std::fs::read(vd.join("latent.bin")).unwrap(), std::fs::read(vd.join("int_synth.json")).unwrap());
|
|
let rgb = match decode_int(c::CAP_NEURAL_INT_SYNTH, &payload, &unpinned(c::CAP_NEURAL_INT_SYNTH, HeldBytes::File(&model))) {
|
|
Ok(IntDecoded::Image { rgb, .. }) => rgb,
|
|
other => panic!("{name}: {other:?}"),
|
|
};
|
|
let mut h = blake3::Hasher::new();
|
|
h.update(b"wai:int-synth-rgb\x01");
|
|
h.update(&rgb);
|
|
assert_eq!(h.finalize().to_hex().as_str(), spec["rgb_hash"].as_str().unwrap(), "{name}");
|
|
n += 1;
|
|
}
|
|
assert!(n >= 4, "the four committed int_synth vectors, found {n}");
|
|
}
|
|
|
|
/// The refusal list is closed: every code distinct, snake_case, and read back.
|
|
#[test]
|
|
fn refusal_codes_are_closed_and_distinct() {
|
|
let mut codes: Vec<&str> = Refusal::ALL.iter().map(|r| r.code()).collect();
|
|
for c in &codes {
|
|
assert!(c.bytes().all(|b| b.is_ascii_lowercase() || b == b'_'), "{c} is snake_case");
|
|
assert_eq!(Refusal::from_code(c).map(Refusal::code), Some(*c));
|
|
}
|
|
codes.sort_unstable();
|
|
codes.dedup();
|
|
assert_eq!(codes.len(), Refusal::ALL.len(), "codes are distinct");
|
|
assert_eq!(Refusal::from_code("prior_not_held"), None);
|
|
}
|
|
|
|
/// A JSON form under each pinned string is refused as `bad_magic`, before any
|
|
/// parameter is read (the parameters here are absent, and would be `set_incomplete`).
|
|
#[test]
|
|
fn json_form_is_refused_before_any_parameter_is_read() {
|
|
for (cap, json) in [
|
|
(crate::codecs::CAP_VIDEO_INT_MOTION, "int_video/int_video_bitstream.json"),
|
|
(crate::codecs::CAP_VIDEO_INT_HYPER, "int_video/video_nk_bitstream.json"),
|
|
(crate::codecs::CAP_NEURAL_INT_HYPER, "int_hyper/tiny_bitstream.json"),
|
|
(crate::codecs::CAP_AUDIO_INT_CODEC, "int_audio/audio_bitstream.json"),
|
|
(crate::codecs::CAP_SPLAT_INT_CODEC, "int_splat/splat.json"),
|
|
] {
|
|
let built = match cap {
|
|
crate::codecs::CAP_NEURAL_INT_HYPER => true,
|
|
crate::codecs::CAP_AUDIO_INT_CODEC => cfg!(feature = "audio_int"),
|
|
crate::codecs::CAP_SPLAT_INT_CODEC => cfg!(feature = "splat_int"),
|
|
_ => cfg!(feature = "video_int"),
|
|
};
|
|
let want = if built { "bad_magic" } else { "unsupported_capability" };
|
|
assert_eq!(decode_int(cap, &read(json), &IntParams::none()).map(|_| ()).unwrap_err().code(), want, "{cap}");
|
|
}
|
|
}
|
|
|
|
/// The refusals that concern the capability or the set rather than the payload's bytes.
|
|
#[test]
|
|
fn capability_and_set_refusals() {
|
|
let code = |r: Result<IntDecoded, IntPayloadError>| r.map(|_| "decoded").unwrap_or_else(|e| e.code());
|
|
// A candidate is not dispatchable (SPEC §4); a dispatchable non-integer capability
|
|
// has no integer decoder.
|
|
assert_eq!(code(decode_int(crate::codecs::CAP_SPLAT_SOG, b"WIS1", &IntParams::none())), "not_dispatchable");
|
|
assert_eq!(code(decode_int(crate::codecs::CAP_IMAGE_PNG, b"\x89PNG", &IntParams::none())), "unsupported_capability");
|
|
// The conditional codec reads three files: a set without h_s.whm is incomplete,
|
|
// and the refusal names the file.
|
|
let mut set = int_hyper_set();
|
|
set.remove("h_s.whm");
|
|
let files = view(&set);
|
|
let r = decode_int(crate::codecs::CAP_NEURAL_INT_HYPER, &read("int_hyper/tiny_bitstream.wih1"),
|
|
&unpinned(crate::codecs::CAP_NEURAL_INT_HYPER, HeldBytes::Set { listing: b"", files: &files }));
|
|
assert_eq!(r.as_ref().map_err(|e| (e.code(), e.detail())).err(), Some(("set_incomplete", "h_s.whm")));
|
|
// One file cannot hold a several-file set.
|
|
let one = read("int_hyper/tiny_g_s.bin");
|
|
assert_eq!(code(decode_int(crate::codecs::CAP_NEURAL_INT_HYPER, &read("int_hyper/tiny_bitstream.wih1"),
|
|
&unpinned(crate::codecs::CAP_NEURAL_INT_HYPER, HeldBytes::File(&one)))), "set_incomplete");
|
|
// Entropy tables that break the table rules are the model's fault.
|
|
let mut bad = int_hyper_set();
|
|
bad.insert("entropy.json".into(), br#"{"scale_edges":[],"cdfs":[],"cdf_lengths":[],"offsets":[],"z_cdfs":[],"z_cdf_lengths":[],"z_offsets":[],"z_median_q":[]}"#.to_vec());
|
|
let files = view(&bad);
|
|
assert_eq!(code(decode_int(crate::codecs::CAP_NEURAL_INT_HYPER, &read("int_hyper/tiny_bitstream.wih1"),
|
|
&unpinned(crate::codecs::CAP_NEURAL_INT_HYPER, HeldBytes::Set { listing: b"", files: &files }))), "model_mismatch");
|
|
// int_mlicpp: a payload its decoder refuses, and a bundle that is not one.
|
|
let bundle = std::fs::read(vroot().join("int_mlicpp/model.wmb")).unwrap();
|
|
assert_eq!(code(decode_int(crate::codecs::CAP_NEURAL_INT_MLICPP, b"WIM1", &unpinned(crate::codecs::CAP_NEURAL_INT_MLICPP, HeldBytes::File(&bundle)))), "payload_malformed");
|
|
assert_eq!(code(decode_int(crate::codecs::CAP_NEURAL_INT_MLICPP, b"WIM1", &unpinned(crate::codecs::CAP_NEURAL_INT_MLICPP, HeldBytes::File(b"WHM1")))), "model_mismatch");
|
|
// int_synth: a side of 2^64 is refused, not shifted.
|
|
let mut p = vec![0u8; 12];
|
|
p[8] = 1;
|
|
p[10] = 64;
|
|
let spec = std::fs::read(PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../neural-conformance/vectors/gradient/int_synth.json")).unwrap();
|
|
assert_eq!(code(decode_int(crate::codecs::CAP_NEURAL_INT_SYNTH, &p, &unpinned(crate::codecs::CAP_NEURAL_INT_SYNTH, HeldBytes::File(&spec)))), "payload_malformed");
|
|
}
|
|
|
|
/// `container::check_int_payload_framing` (what every emit path, `wai wrap` and `wai
|
|
/// inspect` run, with no `neural_int`) is the wire parser with the capability's
|
|
/// keyframe kind: it refuses every payload with the parser's code, a keyframe-0 `WIV1`
|
|
/// under `wai.video.int_hyper` and a `WIV1` cut inside its header among them, and
|
|
/// accepts what the parser accepts.
|
|
#[test]
|
|
fn framing_check_is_the_parser() {
|
|
let k0 = read("int_video/int_video_bitstream.wiv1");
|
|
let payloads: Vec<Vec<u8>> = vec![
|
|
k0.clone(), k0[..40].to_vec(), read("int_video/video_nk_bitstream.wiv1"), read("int_hyper/tiny_bitstream.wih1"),
|
|
read("int_audio/audio_bitstream.wia1"), read("int_splat/splat.wis1"),
|
|
read("int_splat/splat.json"), b"WIV1".to_vec(), b"WIH1\x02".to_vec(), b"WI".to_vec(), Vec::new(),
|
|
];
|
|
let mut refused = std::collections::BTreeSet::new();
|
|
for &(cap, magic) in crate::codecs::PINNED_INT_PAYLOADS {
|
|
for p in &payloads {
|
|
let parsed = match &magic {
|
|
b"WIV1" => wire::parse_wiv1(p, Some((cap == crate::codecs::CAP_VIDEO_INT_HYPER) as u8)).err(),
|
|
b"WIH1" => wire::parse_wih1(p).err(),
|
|
b"WIA1" => wire::parse_wia1(p).err(),
|
|
_ => wire::parse_wis1(p).err(),
|
|
};
|
|
let framed = crate::container::check_int_payload_framing(cap, p).err();
|
|
assert_eq!(framed.as_ref().map(|e| (e.code, e.detail)), parsed.map(|e| (e.code(), e.detail)), "{cap}, {} bytes", p.len());
|
|
refused.extend(framed.map(|e| e.code));
|
|
}
|
|
}
|
|
for code in ["bad_magic", "format_capability_mismatch", "keyframe_capability_mismatch", "version", "stream_length"] {
|
|
assert!(refused.contains(code), "no case gave {code}");
|
|
}
|
|
let hyp = crate::codecs::CAP_VIDEO_INT_HYPER;
|
|
assert_eq!(crate::container::check_int_payload_framing(hyp, &k0).unwrap_err().code, "keyframe_capability_mismatch");
|
|
assert_eq!(crate::container::check_int_payload_framing(crate::codecs::CAP_VIDEO_INT_MOTION, &k0[..40]).unwrap_err().code, "stream_length");
|
|
assert_eq!(crate::container::check_int_payload_framing(crate::codecs::CAP_IMAGE_PNG, b"{}"), Ok(()));
|
|
}
|
|
|
|
/// A key log stand-in: revokes the digests in `revoked`; where `held_to`, a set it
|
|
/// does not grant (`granted`) is not authorised.
|
|
#[derive(Debug)]
|
|
struct Authority {
|
|
revoked: Vec<String>,
|
|
granted: Vec<String>,
|
|
held_to: bool,
|
|
}
|
|
|
|
impl crate::pin::PinAuthority for Authority {
|
|
fn check(&self, capability: &str, prior: &Prior) -> Result<(), PinRefusal> {
|
|
self.check_unpinned(capability, std::slice::from_ref(&prior.sha256))
|
|
}
|
|
fn check_unpinned(&self, capability: &str, digests: &[String]) -> Result<(), PinRefusal> {
|
|
if digests.iter().any(|d| self.revoked.contains(d)) {
|
|
return Err(PinRefusal::Revoked { capability: capability.into(), revoked_by: None });
|
|
}
|
|
if self.held_to && !digests.iter().any(|d| self.granted.contains(d)) {
|
|
return Err(PinRefusal::NotAuthorised { capability: capability.into(), check: None, reason: None });
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// SPEC §3.1 check 5 through the ungated path: a revoked set and an unauthorised set
|
|
/// are refused, pinned and unpinned; with no key log, or with one that grants the set,
|
|
/// the same set resolves.
|
|
#[test]
|
|
fn check_5_through_the_ungated_path() {
|
|
use crate::codecs::{CAP_AUDIO_INT_CODEC as AUD, CAP_NEURAL_INT_HYPER as HYP, CAP_PRIOR_SET};
|
|
let set = int_hyper_set();
|
|
let files = view(&set);
|
|
let listing = crate::pin::listing_from(&files).unwrap();
|
|
let digest = crate::pin::sha256_hex(&listing);
|
|
let held = [HeldBytes::Set { listing: &listing, files: &files }];
|
|
let pin = Prior::new("tiny", digest.clone(), "decode-equivalence").with_form(CAP_PRIOR_SET);
|
|
let code = |r: Result<IntParams<'_>, PinRefusal>| r.map(|p| if p.is_pinned() { "pinned" } else { "unpinned" }).unwrap_or_else(|e| e.code());
|
|
let revoking = Authority { revoked: vec![digest.clone()], granted: vec![], held_to: false };
|
|
let ungranting = Authority { revoked: vec![], granted: vec![], held_to: true };
|
|
let granting = Authority { revoked: vec![], granted: vec![digest.clone()], held_to: true };
|
|
for (prior, ok) in [(Some(&pin), "pinned"), (None, "unpinned")] {
|
|
assert_eq!(code(resolve(HYP, prior, &held, None)), ok);
|
|
assert_eq!(code(resolve(HYP, prior, &held, Some(&revoking))), "prior_revoked");
|
|
assert_eq!(code(resolve(HYP, prior, &held, Some(&ungranting))), "prior_not_authorised");
|
|
assert_eq!(code(resolve(HYP, prior, &held, Some(&granting))), ok);
|
|
}
|
|
// The pinned decode reads the files its listing names, and decodes.
|
|
let p = resolve(HYP, Some(&pin), &held, Some(&granting)).unwrap();
|
|
assert!(decode_int(HYP, &read("int_hyper/tiny_bitstream.wih1"), &p).is_ok());
|
|
// A one-file set, unpinned: checked under its file's digest.
|
|
let model = read("int_audio/audio_model.bin");
|
|
let one = [HeldBytes::File(&model)];
|
|
let revoke_model = Authority { revoked: vec![crate::pin::sha256_hex(&model)], granted: vec![], held_to: false };
|
|
assert_eq!(code(resolve(AUD, None, &one, Some(&revoke_model))), "prior_revoked");
|
|
assert_eq!(code(resolve(AUD, None, &one, None)), "unpinned");
|
|
// A capability that reads no set needs none, and no key log is consulted.
|
|
assert!(resolve(crate::codecs::CAP_VIDEO_INT_MOTION, None, &[], Some(&revoking)).is_ok_and(|p| p.is_none()));
|
|
// An unpinned decode with no set held.
|
|
assert_eq!(code(resolve(HYP, None, &[], None)), "prior_not_held");
|
|
}
|
|
|
|
/// An unpinned decode is checked under the bytes it reads (SPEC §4, *Module
|
|
/// authorisation*): the tiny set is refused under its own listing, and the same files
|
|
/// held with an empty listing, or beside a file the decode does not read, are refused
|
|
/// too, under the listing of the members read. A listing that does not match its files,
|
|
/// or is malformed, is refused before check 5; the decode reads only the members a
|
|
/// listing names.
|
|
#[test]
|
|
fn unpinned_check_5_covers_the_files_the_decode_reads() {
|
|
use crate::pin::{listing_from, sha256_hex};
|
|
let hyp = crate::codecs::CAP_NEURAL_INT_HYPER;
|
|
let set = int_hyper_set();
|
|
let files = view(&set);
|
|
let listing = listing_from(&files).unwrap();
|
|
let revoking = Authority { revoked: vec![sha256_hex(&listing)], granted: vec![], held_to: false };
|
|
let code = |r: Result<IntParams<'_>, PinRefusal>| r.map(|_| "ok").unwrap_or_else(|e| e.code());
|
|
fn held<'a>(listing: &'a [u8], files: &'a BTreeMap<String, &'a [u8]>) -> HeldBytes<'a> {
|
|
HeldBytes::Set { listing, files }
|
|
}
|
|
let tiny = read("int_hyper/tiny_bitstream.wih1");
|
|
let own = listing.as_slice();
|
|
assert_eq!(code(resolve(hyp, None, &[held(own, &files)], Some(&revoking))), "prior_revoked", "its own listing");
|
|
assert_eq!(code(resolve(hyp, None, &[held(b"", &files)], Some(&revoking))), "prior_revoked", "no listing");
|
|
let mut extra = set.clone();
|
|
extra.insert("notes.txt".into(), b"not a member".to_vec());
|
|
let extra = view(&extra);
|
|
assert_eq!(code(resolve(hyp, None, &[held(b"", &extra)], Some(&revoking))), "prior_revoked", "an unread file beside it");
|
|
// With no key log, the same set decodes.
|
|
let p = resolve(hyp, None, &[held(b"", &files)], None).unwrap();
|
|
assert!(!p.is_pinned());
|
|
assert!(decode_int(hyp, &tiny, &p).is_ok());
|
|
// A listing whose file is not the one held, and a malformed listing.
|
|
let mut tampered = set.clone();
|
|
tampered.get_mut("g_s.whm").unwrap().push(0);
|
|
let tampered = view(&tampered);
|
|
assert_eq!(code(resolve(hyp, None, &[held(own, &tampered)], None)), "part_mismatch");
|
|
assert_eq!(code(resolve(hyp, None, &[held(b"not a listing\n", &files)], None)), "set_malformed");
|
|
// A listing of two of the three members: the decode reads those two only.
|
|
let two: BTreeMap<String, &[u8]> = files.iter().filter(|(k, _)| k.as_str() != "h_s.whm").map(|(k, v)| (k.clone(), *v)).collect();
|
|
let two = listing_from(&two).unwrap();
|
|
let p = resolve(hyp, None, &[held(&two, &files)], None).unwrap();
|
|
let r = decode_int(hyp, &tiny, &p);
|
|
assert_eq!(r.as_ref().map_err(|e| (e.code(), e.detail())).err(), Some(("set_incomplete", "h_s.whm")));
|
|
}
|
|
|
|
/// The same refusals from the key log a sink holds (`key_log::SinkKeyLog`), as the
|
|
/// neural sink's `int_mlicpp_revoked_set_is_refused_through_the_key_log` checks them.
|
|
#[cfg(feature = "key_discovery")]
|
|
#[test]
|
|
fn check_5_with_a_sink_key_log() {
|
|
use crate::key_log::{commitment, Anchor, KeyEntry, KeyStatus, ModuleForm, ModuleGrant, Revision, SinkKeyLog};
|
|
use ed25519_dalek::SigningKey;
|
|
const P: &str = "https://publisher.example";
|
|
let hyp = crate::codecs::CAP_NEURAL_INT_HYPER;
|
|
let key = |s: u8| SigningKey::from_bytes(&[s; 32]);
|
|
let set = int_hyper_set();
|
|
let files = view(&set);
|
|
let listing = crate::pin::listing_from(&files).unwrap();
|
|
let digest = crate::pin::sha256(&listing);
|
|
let held = [HeldBytes::Set { listing: &listing, files: &files }];
|
|
let pin = Prior::new("tiny", hex(&digest), "decode-equivalence").with_form(crate::codecs::CAP_PRIOR_SET);
|
|
let sink = |revoked: Vec<[u8; 32]>, statuses: bool| {
|
|
let unsigned = Revision {
|
|
origin: P.into(), seq: 0, prev: None, issued_at: "2026-09-01T00:00:00Z".into(), root: [0; 32],
|
|
next_root: commitment(&key(0x62).verifying_key().to_bytes()),
|
|
keys: vec![KeyEntry::active("modd", key(0x6b).verifying_key().to_bytes(), &["module/decoder"])],
|
|
revoked_modules: vec![], grant_max_s: 3_600, status_max_age_s: 600, sig: [0; 64],
|
|
};
|
|
let r0 = Revision::seal(&key(0x61), unsigned.clone()).unwrap();
|
|
let r1 = Revision::seal(&key(0x61), Revision { seq: 1, prev: Some(r0.revision_hash()),
|
|
issued_at: "2026-09-10T00:00:00Z".into(), revoked_modules: revoked, ..unsigned }).unwrap();
|
|
let grant = ModuleGrant::seal(&key(0x6b), "did:web:publisher.example#modd", P, digest, ModuleForm::Decoder, &[hyp],
|
|
"2026-09-20T05:30:00Z", "2026-09-20T06:30:00Z").unwrap().to_json();
|
|
let st = if statuses { vec![KeyStatus::seal(&key(0x61), &r1, "2026-09-20T05:55:00Z").to_json()] } else { vec![] };
|
|
SinkKeyLog::new(vec![r0.clone(), r1], vec![Anchor::pinned(P, r0.revision_hash())])
|
|
.with_statuses(st).with_grants(vec![grant]).at("2026-09-20T06:00:00Z").hold_pinned_sets(hyp, &[P])
|
|
};
|
|
let code = |r: Result<IntParams<'_>, PinRefusal>| r.map(|_| "ok").unwrap_or_else(|e| e.code());
|
|
for prior in [Some(&pin), None] {
|
|
assert_eq!(code(resolve(hyp, prior, &held, Some(&sink(vec![digest], true)))), "prior_revoked");
|
|
assert_eq!(code(resolve(hyp, prior, &held, Some(&sink(vec![], false)))), "prior_not_authorised");
|
|
assert_eq!(code(resolve(hyp, prior, &held, Some(&sink(vec![], true)))), "ok");
|
|
}
|
|
}
|
|
|
|
/// A pinned set answers only for the paths its listing names, and a form-less pin's
|
|
/// one file only for the capability's one member.
|
|
#[test]
|
|
fn a_verified_set_answers_only_for_what_it_verified() {
|
|
use crate::codecs::CAP_PRIOR_SET;
|
|
let mut set = int_hyper_set();
|
|
let listing = crate::pin::listing_from(&view(&set)).unwrap();
|
|
set.insert("extra.bin".into(), b"unverified".to_vec());
|
|
let files = view(&set);
|
|
let hyp = crate::codecs::CAP_NEURAL_INT_HYPER;
|
|
let pin = Prior::new("tiny", crate::pin::sha256_hex(&listing), "decode-equivalence").with_form(CAP_PRIOR_SET);
|
|
let held = [HeldBytes::Set { listing: &listing, files: &files }];
|
|
let p = resolve(hyp, Some(&pin), &held, None).unwrap();
|
|
assert!(p.is_pinned());
|
|
assert!(p.member(hyp, "g_s.whm").is_ok());
|
|
assert_eq!(p.member(hyp, "extra.bin").unwrap_err().code(), "set_incomplete");
|
|
let aud = crate::codecs::CAP_AUDIO_INT_CODEC;
|
|
let one = [HeldBytes::File(b"WAM1")];
|
|
let p = resolve(aud, Some(&Prior::new("m", crate::pin::sha256_hex(b"WAM1"), "decode-equivalence")), &one, None).unwrap();
|
|
assert!(p.member(aud, "model.wam").is_ok());
|
|
assert!(p.member(hyp, "g_s.whm").is_err(), "one file is not a several-file set");
|
|
}
|
|
|
|
/// The entropy tables of a set obey the table rules of the extension's §2 (§7): a
|
|
/// length of 2 or 4098 is refused, as a payload's own table is, and 3 and 4097 are read.
|
|
#[test]
|
|
fn entropy_tables_obey_the_table_length_rule() {
|
|
use crate::int_hyper_synth::CondEntropy;
|
|
let table = |n: usize| -> Vec<i64> {
|
|
let mut t: Vec<i64> = (0..n as i64).collect();
|
|
t[n - 1] = 65_536;
|
|
t
|
|
};
|
|
let ent = |n: usize| serde_json::json!({
|
|
"scale_edges": [1], "cdfs": [table(n)], "cdf_lengths": [n], "offsets": [0],
|
|
"z_cdfs": [table(n)], "z_cdf_lengths": [n], "z_offsets": [0], "z_median_q": [0]
|
|
});
|
|
for n in [2, 4098] {
|
|
assert!(CondEntropy::try_from_value(&ent(n)).is_err(), "a table of {n} entries is refused");
|
|
}
|
|
for n in [3, 4097] {
|
|
assert!(CondEntropy::try_from_value(&ent(n)).is_ok(), "a table of {n} entries is read");
|
|
}
|
|
}
|
|
|
|
/// A network is planned in 64-bit arithmetic, layer by layer, so a size past the cap is
|
|
/// `too_large` however far past it is, on every target. Planning in `usize` refused a
|
|
/// transposed convolution of stride 2^31 on a side of 3 as malformed on a 32-bit sink
|
|
/// and as too large on a 64-bit one; the stride `usize::MAX / 2 + 1` (2^63 here) is the
|
|
/// same split on this target. A convolution padded past the cap is too large though
|
|
/// its output would be one element. Parameters that do not fit, or a padding that
|
|
/// leaves no output, are the model's. On the committed networks the plan is the shape
|
|
/// they run at.
|
|
#[test]
|
|
fn network_plans_refuse_alike_on_every_target() {
|
|
use crate::int_hyper_synth::{net_shape, plan_net, IntLayer, IntModel, RefusalKind as K};
|
|
let plan = |m: &IntModel, c, h, w| plan_net(m, c, h, w).map(|p| p.out);
|
|
let net = |layer: IntLayer| IntModel { afrac: 8, gf: 8, shift: 8, layers: vec![layer] };
|
|
let convt = |stride: usize, pad: usize| net(IntLayer::ConvT { out_ch: 1, k: 1, stride, pad, opad: 0, weight: vec![1], mult: vec![1], bias: vec![0] });
|
|
let kind = |r: Result<(usize, usize, usize), crate::int_hyper_synth::Refused>| r.map_err(|e| e.kind);
|
|
for stride in [1usize << 31, usize::MAX / 2 + 1, usize::MAX] {
|
|
assert_eq!(kind(plan(&convt(stride, 0), 1, 3, 3)), Err(K::TooLarge), "stride {stride}");
|
|
}
|
|
let conv = net(IntLayer::Conv { out_ch: 1, k: 1, stride: usize::MAX, pad: usize::MAX / 4, weight: vec![1], mult: vec![1], bias: vec![0] });
|
|
assert_eq!(kind(plan(&conv, 1, 3, 3)), Err(K::TooLarge));
|
|
assert_eq!(kind(plan(&convt(1, 2), 1, 3, 3)), Err(K::ModelMismatch), "padding that leaves no output");
|
|
let short = net(IntLayer::ConvT { out_ch: 2, k: 1, stride: 1, pad: 0, opad: 0, weight: vec![1], mult: vec![1, 1], bias: vec![0, 0] });
|
|
assert_eq!(kind(plan(&short, 1, 3, 3)), Err(K::ModelMismatch), "weights that do not fit");
|
|
assert_eq!(kind(plan(&convt(2, 0), 1, 3, 3)), Ok((1, 5, 5)));
|
|
for (file, c, h, w) in [("int_hyper/tiny_g_s.bin", 4, 4, 4), ("int_hyper/tiny_h_s.bin", 2, 2, 2), ("int_splat/splat_gs.bin", 32, 8, 8)] {
|
|
let m = IntModel::from_bytes(&read(file)).unwrap();
|
|
assert_eq!(plan(&m, c, h, w).unwrap(), net_shape(&m, c, h, w).unwrap().out, "{file}");
|
|
}
|
|
}
|
|
|
|
/// The splat converter carries each presentation range exactly: a JSON decimal that
|
|
/// denotes a binary32 value converts to it, and one a single f64 unit away from it,
|
|
/// written as its own shortest decimal, is refused. `splat_int` reads numbers
|
|
/// correctly rounded, so no tolerance stands in for the reader.
|
|
#[cfg(feature = "splat_int")]
|
|
#[test]
|
|
fn splat_ranges_are_carried_exactly() {
|
|
let text = String::from_utf8(read("int_splat/splat.json")).unwrap();
|
|
let js: serde_json::Value = serde_json::from_str(&text).unwrap();
|
|
let lo0 = js["lo"][0].as_f64().unwrap();
|
|
assert_eq!((lo0 as f32) as f64, lo0, "the committed range is a binary32 value, read exactly");
|
|
assert!(wis1_from_json(&js).is_ok());
|
|
for step in [1i64, -1] {
|
|
let off = f64::from_bits(((lo0 as f32) as f64).to_bits().wrapping_add_signed(step));
|
|
let mut bad: serde_json::Value = serde_json::from_str(&text).unwrap();
|
|
bad["lo"][0] = serde_json::from_str(&format!("{off:?}")).unwrap();
|
|
assert_eq!(bad["lo"][0].as_f64(), Some(off), "the reader is correctly rounded");
|
|
assert_eq!(wis1_from_json(&bad).unwrap_err().code(), "field_range", "{off:?}");
|
|
}
|
|
}
|
|
|
|
/// The converters check the JSON's channel count against its tables: a medians list,
|
|
/// a table list or an offsets list one short is refused (`field_range`), never written
|
|
/// out of step with the header.
|
|
#[cfg(all(feature = "audio_int", feature = "splat_int"))]
|
|
#[test]
|
|
fn converters_check_the_channel_count() {
|
|
let audio = jload("int_audio/audio_bitstream.json");
|
|
for key in ["median_q", "cdfs"] {
|
|
let mut j = audio.clone();
|
|
j[key].as_array_mut().unwrap().pop();
|
|
let e = wia1_from_json(&j, 16_000).unwrap_err();
|
|
assert_eq!((e.code(), e.detail().contains("one per channel")), ("field_range", true), "audio {key}: {e}");
|
|
}
|
|
let splat = jload("int_splat/splat.json");
|
|
for key in ["median_q", "cdfs", "offsets"] {
|
|
let mut j = splat.clone();
|
|
j[key].as_array_mut().unwrap().pop();
|
|
let e = wis1_from_json(&j).unwrap_err();
|
|
assert_eq!((e.code(), e.detail().contains("one per channel")), ("field_range", true), "splat {key}: {e}");
|
|
}
|
|
}
|
|
|
|
/// `WHM1` and `WAM1` hold nothing after their last layer (§9): a committed network with
|
|
/// one byte appended is refused, and a decode against it is `model_mismatch`.
|
|
#[test]
|
|
fn a_model_file_with_bytes_after_its_last_layer_is_refused() {
|
|
use crate::int_hyper_synth::IntModel;
|
|
let mut g_s = read("int_hyper/tiny_g_s.bin");
|
|
assert!(IntModel::from_bytes(&g_s).is_ok());
|
|
g_s.push(0);
|
|
assert_eq!(IntModel::from_bytes(&g_s).err(), Some("bytes follow the last layer"));
|
|
let mut set = int_hyper_set();
|
|
set.insert("g_s.whm".into(), g_s);
|
|
let files = view(&set);
|
|
let hyp = crate::codecs::CAP_NEURAL_INT_HYPER;
|
|
let r = decode_int(hyp, &read("int_hyper/tiny_bitstream.wih1"), &unpinned(hyp, HeldBytes::Set { listing: b"", files: &files }));
|
|
assert_eq!(r.map(|_| ()).unwrap_err().code(), "model_mismatch");
|
|
#[cfg(feature = "audio_int")]
|
|
{
|
|
let mut wam = read("int_audio/audio_model.bin");
|
|
assert!(crate::int_audio::AudioModel::from_bytes(&wam).is_ok());
|
|
wam.push(0);
|
|
assert_eq!(crate::int_audio::AudioModel::from_bytes(&wam).err(), Some("bytes follow the last layer"));
|
|
}
|
|
}
|
|
|
|
/// Check 5 covers every identity of the bytes a pinned decode reads, not only the pin's
|
|
/// digest: a one-file set revoked under its file's digest is refused through a set-form
|
|
/// pin over a listing of that file, and a set revoked under the listing of its members
|
|
/// is refused through a pin over a listing that adds a file the decode does not read.
|
|
#[test]
|
|
fn a_pin_by_another_identity_does_not_reach_a_revoked_set() {
|
|
use crate::codecs::{CAP_AUDIO_INT_CODEC as AUD, CAP_NEURAL_INT_HYPER as HYP, CAP_PRIOR_SET};
|
|
use crate::pin::{listing_from, sha256_hex};
|
|
let code = |r: Result<IntParams<'_>, PinRefusal>| r.map(|p| if p.is_pinned() { "pinned" } else { "unpinned" }).unwrap_or_else(|e| e.code());
|
|
let model = read("int_audio/audio_model.bin");
|
|
let one: BTreeMap<String, &[u8]> = [("model.wam".to_string(), model.as_slice())].into_iter().collect();
|
|
let listing = listing_from(&one).unwrap();
|
|
let revoke_file = Authority { revoked: vec![sha256_hex(&model)], granted: vec![], held_to: false };
|
|
let set_pin = Prior::new("m", sha256_hex(&listing), "decode-equivalence").with_form(CAP_PRIOR_SET);
|
|
let held = [HeldBytes::Set { listing: &listing, files: &one }];
|
|
assert_eq!(code(resolve(AUD, Some(&set_pin), &held, None)), "pinned");
|
|
assert_eq!(code(resolve(AUD, Some(&set_pin), &held, Some(&revoke_file))), "prior_revoked", "a set-form pin over the revoked file");
|
|
|
|
let set = int_hyper_set();
|
|
let three = listing_from(&view(&set)).unwrap();
|
|
let mut extra = set.clone();
|
|
extra.insert("README".into(), b"not a member".to_vec());
|
|
let extra_v = view(&extra);
|
|
let four = listing_from(&extra_v).unwrap();
|
|
let revoke_three = Authority { revoked: vec![sha256_hex(&three)], granted: vec![], held_to: false };
|
|
let pin4 = Prior::new("t", sha256_hex(&four), "decode-equivalence").with_form(CAP_PRIOR_SET);
|
|
let held4 = [HeldBytes::Set { listing: &four, files: &extra_v }];
|
|
assert_eq!(code(resolve(HYP, Some(&pin4), &held4, None)), "pinned");
|
|
assert_eq!(code(resolve(HYP, Some(&pin4), &held4, Some(&revoke_three))), "prior_revoked", "a pin over the members and a README");
|
|
// A key log that grants the pin's digest still authorises it.
|
|
let granting = Authority { revoked: vec![], granted: vec![sha256_hex(&four)], held_to: true };
|
|
assert_eq!(code(resolve(HYP, Some(&pin4), &held4, Some(&granting))), "pinned");
|
|
}
|
|
|
|
/// A one-frame clip has one header (§3): of the same one-pixel clip under six
|
|
/// `(mv_precision, block)` pairs, only `(1, 1)` parses; the others are `field_range`.
|
|
/// The converter writes a one-frame JSON clip with that header whatever the JSON
|
|
/// states, and the result decodes as the JSON does.
|
|
#[cfg(feature = "video_int")]
|
|
#[test]
|
|
fn a_one_frame_clip_has_one_header() {
|
|
let rans: Vec<u8> = [0u32, 4].iter().flat_map(|w| w.to_le_bytes()).collect();
|
|
let clip = |mv_precision: u8, block: u32| wire::write_wiv1(&wire::Wiv1 {
|
|
mv_precision, keyframe: 0, n_frames: 1, h: 1, w: 1, block, q: 1, offset: 0,
|
|
cdf: vec![0, 32_768, 49_152, 65_536], mvs: vec![], rans: &rans, wih1: None,
|
|
});
|
|
for (mvp, block) in [(1u8, 1u32), (1, 4), (1, 7), (1, u32::MAX), (2, 1), (2, 1 << 31)] {
|
|
let got = wire::parse_wiv1(&clip(mvp, block), Some(0)).map(|_| ()).map_err(|e| e.code());
|
|
assert_eq!(got, if (mvp, block) == (1, 1) { Ok(()) } else { Err("field_range") }, "mv_precision {mvp}, block {block}");
|
|
}
|
|
let js = serde_json::json!({
|
|
"n_frames": 1, "h": 1, "w": 1, "block": 8, "mv_precision": 2, "q": 1, "offset": 0,
|
|
"cdf": [0, 32_768, 49_152, 65_536], "mvs": [[]], "rans_b64": "AAAAAAQAAAA="
|
|
});
|
|
let p = wiv1_from_json(&js, None).unwrap();
|
|
let v = wire::parse_wiv1(&p, Some(0)).unwrap();
|
|
assert_eq!((v.mv_precision, v.block), (1, 1));
|
|
match decode_int(crate::codecs::CAP_VIDEO_INT_MOTION, &p, &IntParams::none()) {
|
|
Ok(IntDecoded::Video { frames, .. }) => assert_eq!(frames, crate::int_video::decode(&js).unwrap().frames),
|
|
other => panic!("{other:?}"),
|
|
}
|
|
}
|
|
|
|
/// An integer range is carried only where it is exactly a binary32 value: `2^60` is,
|
|
/// and converts to exactly that; `2^60 + 1` is not (an f64 would round it to `2^60`),
|
|
/// and is refused, as the Python verifier refuses it.
|
|
#[cfg(feature = "splat_int")]
|
|
#[test]
|
|
fn splat_integer_ranges_are_carried_exactly() {
|
|
let text = String::from_utf8(read("int_splat/splat.json")).unwrap();
|
|
let with = |n: &str| {
|
|
let mut j: serde_json::Value = serde_json::from_str(&text).unwrap();
|
|
let big: serde_json::Value = serde_json::from_str(n).unwrap();
|
|
j["lo"][0] = big.clone();
|
|
j["hi"][0] = big;
|
|
wis1_from_json(&j)
|
|
};
|
|
let p = with("1152921504606846976").unwrap();
|
|
assert_eq!(wire::parse_wis1(&p).unwrap().ranges[0], ((1u64 << 60) as f32, (1u64 << 60) as f32));
|
|
assert_eq!(with("1152921504606846977").unwrap_err().code(), "field_range");
|
|
assert_eq!(with("-1152921504606846977").unwrap_err().code(), "field_range");
|
|
}
|
|
|
|
/// The JSON reader refuses a frame whose motion vectors are not a list (a number, an
|
|
/// object), as the Python converter does, instead of reading it as an empty list.
|
|
#[cfg(feature = "video_int")]
|
|
#[test]
|
|
fn a_frame_entry_that_is_not_a_list_is_refused() {
|
|
let mut js = jload("int_video/int_video_bitstream.json");
|
|
assert!(crate::int_video::VideoJson::from_value(&js).is_ok());
|
|
for bad in [serde_json::json!(5), serde_json::json!({"dy": 0}), serde_json::json!(null)] {
|
|
js["mvs"][1] = bad.clone();
|
|
assert!(crate::int_video::VideoJson::from_value(&js).is_err(), "{bad}");
|
|
assert_eq!(wiv1_from_json(&js, None).unwrap_err().code(), "field_range", "{bad}");
|
|
}
|
|
}
|
|
|
|
/// `encode_wiv1_zero_motion` writes a `WIV1` keyframe-0 payload that `decode_int`
|
|
/// decodes to the encoder's reconstruction, frame by frame; at `q = 1` that is the
|
|
/// source itself, and at a coarser step every sample is within `q / 2` of it. A
|
|
/// one-frame clip carries no motion vector; a step of 0, an empty clip and a frame of
|
|
/// the wrong size are refused.
|
|
#[cfg(feature = "video_int")]
|
|
#[test]
|
|
fn encode_wiv1_zero_motion_round_trips() {
|
|
use crate::codecs::CAP_VIDEO_INT_MOTION;
|
|
let (h, w) = (9u32, 14u32);
|
|
let frames: Vec<Vec<u8>> = (0..4u32)
|
|
.map(|t| (0..h * w * 3).map(|i| ((i * 7 + t * 29 + (i % 5) * (i % 3) * 40) % 256) as u8).collect())
|
|
.collect();
|
|
for q in [1u32, 3, 8, 64] {
|
|
for n in [1usize, 4] {
|
|
let (payload, recon) = encode_wiv1_zero_motion(&frames[..n], h, w, q).unwrap();
|
|
let v = wire::parse_wiv1(&payload, Some(0)).unwrap();
|
|
assert_eq!((v.n_frames as usize, v.h, v.w, v.q), (n, h, w, q));
|
|
assert_eq!(v.mvs.len(), n - 1, "zero motion, one block per frame");
|
|
match decode_int(CAP_VIDEO_INT_MOTION, &payload, &IntParams::none()).unwrap() {
|
|
IntDecoded::Video { frames: got, width, height } => {
|
|
assert_eq!((width, height), (w, h));
|
|
assert_eq!(got, recon, "q {q}, {n} frames: the decode is the encoder's reconstruction");
|
|
}
|
|
other => panic!("{other:?}"),
|
|
}
|
|
for (src, r) in frames[..n].iter().zip(&recon) {
|
|
let worst = src.iter().zip(r).map(|(a, b)| a.abs_diff(*b) as u32).max().unwrap();
|
|
assert!(worst <= q / 2, "q {q}: an error of {worst}");
|
|
}
|
|
if q == 1 {
|
|
assert_eq!(recon, frames[..n].to_vec(), "q = 1 is lossless");
|
|
}
|
|
}
|
|
}
|
|
let code = |r: Result<(Vec<u8>, Vec<Vec<u8>>), IntPayloadError>| r.unwrap_err().code();
|
|
assert_eq!(code(encode_wiv1_zero_motion(&frames, h, w, 0)), "field_range");
|
|
assert_eq!(code(encode_wiv1_zero_motion(&[], h, w, 4)), "field_range");
|
|
assert_eq!(code(encode_wiv1_zero_motion(&frames, h, w + 1, 4)), "field_range");
|
|
// The caps, before anything is read or allocated: a frame past the tensor cap (its
|
|
// bytes are never looked at), more frames than the frame cap, a clip past the clip
|
|
// cap, and dimensions whose product overflows.
|
|
let one = vec![vec![0u8; 3]];
|
|
assert_eq!(code(encode_wiv1_zero_motion(&one, 4097, 5462, 4)), "too_large");
|
|
assert_eq!(code(encode_wiv1_zero_motion(&one, u32::MAX, u32::MAX, 4)), "too_large");
|
|
let many = vec![Vec::new(); (wire::MAX_FRAMES + 1) as usize];
|
|
assert_eq!(code(encode_wiv1_zero_motion(&many, 1, 1, 4)), "too_large");
|
|
let clip = vec![Vec::new(); 17];
|
|
assert_eq!(code(encode_wiv1_zero_motion(&clip, 4096, 5461, 4)), "too_large", "17 frames of a frame within the tensor cap pass the clip cap");
|
|
}
|
|
|
|
/// The digest `resolve` formed for check 5 (the SHA-256 of the listing of the members
|
|
/// read) keys the loaded set only for the members it was formed over: a decode through a
|
|
/// capability that reads other members hashes those.
|
|
#[test]
|
|
fn a_resolved_digest_keys_only_the_members_it_was_formed_over() {
|
|
use crate::int_set_cache::SetKey;
|
|
let set = int_hyper_set();
|
|
let v = view(&set);
|
|
let p = unpinned(CAP_NEURAL_INT_HYPER, HeldBytes::Set { listing: b"", files: &v });
|
|
let listing = |names: &[&str]| SetKey::listing_digest(&names.iter().map(|n| (n.to_string(), v[*n])).collect()).unwrap();
|
|
let three = listing(&["entropy.json", "g_s.whm", "h_s.whm"]);
|
|
assert_eq!(p.1, Some((set_members(CAP_NEURAL_INT_HYPER), three)), "resolve keeps the digest it formed");
|
|
assert_eq!(p.read_digest(CAP_NEURAL_INT_HYPER), Some(three));
|
|
assert_eq!(p.read_digest(CAP_VIDEO_INT_HYPER), Some(three), "the same three members");
|
|
assert_eq!(p.read_digest(CAP_SPLAT_INT_CODEC), Some(listing(&["g_s.whm"])), "another capability's one member is hashed");
|
|
assert_eq!(IntParams::none().read_digest(CAP_NEURAL_INT_HYPER), SetKey::listing_digest(&BTreeMap::new()));
|
|
}
|