open-standards/wai/wai-wasmq/tests/adversarial.rs
Transaction Science 2593c1f945
Some checks failed
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (push) Successful in 7s
wai-quantum wasmQ / does this change touch it (push) Successful in 10s
wai-quantum energy-landscape jobs / does this change touch it (push) Successful in 28s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (push) Failing after 54s
wai-quantum wasmQ / the reference modules rebuild to their modules of record · linux (push) Successful in 40s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (push) Failing after 1m16s
joule-moq-remote host agent / joule-moq-remote · does this change touch it (push) Has been cancelled
joule-moq-remote host agent / joule-moq-remote, every target (linux-arm64) (push) Has been cancelled
joule-moq-remote host agent / joule-moq-remote, every target (macos) (push) Has been cancelled
joule-moq-room room service / joule-moq-room · does this change touch it (push) Has been cancelled
joule-moq-room room service / joule-moq-room, every target (push) Has been cancelled
joule-moq-web browser client / joule-moq-web · does this change touch it (push) Has been cancelled
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (push) Has been cancelled
wai-app and Create / wai-app · does this change touch it (push) Has been cancelled
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (push) Has been cancelled
wai-app and Create / wai-app · Create, browser end-to-end (macos) (push) Has been cancelled
wai-audit collector and player integration / does this change touch it (push) Has been cancelled
wai-audit collector and player integration / collector · linux (push) Has been cancelled
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (push) Has been cancelled
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Has been cancelled
WAI operation catalogue / does this change touch it (push) Has been cancelled
WAI operation catalogue / the catalogue and every door · linux (push) Has been cancelled
wai-fleet service / does this change touch it (push) Has been cancelled
wai-fleet service / service · linux (push) Has been cancelled
wai-fleet service / service · macos (push) Has been cancelled
wai-ledger service / does this change touch it (push) Has been cancelled
wai-ledger service / service · linux (push) Has been cancelled
wai-ledger service / service · macos (push) Has been cancelled
wai-quantum for JavaScript / does this change touch it (push) Has been cancelled
wai-quantum for JavaScript / javascript · linux (push) Has been cancelled
wai-quantum studio and desktop / does this change touch it (push) Has been cancelled
wai-quantum studio and desktop / studio · browser (push) Has been cancelled
wai-quantum studio and desktop / desktop app · macos (push) Has been cancelled
wai-quantum studio and desktop / desktop app · linux (push) Has been cancelled
wai-quantum studio and desktop / desktop app · windows (push) Has been cancelled
wai-sink-android native sink library / does this change touch it (push) Has been cancelled
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (push) Has been cancelled
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (push) Has been cancelled
wai-sink-swift native sink package / does this change touch it (push) Has been cancelled
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (push) Has been cancelled
WAI Studio / does this change touch it (push) Has been cancelled
WAI Studio / the catalogue's actions, the unit tests and the core · linux (push) Has been cancelled
WAI Studio / Studio in Chromium and Firefox · macos (push) Has been cancelled
WAI Studio / Studio in WebKit · linux (push) Has been cancelled
wai-quantum wasmQ / profile, engines, reference modules, adversarial, vectors · macos (push) Successful in 2m13s
wai-quantum energy-landscape jobs / the module under wasm32-wasip2 (wasmtime) (push) Successful in 2m33s
wai-quantum wasmQ / interpreter and vectors under wasm32-wasip2 (wasmtime) (push) Successful in 2m38s
wai-quantum energy-landscape jobs / the same bits · macos (push) Successful in 4m11s
wai-quantum wasmQ / profile, engines, reference modules, adversarial, vectors · linux (push) Successful in 4m51s
wai-quantum energy-landscape jobs / native against WebAssembly, over many seeds (push) Successful in 6m22s
wai-quantum energy-landscape jobs / the same bits · linux-arm64 (push) Successful in 7m45s
wai-quantum energy-landscape jobs / reader, kernels, answers and the stated result · linux, release (push) Successful in 9m14s
wai-quantum command line / does this change touch it (push) Has been cancelled
wai-quantum command line / command line · linux (push) Has been cancelled
wai-quantum command line / command line · macos (push) Has been cancelled
wai-quantum contract / does this change touch it (push) Has been cancelled
wai-quantum contract / API compatibility · linux (push) Has been cancelled
wai-quantum contract / schemas and the committed tool lists · linux (push) Has been cancelled
wai-quantum contract / the policy is linked, and the deprecations page reads (push) Has been cancelled
wai-quantum docs, course and locales / does this change touch it (push) Has been cancelled
wai-quantum docs, course and locales / every command and snippet in the docs · linux (push) Has been cancelled
wai-quantum docs, course and locales / the course and the command line's languages · linux (push) Has been cancelled
wai-quantum docs, course and locales / the Studio's words in every locale · linux (push) Has been cancelled
wai-quantum editors / does this change touch it (push) Has been cancelled
wai-quantum editors / editors · linux (push) Has been cancelled
wai-quantum editors / editors · macos (push) Has been cancelled
wai-quantum resource estimation / does this change touch it (push) Has been cancelled
wai-quantum resource estimation / estimation and calibration · linux, release (push) Has been cancelled
wai-quantum other formats / does this change touch it (push) Has been cancelled
wai-quantum other formats / readers, writers and the set · linux (push) Has been cancelled
wai-quantum other formats / the set under wasm32-wasip2 (wasmtime) (push) Has been cancelled
wai-quantum OpenQASM reader / does this change touch it (push) Has been cancelled
wai-quantum OpenQASM reader / reader and corpus · linux (push) Has been cancelled
wai-quantum OpenQASM reader / corpus under wasm32-wasip2 (wasmtime) (push) Has been cancelled
wai-quantum pulse and dynamics / does this change touch it (push) Has been cancelled
wai-quantum pulse and dynamics / reader, simulator and pulse jobs · linux, release (push) Has been cancelled
wai-quantum pulse and dynamics / the same bits · linux-arm64 (push) Has been cancelled
wai-quantum pulse and dynamics / the same bits · macos (push) Has been cancelled
wai-quantum pulse and dynamics / reader, simulator and pulse jobs under wasm32-wasip2 (wasmtime) (push) Has been cancelled
wai-quantum for Python / does this change touch it (push) Has been cancelled
wai-quantum for Python / python · linux (push) Has been cancelled
wai-quantum for Python / python · macos (push) Has been cancelled
wai-quantum error correction and mitigation / does this change touch it (push) Has been cancelled
wai-quantum error correction and mitigation / codes, decoders, texts and mitigation · linux, release (push) Has been cancelled
wai-quantum extension points / does this change touch it (push) Has been cancelled
wai-quantum extension points / the registry and the example · linux (push) Has been cancelled
wai-quantum extension points / the registry and the example · linux-arm64 (push) Has been cancelled
wai-quantum extension points / the registry under wasm32-wasip2 (wasmtime) (push) Has been cancelled
wai-quantum service / does this change touch it (push) Has been cancelled
wai-quantum service / service · linux (push) Has been cancelled
wai-quantum service / service · macos (push) Has been cancelled
wai-quantum service / service energy · linux packages (push) Has been cancelled
wai-quantum variational and algorithm library / does this change touch it (push) Has been cancelled
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux, release (push) Has been cancelled
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux-arm64, release (push) Has been cancelled
wai-quantum verdicts and repairs / does this change touch it (push) Has been cancelled
wai-quantum verdicts and repairs / verdicts, repairs and the intent set · linux (push) Has been cancelled
wai-quantum verdicts and repairs / the intent set and the verdict under wasm32-wasip2 (wasmtime) (push) Has been cancelled
wai-quantum views in agent hosts / does this change touch it (push) Has been cancelled
wai-quantum views in agent hosts / views · the page and its engine (push) Has been cancelled
wai-quantum views in agent hosts / views · a scripted host (push) Has been cancelled
wai-quantum-landscape-fpga reference design / does this change touch it (push) Has been cancelled
wai-quantum-landscape-fpga reference design / the member against the model of the logic, and the logic against the model · linux, release (push) Has been cancelled
wai-quantum-landscape-fpga reference design / the board's code on the board's processor's architecture · linux-arm64, release (push) Has been cancelled
wai: wasmQ, an OpenQASM 3 program and the WebAssembly its externs call, run the same everywhere, metered and receipted
Re-landed from PR #659 (branch wai/quantum-wasmq, head 39fdb517a) as one
commit on main, under the land-it model. Five review rounds; the last had
no high or critical finding, and its mediums were fixed on the branch.

