open-standards/scripts/check-wasmtime-pin.py
Transaction Science 0c9e94e861 wai: wasmQ, an OpenQASM 3 program and the WebAssembly its externs call, run the same everywhere, metered and receipted
A new capability, wai.quantum.wasmq (extensions/quantum-wasmq.md, its WIT beside it): one
WAI1 envelope carries an OpenQASM 3 program, the WebAssembly modules that define its
`extern` functions and their priors, each pinned by BLAKE3 in a canonical contract.

- The profile: a core Lime1 module that imports nothing, bounds its memory and has no start
  function, threads, vectors, references or growing table, each rule a refusal code.
- The prepared module meters its own fuel at the start of each run of instructions, counts
  its own call depth and returns only canonical NaNs, so any engine runs it alike. The fuel
  equals the pinned wasmtime's own default count.
- Two engines, the pinned wasmtime and an interpreter of the crate's own that also runs on
  wasm32, agree on every instruction, charge, trap and fault.
- The reader takes extern calls as steps when asked (qasm::read_callouts); its existing
  types are unchanged.
- A run seals the engine's job receipt with the fuel, the modules' digests, a digest of
  every call and the fault; a callout over budget ends in a signed fault receipt.
- wai-quantum pack-wasmq, check-wasmq, run ENVELOPE.wai (--engine, --transcript, --replay)
  and verify --bundle.
- Reference modules from the engine's own decoder and atom planner, modules of record built
  on x86_64 Linux; vectors in quantum-conformance/wasmq/; a workflow that runs them on
  linux, macOS and wasm32, and rebuilds the modules.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-05 14:47:47 -05:00

171 lines
7.8 KiB
Python
Executable file