A hybrid quantum program runs classical code between its gates. OpenQASM 3
declares such a function with `extern` and leaves its body and linking
outside the language. wasmQ (wai.quantum.wasmq) packs the program and the
WebAssembly that defines its externs into one content-addressed WAI
envelope; a run gives the same result bytes and fuel on every host and
seals the engine's job receipt.

- extensions/quantum-wasmq.md (normative) and quantum-wasmq.wit.
- wai/wai-wasmq: the profile (Lime1, nothing imported, bounded memory,
  refusal codes W001-W017); what a run asks of its host, bounded from the
  bundle alone before anything runs; fuel defined on the module as written;
  a weighted stack; two engines (the pinned wasmtime 50.0.1 and an
  interpreter of the crate's own that also runs on wasm32); the envelope,
  runner, receipts, transcript replay, reference programs and vectors.
- wai-quantum: engine::wasmq (built with quantum_receipt), the job form
  beside its receipts; the estimate verifier answers a request no host
  re-runs with a verdict carrying rerun_error, combined here with main's
  reaction clock (clock_of).
- wai/quantum-conformance/wasmq: envelopes, refusals and receipts.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 13:22:09 -05:00

328 lines
16 KiB
Rust

//! What must fail does (extensions/quantum-wasmq.md §3, §4, §6): a module that could
//! reach outside itself or let a host choose its result is refused before it runs; a
//! bundle whose bytes are not the ones its contract pins is refused; a callout that runs
//! past its fuel, its call depth or its result's width ends the run in a signed fault
//! receipt, the same on every engine; and each protection is shown to be load-bearing by
//! the failure that appears when it is taken away.
use serde_json::Value;
use wai_quantum::ed25519_dalek::SigningKey;
use wai_wasmq::engine::{Engine, Interpreter, Trap};
use wai_wasmq::envelope::{Bundle, Callout, Module, Run, open, pack};
use wai_wasmq::interp::Metering;
use wai_wasmq::meter::{self, Parts};
use wai_wasmq::run::{EngineKind, run};
fn wasm(wat: &str) -> Vec<u8> {
wat::parse_str(wat).unwrap()
}
/// A program that measures eight qubits into `b` and calls `f` on them.
const PROGRAM: &str = "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nextern f(bit[8]) -> bit[8];\nqubit[8] q;\nbit[8] b;\nbit[8] r;\nh q;\nb = measure q;\nr = f(b);\n";
fn bundle(module: Vec<u8>, run: Run) -> Bundle {
Bundle { program: PROGRAM.into(), inputs: Default::default(), modules: vec![Module { name: "m".into(), bytes: module, priors: vec![] }], callouts: vec![Callout { name: "f".into(), module: 0, export: "f".into() }], run }
}
fn refused(module: &str) -> &'static str {
match pack(&bundle(wasm(module), Run::new(4, 1))) {
Ok(_) => "packed",
Err(r) => r.code,
}
}
fn kinds() -> Vec<EngineKind> {
#[allow(unused_mut)] // the native engine is added where this build has it
let mut k = vec![EngineKind::Interpreter, EngineKind::InterpreterPrepared];
#[cfg(all(feature = "wasmtime", not(target_arch = "wasm32")))]
k.insert(0, EngineKind::Native);
k
}
/// Runs a bundle on every engine: the job bodies are byte-identical; the first.
fn everywhere(b: &Bundle) -> Value {
let o = open(&pack(b).unwrap()).unwrap();
let key = SigningKey::from_bytes(&[7; 32]);
let mut first: Option<Vec<u8>> = None;
let mut body = Value::Null;
for k in kinds() {
let (v, _) = run(&o, k, &key, false).unwrap();
let bytes = wai_quantum::engine::receipt::canonical(&v);
match &first {
None => first = Some(bytes),
Some(f) => assert_eq!(f, &bytes, "{k:?} gives other bytes"),
}
body = v;
}
let verdict = wai_wasmq::verify(&body, Some(&o), kinds()[0]).unwrap();
assert!(verdict.ok(), "{verdict:?}");
body
}
const IDENTITY: &str = r#"(module (memory 1 1) (func (export "f") (param i64) (result i64) local.get 0))"#;
#[test]
fn the_positive_control_packs_and_runs() {
assert_eq!(refused(IDENTITY), "packed");
let body = everywhere(&bundle(wasm(IDENTITY), Run::new(16, 3)));
assert_eq!(body["result"]["status"], "ok");
// r = f(b) = b in every shot: the high byte of each key equals the low byte.
for (k, _) in body["result"]["counts"].as_object().unwrap() {
assert_eq!(&k[..8], &k[8..], "{k}");
}
}
#[test]
fn a_module_that_imports_a_clock_is_refused() {
let clock = r#"(module (import "wasi:clocks/monotonic-clock" "now" (func $now (result i64)))
(func (export "f") (param i64) (result i64) call $now))"#;
assert_eq!(refused(clock), "W002");
let random = r#"(module (import "wasi_snapshot_preview1" "random_get" (func (param i32 i32) (result i32))) (func (export "f") (param i64) (result i64) local.get 0))"#;
assert_eq!(refused(random), "W002");
let memory = r#"(module (import "env" "memory" (memory 1 1)) (func (export "f") (param i64) (result i64) local.get 0))"#;
assert_eq!(refused(memory), "W002");
}
#[test]
fn a_memory_without_a_maximum_or_past_the_profiles_is_refused() {
assert_eq!(refused(r#"(module (memory 1) (func (export "f") (param i64) (result i64) local.get 0))"#), "W003");
assert_eq!(refused(r#"(module (memory 1 65536) (func (export "f") (param i64) (result i64) local.get 0))"#), "W003");
assert_eq!(refused(r#"(module (memory 1 1 shared) (func (export "f") (param i64) (result i64) local.get 0))"#), "W005");
}
#[test]
fn threads_vectors_and_a_start_function_are_refused() {
assert_eq!(refused(r#"(module (memory 1 1) (func (export "f") (param i64) (result i64) i32.const 0 local.get 0 i64.atomic.rmw.add))"#), "W005");
assert_eq!(refused(r#"(module (func (export "f") (param i64) (result i64) local.get 0 i64x2.splat i64x2.extract_lane 0))"#), "W006");
assert_eq!(refused(r#"(module (func $s) (start $s) (func (export "f") (param i64) (result i64) local.get 0))"#), "W004");
}
#[test]
fn growing_memory_past_the_maximum_fails_the_same_way_everywhere() {
// Grows by the low bits of its input: past the maximum of 3 pages, memory.grow gives -1.
let m = wasm(r#"(module (memory 1 3) (func (export "f") (param i64) (result i64)
local.get 0 i32.wrap_i64 i32.const 7 i32.and memory.grow i64.extend_i32_s i64.const 255 i64.and))"#);
let body = everywhere(&bundle(m, Run::new(32, 5)));
let counts = body["result"]["counts"].as_object().unwrap();
let mut saw_fail = false;
for k in counts.keys() {
let b = u8::from_str_radix(&k[8..], 2).unwrap() & 7;
let r = u8::from_str_radix(&k[..8], 2).unwrap();
if b <= 2 {
assert_eq!(r, 1, "{k}: grew from one page");
} else {
assert_eq!(r, 255, "{k}: past the maximum");
saw_fail = true;
}
}
assert!(saw_fail);
}
/// Returns the low 8 bits of a NaN's payload made by arithmetic on a NaN with a payload.
const NAN_PATH: &str = r#"(module (memory 1 1) (func (export "f") (param i64) (result i64)
f64.const nan:0x4000000000abc local.get 0 f64.convert_i64_u f64.add i64.reinterpret_f64 i64.const 255 i64.and))"#;
#[test]
fn a_nan_path_reads_the_canonical_nan_everywhere() {
let body = everywhere(&bundle(wasm(NAN_PATH), Run::new(16, 2)));
for k in body["result"]["counts"].as_object().unwrap().keys() {
assert_eq!(&k[..8], "00000000", "the canonical NaN's payload is zero: {k}");
}
}
#[cfg(all(feature = "wasmtime", not(target_arch = "wasm32")))]
#[test]
fn without_canonicalisation_a_nan_path_reads_what_the_host_chose() {
// The payload propagates through the addition on this host's processor (arm64 and
// x86_64 alike), so with neither the preparation's canonicalisation nor the engine's
// the module returns the payload's low byte, 0xbc; with either, zero.
let m = wasm(NAN_PATH);
let call = |parts: Parts, engine_nan: bool| -> u64 {
let p = meter::prepare_with(&m, parts).unwrap();
let e = wai_wasmq::engine::Native::with(&p, engine_nan).unwrap();
let mut i = e.instantiate().unwrap();
i.set_fuel(1000);
i.call("f", &[3]).unwrap()[0]
};
let off = Parts { nan: false, ..Parts::ALL };
assert_eq!(call(off, false), 0xbc, "this test can fail: without canonicalisation the host's NaN shows");
assert_eq!(call(Parts::ALL, false), 0, "the preparation alone canonicalises");
assert_eq!(call(off, true), 0, "the engine alone canonicalises");
assert_eq!(call(Parts::ALL, true), 0);
}
#[test]
fn a_callout_past_its_fuel_ends_in_a_signed_fault_receipt() {
// Spins for as many iterations as its input says.
let m = wasm(r#"(module (func (export "f") (param i64) (result i64) (local i64)
block loop local.get 0 i64.eqz br_if 1 local.get 0 i64.const 1 i64.sub local.set 0 br 0 end end local.get 1))"#);
let mut r = Run::new(64, 9);
r.per_call = 400;
let body = everywhere(&bundle(m.clone(), r.clone()));
assert_eq!(body["status"], "fault");
let f = &body["result"]["fault"];
assert_eq!(f["reason"], "fuel_exhausted");
assert_eq!(f["budget"], "call");
assert_eq!(f["fuel"], 400);
assert!(body["result"]["shots_run"].as_u64().unwrap() < 64);
// The run's budget, not the call's, when it is the smaller.
r.per_call = 1_000_000;
r.per_job = 3000;
let body = everywhere(&bundle(m, r));
assert_eq!(body["result"]["fault"]["budget"], "run");
assert!(body["result"]["fuel"]["used"].as_u64().unwrap() <= 3000);
}
#[test]
fn an_endless_loop_stops_at_its_budget() {
let m = wasm(r#"(module (func (export "f") (param i64) (result i64) loop br 0 end i64.const 0))"#);
let mut r = Run::new(4, 1);
r.per_call = 10_000;
let body = everywhere(&bundle(m, r));
assert_eq!(body["result"]["fault"]["reason"], "fuel_exhausted");
assert_eq!(body["result"]["fault"]["shot"], 0);
}
#[test]
fn recursion_past_the_stack_ends_in_a_fault_receipt() {
let m = wasm(r#"(module (func $r (param i64) (result i64) local.get 0 i64.const 1 i64.add call $r)
(func (export "f") (param i64) (result i64) local.get 0 call $r))"#);
let body = everywhere(&bundle(m, Run::new(2, 1)));
assert_eq!(body["result"]["fault"]["reason"], "stack_exhausted");
}
#[test]
fn a_trap_and_a_result_past_its_width_end_in_fault_receipts() {
let trap = wasm(r#"(module (func (export "f") (param i64) (result i64) local.get 0 i64.const 0 i64.div_u))"#);
assert_eq!(everywhere(&bundle(trap, Run::new(2, 1)))["result"]["fault"]["reason"], "integer_divide_by_zero");
let wide = wasm(r#"(module (func (export "f") (param i64) (result i64) i64.const 256))"#);
let body = everywhere(&bundle(wide, Run::new(2, 1)));
assert_eq!(body["result"]["fault"]["reason"], "bad_result");
}
#[test]
fn a_callout_with_the_wrong_signature_is_refused() {
assert_eq!(refused(r#"(module (func (export "f") (param i32) (result i32) local.get 0))"#), "W015");
assert_eq!(refused(r#"(module (func (export "g") (param i64) (result i64) local.get 0))"#), "W015");
}
fn envelope() -> Vec<u8> {
let mut b = bundle(wasm(IDENTITY), Run::new(4, 1));
b.modules[0].priors.push(("table".into(), b"a prior's bytes".to_vec()));
// A module with priors exports wasmq-prior.
b.modules[0].bytes = wasm(r#"(module (memory 1 1) (export "memory" (memory 0))
(func (export "cabi_realloc") (param i32 i32 i32 i32) (result i32) i32.const 1024)
(func (export "wasmq-prior") (param i32 i32 i32))
(func (export "f") (param i64) (result i64) local.get 0))"#);
pack(&b).unwrap()
}
fn tamper(env: &[u8], find: &[u8], with: &[u8]) -> Vec<u8> {
let at = env.windows(find.len()).position(|w| w == find).expect("the bytes are in the envelope");
let mut e = env.to_vec();
e[at..at + with.len()].copy_from_slice(with);
e
}
#[test]
fn a_prior_whose_hash_is_not_the_pinned_one_is_refused() {
let env = envelope();
assert!(open(&env).is_ok(), "the positive control opens");
let bad = tamper(&env, b"a prior's bytes", b"A prior's bytes");
assert_eq!(open(&bad).unwrap_err().code, "W014");
}
/// The code `env` is refused with, by a refusal that says `needle`: the rule this test
/// is of, and no other.
fn refusal(env: &[u8], needle: &str) -> &'static str {
let e = open(env).expect_err("the envelope opened");
assert!(e.message.contains(needle), "refused for another rule than the one that says {needle:?}: {e}");
e.code
}
#[test]
fn a_changed_module_or_program_is_refused_for_its_digest() {
let env = envelope();
assert_eq!(refusal(&tamper(&env, b"wasmq-prior", b"wasmq-PRIOR"), "module m: its bytes hash to"), "W014");
assert_eq!(refusal(&tamper(&env, b"h q;", b"x q;"), "the program: its bytes hash to"), "W014");
}
#[test]
fn each_rule_of_the_envelope_s_form_refuses_what_breaks_it() {
use wai_wasmq::envelope::{MAGIC, from_parts, parts};
let env = envelope();
let (manifest, contract, frames) = parts(&env).unwrap();
let text = String::from_utf8(contract.clone()).unwrap();
let with = |manifest: &[u8], magic: &[u8; 4], contract: &[u8], frames: &[u8]| from_parts(manifest, magic, contract, frames).unwrap();
assert_eq!(with(&manifest, MAGIC, &contract, &frames), env, "the parts, unchanged, are the envelope");
let edit = |from: &str, to: &str| {
let edited = text.replacen(from, to, 1);
assert_ne!(edited, text, "{from} is in the contract");
edited.into_bytes()
};
// The contract's own run changed, canonical still: it no longer agrees with the manifest.
assert_eq!(refusal(&with(&manifest, MAGIC, &edit("\"shots\":4", "\"shots\":5"), &frames), "manifest"), "W013");
// A member the profile does not write, in its sorted place, the lengths made right:
// another envelope for the same bundle (§2.4).
assert_eq!(refusal(&with(&manifest, MAGIC, &edit(",\"modules\":", ",\"extra\":0,\"modules\":"), &frames), "members this profile does not write"), "W013");
// The same members, not in the canonical form: a space, and the keys in another order.
assert_eq!(refusal(&with(&manifest, MAGIC, &edit("{", "{ "), &frames), "canonical"), "W013");
let v: serde_json::Value = serde_json::from_slice(&contract).unwrap();
let reordered = format!("{{\"run\":{},\"callouts\":{},\"modules\":{},\"profile\":{},\"program\":{}}}", v["run"], v["callouts"], v["modules"], v["profile"], v["program"]);
assert_eq!(refusal(&with(&manifest, MAGIC, reordered.as_bytes(), &frames), "canonical"), "W013");
// Another payload.
assert_eq!(refusal(&with(&manifest, b"WQM2", &contract, &frames), "not a WQM1 bundle"), "W013");
// Frames that do not end where the payload does: a byte more, a byte less.
let mut more = frames.clone();
more.push(0);
assert_eq!(refusal(&with(&manifest, MAGIC, &contract, &more), "after the frames"), "W013");
assert_eq!(refusal(&with(&manifest, MAGIC, &contract, &frames[..frames.len() - 1]), "ends inside a frame"), "W013");
// The manifest: a byte of it, and another capability.
let m = String::from_utf8(manifest.clone()).unwrap();
assert_eq!(refusal(&with(m.replace("\"shots\":4", "\"shots\":5").as_bytes(), MAGIC, &contract, &frames), "manifest"), "W013");
assert_eq!(refusal(&with(m.replace("wai.quantum.wasmq", "wai.quantum.wasmx").as_bytes(), MAGIC, &contract, &frames), "capability"), "W013");
// The container: bytes after the payload, a byte short, another magic.
let mut long = env.clone();
long.push(0);
assert_eq!(refusal(&long, "after the payload"), "W013");
assert_eq!(refusal(&env[..env.len() - 1], "ends inside a frame"), "W013");
assert_eq!(refusal(&tamper(&env, b"WAI1", b"WAI2"), "not a WAI1 envelope"), "W013");
}
#[test]
fn a_receipt_changed_after_sealing_or_held_to_another_bundle_does_not_verify() {
let b = bundle(wasm(IDENTITY), Run::new(8, 4));
let o = open(&pack(&b).unwrap()).unwrap();
let key = SigningKey::from_bytes(&[7; 32]);
let (body, _) = run(&o, EngineKind::Interpreter, &key, false).unwrap();
assert!(wai_wasmq::verify(&body, Some(&o), EngineKind::Interpreter).unwrap().ok());
let mut forged = body.clone();
forged["result"]["fuel"]["used"] = serde_json::json!(1);
assert!(!wai_wasmq::verify(&forged, None, EngineKind::Interpreter).unwrap().signature);
let mut other = b.clone();
other.run.seed = 5;
let o2 = open(&pack(&other).unwrap()).unwrap();
assert_eq!(wai_wasmq::verify(&body, Some(&o2), EngineKind::Interpreter).unwrap().bundle, Some(false));
}
#[test]
fn without_its_meter_a_prepared_module_counts_nothing_and_without_its_stack_weight_it_is_not_bounded() {
let m = wasm(r#"(module (func $r (param i64) (result i64) local.get 0 i64.eqz if (result i64) i64.const 0 else local.get 0 i64.const 1 i64.sub call $r end)
(func (export "f") (param i64) (result i64) local.get 0 call $r))"#);
let go = |parts: Parts, arg: u64| {
let p = meter::prepare_with(&m, parts).unwrap();
let e = Interpreter::new(&p, Metering::Module).unwrap();
let mut i = e.instantiate().unwrap();
i.set_fuel(1 << 40);
i.set_stack(0);
let r = i.call("f", &[arg]);
(r, (1i64 << 40) - i.fuel())
};
let (r, used) = go(Parts::ALL, 100);
assert!(r.is_ok() && used > 0);
assert_eq!(go(Parts { fuel: false, ..Parts::ALL }, 100).1, 0, "this test can fail: no meter, no fuel");
// Deeper than the stack's weight lets a function of this size go.
let deep = 20_000;
assert_eq!(go(Parts::ALL, deep).0, Err(Trap::Stack));
assert!(go(Parts { stack: false, ..Parts::ALL }, deep).0.is_ok(), "this test can fail: no weight, no bound");
}