#!/usr/bin/env python3
"""check-wasmtime-pin.py: every workflow that runs wasmtime runs the one pinned release,
with both of its determinism settings.
WAI's WebAssembly legs hold a module's output to pinned bits. A stock wasmtime leaves NaN
canonicalisation off and lets each relaxed SIMD instruction take the host's behaviour, and
two versions of it can lower one instruction two ways, so a leg's bits would be the
engine's choice. The install steps' comment says every workflow pins one version and
passes both settings; this makes that true of every workflow, not only of the ones that
comment sits in.
In every `.github/workflows/*.yml`, with continued lines (`\\` at the end) joined, this
requires that
1. every download of a wasmtime release names version PIN (the `v=` the step sets, or
the version in the URL), and the SHA-256 it checks is the digest of that archive in
DIGESTS;
2. wasmtime is installed no other way (`cargo install wasmtime-cli`, the install
script, or an action);
3. every `wasmtime run`, `wasmtime serve` or `wasmtime compile`, every `wasmtime` given
a module path, and every `CARGO_TARGET_<triple>_RUNNER` that is wasmtime, passes
`-W nan-canonicalization=y` and `-W relaxed-simd-deterministic=y`;
4. at least one download and one run were found, so a check that looked in the wrong
place fails rather than passing on nothing;
5. every crate under wai/ that depends on wasmtime as a library pins it to that release
exactly (`version = "=PIN"`), so the engine a crate embeds is the one the workflows
run (wai-wasmq's native engine, extensions/quantum-wasmq.md §4.6).
check-wasmtime-pin.py [--root DIR]
Exit 0 when all four hold, 1 with one line a finding otherwise. Standard library only.
"""
import os
import re
import sys
PIN = "50.0.1"
# The release archives a workflow may download, by name, with the SHA-256 the release lists.
DIGESTS = {
"wasmtime-v50.0.1-x86_64-linux.tar.xz": "685466455dd42a14774a86c36c8f2e45cbb9bd6d92a850139315df60a75498a5",
}
FLAGS = ("nan-canonicalization=y", "relaxed-simd-deterministic=y")
DOWNLOAD = re.compile(r"bytecodealliance/wasmtime/releases/download/")
URL_VERSION = re.compile(r"releases/download/v(\d[^/\"'\s]*)/")
SET_VERSION = re.compile(r"^\s*v=[\"']?([^\"'\s;]+)")
ARCHIVE = re.compile(r"wasmtime-v(\$v|\$\{v\}|\d[^-\"'\s]*)-([a-z0-9_]+-[a-z]+)\.tar\.xz")
DIGEST = re.compile(r"\b([0-9a-f]{64})\b.*sha256sum -c")
OTHER_INSTALL = re.compile(
r"cargo\s+(?:\S+\s+)*install\s+(?:\S+\s+)*wasmtime-cli|wasmtime\.dev/install|uses:\s*\S*wasmtime"
)
# wasmtime as a command: run, serve or compile, or given a module path (`wasmtime x.wasm`).
INVOKE = re.compile(
r"(?:^|[\s;&|(`/\"'=])wasmtime\s+(?:(?:run|serve|compile)\b|[^\s-]\S*\.(?:c?wasm|wat)\b)"
)
RUNNER = re.compile(r"CARGO_TARGET_\w+_RUNNER\s*[:=]\s*[\"']?wasmtime\b")
# A Cargo dependency on the wasmtime library, and the version it asks for.
CRATE_DEP = re.compile(r"^\s*wasmtime\s*=")
CRATE_VERSION = re.compile(r"version\s*=\s*\"([^\"]+)\"|^\s*wasmtime\s*=\s*\"([^\"]+)\"")
def logical_lines(text):
"""(first line number, text) with backslash-continued lines joined."""
out, buf, start = [], "", 0
for n, line in enumerate(text.splitlines(), 1):
if not buf:
start = n
if line.rstrip().endswith("\\"):
buf += line.rstrip()[:-1] + " "
continue
out.append((start, buf + line))
buf = ""
if buf:
out.append((start, buf))
return out
def check_file(path, rel, findings):
lines = logical_lines(open(path, encoding="utf-8").read())
downloads = runs = 0
for i, (n, line) in enumerate(lines):
code = line.lstrip()
if code.startswith("#"):
continue
if OTHER_INSTALL.search(line):
findings.append(f"{rel}:{n}: installs wasmtime some other way; download the pinned release ({PIN}) by version and SHA-256")
if DOWNLOAD.search(line):
downloads += 1
m = URL_VERSION.search(line)
ver = m.group(1) if m else None
if ver is None: # the URL takes the version from the step's `v=`
for _, prev in reversed(lines[max(0, i - 12) : i]):
s = SET_VERSION.match(prev)
if s:
ver = s.group(1)
break
if ver != PIN:
findings.append(f"{rel}:{n}: downloads wasmtime {ver or '(version not found)'}; every workflow pins {PIN}")
near = lines[max(0, i - 12) : i + 5]
arch = None
for _, other in near:
a = ARCHIVE.search(other)
if a:
arch = a.group(2)
break
name = f"wasmtime-v{ver}-{arch}.tar.xz" if arch else None
digests = [d.group(1) for _, other in lines[i : i + 5] for d in [DIGEST.search(other)] if d]
if name not in DIGESTS:
findings.append(f"{rel}:{n}: downloads {name or 'an archive whose name was not found'}, which has no pinned digest here")
elif not digests:
findings.append(f"{rel}:{n}: downloads {name} and checks no SHA-256 within four lines")
elif digests[0] != DIGESTS[name]:
findings.append(f"{rel}:{n}: checks {name} against {digests[0][:12]}..., not its pinned digest {DIGESTS[name][:12]}...")
for rx in (INVOKE, RUNNER):
m = rx.search(line)
if m:
runs += 1
rest = line[m.start() :]
missing = [f for f in FLAGS if f not in rest]
if missing:
findings.append(f"{rel}:{n}: runs wasmtime without {' and '.join('-W ' + f for f in missing)}")
break
return downloads, runs
def main(argv):
root = "."
if len(argv) == 3 and argv[1] == "--root":
root = argv[2]
elif len(argv) != 1:
sys.exit("usage: check-wasmtime-pin.py [--root DIR]")
wf = os.path.join(root, ".github", "workflows")
findings, downloads, runs = [], 0, 0
for name in sorted(os.listdir(wf)) if os.path.isdir(wf) else []:
if name.endswith((".yml", ".yaml")):
d, r = check_file(os.path.join(wf, name), f".github/workflows/{name}", findings)
downloads += d
runs += r
crates = 0
wai = os.path.join(root, "wai")
for dirpath, dirnames, filenames in os.walk(wai):
dirnames[:] = [d for d in dirnames if d not in ("target", "node_modules", ".git")]
if "Cargo.toml" not in filenames:
continue
path = os.path.join(dirpath, "Cargo.toml")
rel = os.path.relpath(path, root)
section = ""
for n, line in enumerate(open(path, encoding="utf-8").read().splitlines(), 1):
if line.strip().startswith("["):
section = line.strip()
continue
# Only a dependency table names a crate; `wasmtime = [...]` in [features] does not.
if "dependencies" not in section or not CRATE_DEP.match(line):
continue
crates += 1
v = CRATE_VERSION.search(line)
ver = (v.group(1) or v.group(2)) if v else None
if ver != "=" + PIN:
findings.append(f"{rel}:{n}: depends on wasmtime {ver or '(no version)'}; a crate pins the release the workflows run, \"={PIN}\"")
if downloads == 0 or runs == 0:
findings.append(f"found {downloads} wasmtime download(s) and {runs} run(s) under {wf}: this check looked in the wrong place")
for f in findings:
print(f"::error::{f}")
if findings:
return 1
print(f"check-wasmtime-pin: {downloads} downloads of wasmtime {PIN}, each checked against its digest; {runs} runs, each with both settings; {crates} crate dependencies, each on ={PIN}")
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv))