Some checks failed
no-vanity-metrics / Scan committed Markdown for vanity metrics (push) Successful in 3s
standards workspaces build + test / aac-rs (push) Successful in 37s
standards workspaces build + test / joule-code-rs (push) Successful in 42s
standards workspaces build + test / eoc-rs (push) Successful in 1m47s
standards workspaces build + test / jcp-rs (push) Successful in 1m14s
standards workspaces build + test / proof-rs (push) Successful in 1m9s
standards workspaces build + test / jouleclaw-rs (push) Successful in 4m6s
standards workspaces build + test / joulecontract-rs (push) Successful in 38s
standards workspaces build + test / sandbox-rs (push) Successful in 2m1s
standards workspaces build + test / map-rs (push) Successful in 36s
standards workspaces build + test / mesh-rs (push) Successful in 34s
standards workspaces build + test / openpay (push) Successful in 1m23s
standards workspaces build + test / smart-byte-rs (push) Successful in 2m28s
WAI conformance / cargo-class corpora · linux-arm64 (push) Successful in 33s
WAI conformance / cargo-class corpora · linux (push) Successful in 28s
WAI conformance / cross-arch byte-equality · linux-arm64 (push) Successful in 17s
WAI conformance / cross-arch byte-equality · linux (push) Successful in 14s
standards workspaces build + test / arl-rs (push) Successful in 8m41s
standards workspaces build + test / joule-ui-rs (push) Successful in 7m56s
standards workspaces build + test / jouledesk-rs (push) Successful in 8m24s
standards workspaces build + test / joulehook-rs (push) Successful in 8m34s
standards workspaces build + test / sense-rs (push) Successful in 7m58s
WAI conformance / cargo-class corpora · macos (push) Successful in 1m4s
WAI conformance / cross-arch byte-equality · macos (push) Successful in 23s
WAI conformance / cargo-class corpora · windows (push) Has been cancelled
WAI conformance / cross-arch byte-equality · windows (push) Has been cancelled
crates.io publish was blocked: the name `wai` is taken (an unrelated 2017 crate), and `default = ["codecs"]` would make `cargo add` pull libjxl/libavif/opus/FLAC/ rav1e/dav1d system libraries — so most builds would fail for someone who only wants quantum. Extracting the quantum stack solves both. `wai-quantum` is dependency-light and pure Rust (blake3 + serde_json, with ed25519-dalek only for the sealing features). No system libraries, so it builds everywhere the stack already runs: native, browser wasm, and wasm32-wasip2. The split follows the seam that was already there — 12,975 lines moved with only four coupling points: - `world:🧾:merkle_root` (used by 3 modules) is reproduced verbatim in `wai-quantum::merkle`, domain separators included, so every previously issued receipt and published conformance vector still verifies; - `wrap_envelope` is a WAI-envelope concern, not a quantum one, so it stays in `wai` as `quantum_envelope` (it had no external callers). `wai` keeps every quantum feature name and re-exports the modules unchanged, so wai-web, wai-quantum-serve and the deployed site build without edits. Also re-exports `ed25519_dalek` from wai-quantum: the sealing API takes a `SigningKey`, and a consumer picking their own version could get an incompatible one. Verified the extraction is observably a no-op — identical to the values recorded before it: ghz -n 5 circuit-id 355d1166… state-hash 0b5f4201… grover -n 4 -p 9 state-hash 25a7d930… receipt JSON sha256 117ff588… 208 tests pass (173 wai-quantum + 35 wai); wai-web and the WASI-HTTP component build unchanged, and the edge endpoint still returns the identical hash. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
1820 lines
72 KiB
Rust
1820 lines
72 KiB
Rust
//! Quantum-operations attestation — `wai.quantum.calibration` + `wai.quantum.job`
|
||
//! (extensions/quantum-ops). The verify-half of WAI applied to the quantum
|
||
//! processing industry.
|
||
//!
|
||
//! Every quantum-control stack in the field emits the same three unsigned things
|
||
//! — an HDF5 dataset, a JSON state snapshot, and a dashboard/log — and not one
|
||
//! signs, content-addresses, ports, or meters the joules of a single operation.
|
||
//! The industry already reaches for reproducibility (QM's `__package_versions__`,
|
||
//! QBOM bills-of-materials, QCIVET hash-chained audit) but stops exactly short of
|
||
//! two axes: **cryptographic signature/portability**, and **measured joules**.
|
||
//!
|
||
//! This module is those two axes. It does NOT reconstruct anything and does NOT
|
||
//! run hardware — the calibration/job is performed by the sink's control system
|
||
//! (Quantify / QUAlibrate / Juice / Qubex / a real QPU), exactly as WAI dispatches
|
||
//! a decode to a sink's codec. WAI owns the **receipt**: a signed, content-
|
||
//! addressed, joule-metered, capability-granted record of what ran, under what
|
||
//! device state, at what energy cost, authorized by whom.
|
||
//!
|
||
//! Two receipts, cross-linked:
|
||
//! - [`CalibrationReceipt`] — a bring-up / tune-up run: binds the applied config
|
||
//! to the evidence it produced (RB / T1·T2 / gate fidelity) and the device
|
||
//! lineage, sealed with the measured joules and the grant that authorized it.
|
||
//! - [`JobReceipt`] — a dispatched circuit: binds the circuit to its result and
|
||
//! the **`CalibrationReceipt` the device was in** when it ran (the QCIVET
|
||
//! "verify the claimed calibration was in effect" check), sealed the same way.
|
||
//!
|
||
//! The **grant ceiling is enforced in `verify()`**: a receipt whose measured
|
||
//! joules exceed the grant's joule budget is not cryptographically valid — an
|
||
//! operation that blew its energy grant cannot produce a passing receipt. Gate ⊗
|
||
//! meter ⊗ receipt, in one object. A JWP profile — the world/provenance Merkle +
|
||
//! Ed25519, reused.
|
||
|
||
use crate::merkle::merkle_root;
|
||
use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
|
||
|
||
/// A signed attestation that a calibration run happened, with what result and
|
||
/// energy cost, under what authorization.
|
||
pub const CAP_QUANTUM_CALIBRATION: &str = "wai.quantum.calibration";
|
||
/// A signed attestation that a circuit was dispatched to a backend, with what
|
||
/// result and energy cost, under what calibration state and authorization.
|
||
pub const CAP_QUANTUM_JOB: &str = "wai.quantum.job";
|
||
/// A signed attestation of ONE classical error-mitigation post-processing step —
|
||
/// the deterministic layer between raw QPU counts and the reported number, which
|
||
/// today runs in nondeterministic float with no provenance over the step.
|
||
pub const CAP_QUANTUM_MITIGATE: &str = "wai.quantum.mitigate";
|
||
/// A signed attestation of ONE quantum error-correction DECODE — the classical
|
||
/// decoder that turns a syndrome into a correction, with its measured energy.
|
||
/// Joules-per-decode is the metric no decoder reports today.
|
||
pub const CAP_QUANTUM_DECODE: &str = "wai.quantum.decode";
|
||
/// A signed attestation that a COMPILED / routed circuit is equivalent to its
|
||
/// source — compilation correctness, which no compiler signs today.
|
||
pub const CAP_QUANTUM_COMPILE: &str = "wai.quantum.compile";
|
||
/// A signed attestation of a neutral-atom REARRANGEMENT plan — turning a low-yield
|
||
/// stochastic load into a defect-free target array, with the plan's cost.
|
||
pub const CAP_QUANTUM_REARRANGE: &str = "wai.quantum.rearrange";
|
||
/// A signed Quantum Bill of Materials — one provenance manifest bundling the stage
|
||
/// receipts of a quantum computation (compile, calibrate, execute, mitigate, …).
|
||
pub const CAP_QUANTUM_QBOM: &str = "wai.quantum.qbom";
|
||
|
||
const DOMAIN_CAL: &[u8] = b"wai:quantum-calibration\x01";
|
||
const DOMAIN_CAL_ID: &[u8] = b"wai:quantum-calibration-id\x01";
|
||
const DOMAIN_JOB: &[u8] = b"wai:quantum-job\x01";
|
||
const DOMAIN_JOB_ID: &[u8] = b"wai:quantum-job-id\x01";
|
||
const DOMAIN_MIT: &[u8] = b"wai:quantum-mitigate\x01";
|
||
const DOMAIN_MIT_ID: &[u8] = b"wai:quantum-mitigate-id\x01";
|
||
const DOMAIN_MIT_RESULT: &[u8] = b"wai:quantum-mitigate-result\x01";
|
||
const DOMAIN_DEC: &[u8] = b"wai:quantum-decode\x01";
|
||
const DOMAIN_DEC_ID: &[u8] = b"wai:quantum-decode-id\x01";
|
||
const DOMAIN_CMP: &[u8] = b"wai:quantum-compile\x01";
|
||
const DOMAIN_CMP_ID: &[u8] = b"wai:quantum-compile-id\x01";
|
||
const DOMAIN_REA: &[u8] = b"wai:quantum-rearrange\x01";
|
||
const DOMAIN_REA_ID: &[u8] = b"wai:quantum-rearrange-id\x01";
|
||
|
||
fn hx(b: &[u8]) -> String {
|
||
b.iter().map(|x| format!("{x:02x}")).collect()
|
||
}
|
||
|
||
fn from_hex32(s: &str) -> Option<[u8; 32]> {
|
||
if s.len() != 64 || !s.bytes().all(|b| b.is_ascii_hexdigit()) {
|
||
return None;
|
||
}
|
||
let mut out = [0u8; 32];
|
||
for (i, c) in s.as_bytes().chunks(2).enumerate() {
|
||
out[i] = u8::from_str_radix(std::str::from_utf8(c).ok()?, 16).ok()?;
|
||
}
|
||
Some(out)
|
||
}
|
||
|
||
fn from_hex64(s: &str) -> Option<[u8; 64]> {
|
||
if s.len() != 128 || !s.bytes().all(|b| b.is_ascii_hexdigit()) {
|
||
return None;
|
||
}
|
||
let mut out = [0u8; 64];
|
||
for (i, c) in s.as_bytes().chunks(2).enumerate() {
|
||
out[i] = u8::from_str_radix(std::str::from_utf8(c).ok()?, 16).ok()?;
|
||
}
|
||
Some(out)
|
||
}
|
||
|
||
/// Content hash of arbitrary operation bytes (a pulse config, an HDF5 dataset,
|
||
/// a result histogram) — the content-addressing primitive the whole module is
|
||
/// built on. Anyone with the bytes recomputes it and checks the binding.
|
||
pub fn content_hash(bytes: &[u8]) -> [u8; 32] {
|
||
*blake3::hash(bytes).as_bytes()
|
||
}
|
||
|
||
/// A reference to the JCP grant that authorized an operation: capability ⊗
|
||
/// joule-ceiling (⊗ optional funds-ceiling, JCP §4.8 dual ceiling). The full
|
||
/// grant lives in the JCP layer; the receipt binds its content hash plus the
|
||
/// ceilings it enforces, so the receipt is self-contained for the budget check.
|
||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||
pub struct GrantRef {
|
||
/// Content hash of the full JCP grant (authoritative record elsewhere).
|
||
pub grant_hash: [u8; 32],
|
||
/// The capability the grant conferred, e.g. `"quantum.calibrate"`.
|
||
pub capability: String,
|
||
/// The joule budget (µJ) the grant authorized. `u64::MAX` = no joule ceiling.
|
||
pub joule_ceiling_micro: u64,
|
||
/// Optional money ceiling (minor units); `None` = no funds ceiling.
|
||
pub funds_ceiling: Option<u64>,
|
||
}
|
||
|
||
impl GrantRef {
|
||
/// A grant with no ceilings — for unmetered/unbudgeted operations (the
|
||
/// receipt still binds and signs everything else).
|
||
pub fn unbounded(capability: impl Into<String>) -> GrantRef {
|
||
GrantRef {
|
||
grant_hash: [0u8; 32],
|
||
capability: capability.into(),
|
||
joule_ceiling_micro: u64::MAX,
|
||
funds_ceiling: None,
|
||
}
|
||
}
|
||
|
||
fn append_to(&self, o: &mut Vec<u8>) {
|
||
o.extend_from_slice(&self.grant_hash);
|
||
o.extend_from_slice(self.capability.as_bytes());
|
||
o.push(0); // capability terminator (unambiguous framing)
|
||
o.extend_from_slice(&self.joule_ceiling_micro.to_be_bytes());
|
||
match self.funds_ceiling {
|
||
Some(f) => {
|
||
o.push(1);
|
||
o.extend_from_slice(&f.to_be_bytes());
|
||
}
|
||
None => o.push(0),
|
||
}
|
||
}
|
||
|
||
fn to_json(&self) -> String {
|
||
let funds = match self.funds_ceiling {
|
||
Some(f) => f.to_string(),
|
||
None => "null".into(),
|
||
};
|
||
format!(
|
||
"{{\"capability\":{},\"funds_ceiling\":{},\"grant_hash\":\"{}\",\"joule_ceiling_micro\":{}}}",
|
||
serde_json::to_string(&self.capability).unwrap(),
|
||
funds,
|
||
hx(&self.grant_hash),
|
||
self.joule_ceiling_micro,
|
||
)
|
||
}
|
||
|
||
fn from_json(v: &serde_json::Value) -> Option<GrantRef> {
|
||
let o = v.as_object()?;
|
||
Some(GrantRef {
|
||
grant_hash: from_hex32(o.get("grant_hash")?.as_str()?)?,
|
||
capability: o.get("capability")?.as_str()?.to_owned(),
|
||
joule_ceiling_micro: o.get("joule_ceiling_micro")?.as_u64()?,
|
||
funds_ceiling: match o.get("funds_ceiling") {
|
||
Some(serde_json::Value::Number(n)) => Some(n.as_u64()?),
|
||
_ => None,
|
||
},
|
||
})
|
||
}
|
||
}
|
||
|
||
/// One piece of calibration evidence — a benchmark outcome, content-addressed to
|
||
/// its raw bytes. The `blob_hash` binds a fidelity *claim* to the exact dataset
|
||
/// (HDF5/JSON) that produced it; `summary` is human-readable and not trusted.
|
||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||
pub struct Evidence {
|
||
/// e.g. `"randomized_benchmarking"`, `"t1t2"`, `"rabi"`, `"readout_gmm"`.
|
||
pub kind: String,
|
||
/// BLAKE3 of the canonical evidence bytes (the vendor dataset).
|
||
pub blob_hash: [u8; 32],
|
||
/// Human summary, e.g. `"1q RB fidelity 0.9992"`. Informational only.
|
||
pub summary: String,
|
||
}
|
||
|
||
impl Evidence {
|
||
fn leaf(&self) -> [u8; 32] {
|
||
let mut h = blake3::Hasher::new();
|
||
h.update(b"wai:qcal-evidence\x01");
|
||
h.update(self.kind.as_bytes());
|
||
h.update(&[0]);
|
||
h.update(&self.blob_hash);
|
||
*h.finalize().as_bytes()
|
||
}
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// CalibrationReceipt
|
||
// ---------------------------------------------------------------------------
|
||
|
||
/// A signed, joule-metered, grant-authorized record of one calibration/tune-up
|
||
/// run. Chains to the prior device state via `parent_receipt_hash` — the signed
|
||
/// "digital twin" lineage the vendors keep only as mutable, unsigned data.
|
||
#[derive(Clone, Debug, PartialEq)]
|
||
pub struct CalibrationReceipt {
|
||
/// Device / QPU identity (e.g. `"qm:opx1000:lab3"`, a wafer/die id).
|
||
pub device_id: String,
|
||
/// What was calibrated: `"q3"`, `"cz(q3,q4)"`, `"readout:q3"`.
|
||
pub target: String,
|
||
/// Content hash of the applied pulse/gate config (the "config").
|
||
pub config_hash: [u8; 32],
|
||
/// The evidence produced (the "proof it's tuned").
|
||
pub evidence: Vec<Evidence>,
|
||
/// Merkle root over `[config_hash] ++ evidence leaves`.
|
||
pub merkle_root: [u8; 32],
|
||
/// Measured energy of the run (µJ). Attested by the signature.
|
||
pub joules_micro: u64,
|
||
/// The grant that authorized the run (capability ⊗ joule ceiling).
|
||
pub grant: GrantRef,
|
||
/// Prior calibration state this one supersedes (signed lineage).
|
||
pub parent_receipt_hash: Option<[u8; 32]>,
|
||
pub signer_pubkey: [u8; 32],
|
||
pub signer_id: String,
|
||
pub sig: [u8; 64],
|
||
}
|
||
|
||
impl CalibrationReceipt {
|
||
fn leaves(&self) -> Vec<[u8; 32]> {
|
||
let mut v = vec![self.config_hash];
|
||
v.extend(self.evidence.iter().map(|e| e.leaf()));
|
||
v
|
||
}
|
||
|
||
fn signing_payload(&self) -> Vec<u8> {
|
||
let mut o = DOMAIN_CAL.to_vec();
|
||
o.extend_from_slice(self.device_id.as_bytes());
|
||
o.push(0);
|
||
o.extend_from_slice(self.target.as_bytes());
|
||
o.push(0);
|
||
o.extend_from_slice(&self.config_hash);
|
||
o.extend_from_slice(&self.merkle_root);
|
||
o.extend_from_slice(&(self.evidence.len() as u64).to_be_bytes());
|
||
o.extend_from_slice(&self.joules_micro.to_be_bytes());
|
||
self.grant.append_to(&mut o);
|
||
match self.parent_receipt_hash {
|
||
Some(h) => {
|
||
o.push(1);
|
||
o.extend_from_slice(&h);
|
||
}
|
||
None => o.push(0),
|
||
}
|
||
o.extend_from_slice(&self.signer_pubkey);
|
||
o.extend_from_slice(self.signer_id.as_bytes());
|
||
o
|
||
}
|
||
|
||
/// Build and sign a calibration receipt.
|
||
#[allow(clippy::too_many_arguments)]
|
||
pub fn seal(
|
||
signer: &SigningKey,
|
||
signer_id: impl Into<String>,
|
||
device_id: impl Into<String>,
|
||
target: impl Into<String>,
|
||
config_hash: [u8; 32],
|
||
evidence: Vec<Evidence>,
|
||
joules_micro: u64,
|
||
grant: GrantRef,
|
||
parent_receipt_hash: Option<[u8; 32]>,
|
||
) -> CalibrationReceipt {
|
||
let mut r = CalibrationReceipt {
|
||
device_id: device_id.into(),
|
||
target: target.into(),
|
||
config_hash,
|
||
evidence,
|
||
merkle_root: [0u8; 32],
|
||
joules_micro,
|
||
grant,
|
||
parent_receipt_hash,
|
||
signer_pubkey: signer.verifying_key().to_bytes(),
|
||
signer_id: signer_id.into(),
|
||
sig: [0u8; 64],
|
||
};
|
||
r.merkle_root = merkle_root(&r.leaves());
|
||
r.sig = signer.sign(&r.signing_payload()).to_bytes();
|
||
r
|
||
}
|
||
|
||
/// The measured energy is within the grant's joule ceiling — the metered
|
||
/// half of the gate ⊗ meter ⊗ receipt property.
|
||
pub fn honors_budget(&self) -> bool {
|
||
self.joules_micro <= self.grant.joule_ceiling_micro
|
||
}
|
||
|
||
/// Cryptographically valid AND within budget: the Merkle root recomputes,
|
||
/// the measured joules are within the grant ceiling, and the signature is
|
||
/// valid. A run that blew its energy grant cannot produce a passing receipt.
|
||
pub fn verify(&self) -> bool {
|
||
if merkle_root(&self.leaves()) != self.merkle_root {
|
||
return false;
|
||
}
|
||
if !self.honors_budget() {
|
||
return false;
|
||
}
|
||
let Ok(k) = VerifyingKey::from_bytes(&self.signer_pubkey) else {
|
||
return false;
|
||
};
|
||
k.verify(&self.signing_payload(), &Signature::from_bytes(&self.sig))
|
||
.is_ok()
|
||
}
|
||
|
||
/// Confirm a fidelity claim binds to real bytes: recompute `content_hash` of
|
||
/// the supplied dataset and match the i-th evidence's `blob_hash`. This is
|
||
/// what makes "1q RB fidelity 0.9992" checkable instead of asserted.
|
||
pub fn evidence_matches(&self, i: usize, dataset_bytes: &[u8]) -> bool {
|
||
self.evidence
|
||
.get(i)
|
||
.is_some_and(|e| content_hash(dataset_bytes) == e.blob_hash)
|
||
}
|
||
|
||
/// Stable id for chaining and for a job to reference this device state.
|
||
pub fn receipt_hash(&self) -> [u8; 32] {
|
||
let mut h = blake3::Hasher::new();
|
||
h.update(DOMAIN_CAL_ID);
|
||
h.update(&self.signing_payload());
|
||
h.update(&self.sig);
|
||
*h.finalize().as_bytes()
|
||
}
|
||
|
||
pub fn to_json(&self) -> String {
|
||
let ev: Vec<String> = self
|
||
.evidence
|
||
.iter()
|
||
.map(|e| {
|
||
format!(
|
||
"{{\"blob_hash\":\"{}\",\"kind\":{},\"summary\":{}}}",
|
||
hx(&e.blob_hash),
|
||
serde_json::to_string(&e.kind).unwrap(),
|
||
serde_json::to_string(&e.summary).unwrap(),
|
||
)
|
||
})
|
||
.collect();
|
||
let parent = match self.parent_receipt_hash {
|
||
Some(h) => format!("\"{}\"", hx(&h)),
|
||
None => "null".into(),
|
||
};
|
||
format!(
|
||
"{{\"kind\":\"quantum-calibration\",\"config_hash\":\"{}\",\"device_id\":{},\
|
||
\"evidence\":[{}],\"grant\":{},\"joules_micro\":{},\"merkle_root\":\"{}\",\
|
||
\"parent_receipt_hash\":{},\"receipt_hash\":\"{}\",\"sig\":\"{}\",\
|
||
\"signer_id\":{},\"signer_pubkey\":\"{}\",\"target\":{}}}",
|
||
hx(&self.config_hash),
|
||
serde_json::to_string(&self.device_id).unwrap(),
|
||
ev.join(","),
|
||
self.grant.to_json(),
|
||
self.joules_micro,
|
||
hx(&self.merkle_root),
|
||
parent,
|
||
hx(&self.receipt_hash()),
|
||
hx(&self.sig),
|
||
serde_json::to_string(&self.signer_id).unwrap(),
|
||
hx(&self.signer_pubkey),
|
||
serde_json::to_string(&self.target).unwrap(),
|
||
)
|
||
}
|
||
|
||
pub fn from_json(s: &str) -> Option<CalibrationReceipt> {
|
||
let v: serde_json::Value = serde_json::from_str(s).ok()?;
|
||
let o = v.as_object()?;
|
||
let mut evidence = Vec::new();
|
||
for ev in o.get("evidence")?.as_array()? {
|
||
let eo = ev.as_object()?;
|
||
evidence.push(Evidence {
|
||
kind: eo.get("kind")?.as_str()?.to_owned(),
|
||
blob_hash: from_hex32(eo.get("blob_hash")?.as_str()?)?,
|
||
summary: eo.get("summary")?.as_str()?.to_owned(),
|
||
});
|
||
}
|
||
let parent = match o.get("parent_receipt_hash") {
|
||
Some(serde_json::Value::String(s)) => Some(from_hex32(s)?),
|
||
_ => None,
|
||
};
|
||
Some(CalibrationReceipt {
|
||
device_id: o.get("device_id")?.as_str()?.to_owned(),
|
||
target: o.get("target")?.as_str()?.to_owned(),
|
||
config_hash: from_hex32(o.get("config_hash")?.as_str()?)?,
|
||
evidence,
|
||
merkle_root: from_hex32(o.get("merkle_root")?.as_str()?)?,
|
||
joules_micro: o.get("joules_micro")?.as_u64()?,
|
||
grant: GrantRef::from_json(o.get("grant")?)?,
|
||
parent_receipt_hash: parent,
|
||
signer_pubkey: from_hex32(o.get("signer_pubkey")?.as_str()?)?,
|
||
signer_id: o.get("signer_id")?.as_str()?.to_owned(),
|
||
sig: from_hex64(o.get("sig")?.as_str()?)?,
|
||
})
|
||
}
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// JobReceipt
|
||
// ---------------------------------------------------------------------------
|
||
|
||
/// A signed, joule-metered, grant-authorized record of one dispatched circuit —
|
||
/// bound to the exact calibration state the device was in when it ran.
|
||
#[derive(Clone, Debug, PartialEq)]
|
||
pub struct JobReceipt {
|
||
/// Which backend actually ran it: `"ionq:aria-1"`, `"wai.quantum.circuit"`.
|
||
pub backend_id: String,
|
||
/// Content hash of the dispatched circuit (e.g. a WQC container / QASM).
|
||
pub circuit_hash: [u8; 32],
|
||
/// The [`CalibrationReceipt::receipt_hash`] of the device state in effect —
|
||
/// the QCIVET "the claimed calibration was actually in effect" binding.
|
||
/// `None` for a simulator or an uncalibrated run.
|
||
pub calibration_ref: Option<[u8; 32]>,
|
||
/// Content hash of the returned result (counts histogram / statevector).
|
||
pub result_hash: [u8; 32],
|
||
/// Shots requested (informational).
|
||
pub shots: u64,
|
||
/// Merkle root over `[circuit_hash, result_hash, (calibration_ref?)]`.
|
||
pub merkle_root: [u8; 32],
|
||
/// Measured energy of the job (µJ). Attested by the signature.
|
||
pub joules_micro: u64,
|
||
pub grant: GrantRef,
|
||
pub parent_receipt_hash: Option<[u8; 32]>,
|
||
pub signer_pubkey: [u8; 32],
|
||
pub signer_id: String,
|
||
pub sig: [u8; 64],
|
||
}
|
||
|
||
impl JobReceipt {
|
||
fn leaves(&self) -> Vec<[u8; 32]> {
|
||
let mut v = vec![self.circuit_hash, self.result_hash];
|
||
if let Some(c) = self.calibration_ref {
|
||
v.push(c);
|
||
}
|
||
v
|
||
}
|
||
|
||
fn signing_payload(&self) -> Vec<u8> {
|
||
let mut o = DOMAIN_JOB.to_vec();
|
||
o.extend_from_slice(self.backend_id.as_bytes());
|
||
o.push(0);
|
||
o.extend_from_slice(&self.circuit_hash);
|
||
o.extend_from_slice(&self.result_hash);
|
||
match self.calibration_ref {
|
||
Some(c) => {
|
||
o.push(1);
|
||
o.extend_from_slice(&c);
|
||
}
|
||
None => o.push(0),
|
||
}
|
||
o.extend_from_slice(&self.shots.to_be_bytes());
|
||
o.extend_from_slice(&self.merkle_root);
|
||
o.extend_from_slice(&self.joules_micro.to_be_bytes());
|
||
self.grant.append_to(&mut o);
|
||
match self.parent_receipt_hash {
|
||
Some(h) => {
|
||
o.push(1);
|
||
o.extend_from_slice(&h);
|
||
}
|
||
None => o.push(0),
|
||
}
|
||
o.extend_from_slice(&self.signer_pubkey);
|
||
o.extend_from_slice(self.signer_id.as_bytes());
|
||
o
|
||
}
|
||
|
||
#[allow(clippy::too_many_arguments)]
|
||
pub fn seal(
|
||
signer: &SigningKey,
|
||
signer_id: impl Into<String>,
|
||
backend_id: impl Into<String>,
|
||
circuit_hash: [u8; 32],
|
||
result_hash: [u8; 32],
|
||
calibration_ref: Option<[u8; 32]>,
|
||
shots: u64,
|
||
joules_micro: u64,
|
||
grant: GrantRef,
|
||
parent_receipt_hash: Option<[u8; 32]>,
|
||
) -> JobReceipt {
|
||
let mut r = JobReceipt {
|
||
backend_id: backend_id.into(),
|
||
circuit_hash,
|
||
calibration_ref,
|
||
result_hash,
|
||
shots,
|
||
merkle_root: [0u8; 32],
|
||
joules_micro,
|
||
grant,
|
||
parent_receipt_hash,
|
||
signer_pubkey: signer.verifying_key().to_bytes(),
|
||
signer_id: signer_id.into(),
|
||
sig: [0u8; 64],
|
||
};
|
||
r.merkle_root = merkle_root(&r.leaves());
|
||
r.sig = signer.sign(&r.signing_payload()).to_bytes();
|
||
r
|
||
}
|
||
|
||
pub fn honors_budget(&self) -> bool {
|
||
self.joules_micro <= self.grant.joule_ceiling_micro
|
||
}
|
||
|
||
/// Merkle recomputes, joules within the grant ceiling, signature valid.
|
||
pub fn verify(&self) -> bool {
|
||
if merkle_root(&self.leaves()) != self.merkle_root {
|
||
return false;
|
||
}
|
||
if !self.honors_budget() {
|
||
return false;
|
||
}
|
||
let Ok(k) = VerifyingKey::from_bytes(&self.signer_pubkey) else {
|
||
return false;
|
||
};
|
||
k.verify(&self.signing_payload(), &Signature::from_bytes(&self.sig))
|
||
.is_ok()
|
||
}
|
||
|
||
/// The cross-link check: this job claims it ran under `cal`'s device state.
|
||
/// Confirms `cal` verifies and its id equals this job's `calibration_ref`.
|
||
/// A third party can now assert "this result was produced on a device in
|
||
/// this attested calibration state" — the property QCIVET names and nobody
|
||
/// signs.
|
||
pub fn verify_against_calibration(&self, cal: &CalibrationReceipt) -> bool {
|
||
self.calibration_ref == Some(cal.receipt_hash()) && cal.verify() && self.verify()
|
||
}
|
||
|
||
/// Confirm the result bytes bind to this receipt's `result_hash`.
|
||
pub fn result_matches(&self, result_bytes: &[u8]) -> bool {
|
||
content_hash(result_bytes) == self.result_hash
|
||
}
|
||
|
||
pub fn receipt_hash(&self) -> [u8; 32] {
|
||
let mut h = blake3::Hasher::new();
|
||
h.update(DOMAIN_JOB_ID);
|
||
h.update(&self.signing_payload());
|
||
h.update(&self.sig);
|
||
*h.finalize().as_bytes()
|
||
}
|
||
|
||
pub fn to_json(&self) -> String {
|
||
let cal = match self.calibration_ref {
|
||
Some(c) => format!("\"{}\"", hx(&c)),
|
||
None => "null".into(),
|
||
};
|
||
let parent = match self.parent_receipt_hash {
|
||
Some(h) => format!("\"{}\"", hx(&h)),
|
||
None => "null".into(),
|
||
};
|
||
format!(
|
||
"{{\"kind\":\"quantum-job\",\"backend_id\":{},\"calibration_ref\":{},\
|
||
\"circuit_hash\":\"{}\",\"grant\":{},\"joules_micro\":{},\"merkle_root\":\"{}\",\
|
||
\"parent_receipt_hash\":{},\"receipt_hash\":\"{}\",\"result_hash\":\"{}\",\
|
||
\"shots\":{},\"sig\":\"{}\",\"signer_id\":{},\"signer_pubkey\":\"{}\"}}",
|
||
serde_json::to_string(&self.backend_id).unwrap(),
|
||
cal,
|
||
hx(&self.circuit_hash),
|
||
self.grant.to_json(),
|
||
self.joules_micro,
|
||
hx(&self.merkle_root),
|
||
parent,
|
||
hx(&self.receipt_hash()),
|
||
hx(&self.result_hash),
|
||
self.shots,
|
||
hx(&self.sig),
|
||
serde_json::to_string(&self.signer_id).unwrap(),
|
||
hx(&self.signer_pubkey),
|
||
)
|
||
}
|
||
|
||
pub fn from_json(s: &str) -> Option<JobReceipt> {
|
||
let v: serde_json::Value = serde_json::from_str(s).ok()?;
|
||
let o = v.as_object()?;
|
||
let opt_hash = |k: &str| match o.get(k) {
|
||
Some(serde_json::Value::String(s)) => from_hex32(s),
|
||
_ => None,
|
||
};
|
||
Some(JobReceipt {
|
||
backend_id: o.get("backend_id")?.as_str()?.to_owned(),
|
||
circuit_hash: from_hex32(o.get("circuit_hash")?.as_str()?)?,
|
||
calibration_ref: opt_hash("calibration_ref"),
|
||
result_hash: from_hex32(o.get("result_hash")?.as_str()?)?,
|
||
shots: o.get("shots")?.as_u64()?,
|
||
merkle_root: from_hex32(o.get("merkle_root")?.as_str()?)?,
|
||
joules_micro: o.get("joules_micro")?.as_u64()?,
|
||
grant: GrantRef::from_json(o.get("grant")?)?,
|
||
parent_receipt_hash: opt_hash("parent_receipt_hash"),
|
||
signer_pubkey: from_hex32(o.get("signer_pubkey")?.as_str()?)?,
|
||
signer_id: o.get("signer_id")?.as_str()?.to_owned(),
|
||
sig: from_hex64(o.get("sig")?.as_str()?)?,
|
||
})
|
||
}
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// MitigationReceipt
|
||
// ---------------------------------------------------------------------------
|
||
|
||
/// A signed, joule-metered, grant-authorized record of ONE classical error-
|
||
/// mitigation post-processing step: it binds the raw measured data and the
|
||
/// (optional, hash-pinned) noise model that went IN to the mitigated estimate
|
||
/// and its error bar that came OUT, plus the method and seed that produced it.
|
||
///
|
||
/// This is the layer nobody signs. Zero-noise extrapolation, readout inversion,
|
||
/// classical shadows, PEC/PEA aggregation — all are deterministic numerics that
|
||
/// today run in float Python (Mitiq, Boulder Opal) with no reproducibility
|
||
/// guarantee and no provenance over the step between the QPU and the reported
|
||
/// number. A `MitigationReceipt` makes `(raw counts, noise-model hash, method,
|
||
/// seed) → (mitigated estimate ± error, joules)` a byte-exact, content-addressed,
|
||
/// budget-gated, auditable object: a quantum result you can take to an auditor.
|
||
///
|
||
/// Estimates are signed fixed-point at scale `2^20` (the WAI quantum unit); an
|
||
/// expectation value of `1.0` is `1_048_576`. The `noise_model_hash` is
|
||
/// deliberately just a hash: WAI does not re-derive a device's learned noise
|
||
/// model — it pins the model's provenance so PEC/PEA/TEM estimators (which
|
||
/// consume an externally-learned sparse Pauli–Lindblad / IC-POVM model) bind
|
||
/// *which* model produced the number without claiming to have learned it.
|
||
#[derive(Clone, Debug, PartialEq)]
|
||
pub struct MitigationReceipt {
|
||
/// Where the raw data came from: `"ibm:torino"`, `"wai.quantum.circuit"`.
|
||
pub backend_id: String,
|
||
/// The mitigation method: `"zne.richardson"`, `"zne.linear"`,
|
||
/// `"readout.tensored"`, `"shadow.pauli"`.
|
||
pub method: String,
|
||
/// Human label of the estimated observable: `"ZZ"`, `"<H>"`. Informational.
|
||
pub observable: String,
|
||
/// Content hash of the canonical raw measured data (the counts table / the
|
||
/// noise-scaled expectation table / the shadow snapshots).
|
||
pub input_hash: [u8; 32],
|
||
/// Content hash of the noise model the method consumed, if any (the readout
|
||
/// assignment matrix, the learned sparse Pauli–Lindblad model). `None` for
|
||
/// methods that need no learned model (ZNE, classical shadows).
|
||
pub noise_model_hash: Option<[u8; 32]>,
|
||
/// The unmitigated expectation (fixed-point, scale 2^20), signed.
|
||
pub raw_estimate_fx: i64,
|
||
/// The mitigated expectation (fixed-point, scale 2^20), signed.
|
||
pub mitigated_estimate_fx: i64,
|
||
/// One-sigma / fit-residual error bar on the mitigated estimate (fixed-point).
|
||
pub error_bar_fx: i64,
|
||
/// Total shots behind the raw data (informational).
|
||
pub shots: u64,
|
||
/// Merkle root over `[input_hash, (noise_model_hash?), result_leaf]`, where
|
||
/// `result_leaf` binds `(method, raw, mitigated, error)`.
|
||
pub merkle_root: [u8; 32],
|
||
/// Measured energy of the post-processing (µJ). Attested by the signature.
|
||
pub joules_micro: u64,
|
||
pub grant: GrantRef,
|
||
pub parent_receipt_hash: Option<[u8; 32]>,
|
||
pub signer_pubkey: [u8; 32],
|
||
pub signer_id: String,
|
||
pub sig: [u8; 64],
|
||
}
|
||
|
||
impl MitigationReceipt {
|
||
fn result_leaf(&self) -> [u8; 32] {
|
||
let mut h = blake3::Hasher::new();
|
||
h.update(DOMAIN_MIT_RESULT);
|
||
h.update(self.method.as_bytes());
|
||
h.update(&[0]);
|
||
h.update(&self.raw_estimate_fx.to_be_bytes());
|
||
h.update(&self.mitigated_estimate_fx.to_be_bytes());
|
||
h.update(&self.error_bar_fx.to_be_bytes());
|
||
*h.finalize().as_bytes()
|
||
}
|
||
|
||
fn leaves(&self) -> Vec<[u8; 32]> {
|
||
let mut v = vec![self.input_hash];
|
||
if let Some(m) = self.noise_model_hash {
|
||
v.push(m);
|
||
}
|
||
v.push(self.result_leaf());
|
||
v
|
||
}
|
||
|
||
fn signing_payload(&self) -> Vec<u8> {
|
||
let mut o = DOMAIN_MIT.to_vec();
|
||
o.extend_from_slice(self.backend_id.as_bytes());
|
||
o.push(0);
|
||
o.extend_from_slice(self.method.as_bytes());
|
||
o.push(0);
|
||
o.extend_from_slice(self.observable.as_bytes());
|
||
o.push(0);
|
||
o.extend_from_slice(&self.input_hash);
|
||
match self.noise_model_hash {
|
||
Some(m) => {
|
||
o.push(1);
|
||
o.extend_from_slice(&m);
|
||
}
|
||
None => o.push(0),
|
||
}
|
||
o.extend_from_slice(&self.raw_estimate_fx.to_be_bytes());
|
||
o.extend_from_slice(&self.mitigated_estimate_fx.to_be_bytes());
|
||
o.extend_from_slice(&self.error_bar_fx.to_be_bytes());
|
||
o.extend_from_slice(&self.shots.to_be_bytes());
|
||
o.extend_from_slice(&self.merkle_root);
|
||
o.extend_from_slice(&self.joules_micro.to_be_bytes());
|
||
self.grant.append_to(&mut o);
|
||
match self.parent_receipt_hash {
|
||
Some(h) => {
|
||
o.push(1);
|
||
o.extend_from_slice(&h);
|
||
}
|
||
None => o.push(0),
|
||
}
|
||
o.extend_from_slice(&self.signer_pubkey);
|
||
o.extend_from_slice(self.signer_id.as_bytes());
|
||
o
|
||
}
|
||
|
||
/// Build and sign a mitigation receipt.
|
||
#[allow(clippy::too_many_arguments)]
|
||
pub fn seal(
|
||
signer: &SigningKey,
|
||
signer_id: impl Into<String>,
|
||
backend_id: impl Into<String>,
|
||
method: impl Into<String>,
|
||
observable: impl Into<String>,
|
||
input_hash: [u8; 32],
|
||
noise_model_hash: Option<[u8; 32]>,
|
||
raw_estimate_fx: i64,
|
||
mitigated_estimate_fx: i64,
|
||
error_bar_fx: i64,
|
||
shots: u64,
|
||
joules_micro: u64,
|
||
grant: GrantRef,
|
||
parent_receipt_hash: Option<[u8; 32]>,
|
||
) -> MitigationReceipt {
|
||
let mut r = MitigationReceipt {
|
||
backend_id: backend_id.into(),
|
||
method: method.into(),
|
||
observable: observable.into(),
|
||
input_hash,
|
||
noise_model_hash,
|
||
raw_estimate_fx,
|
||
mitigated_estimate_fx,
|
||
error_bar_fx,
|
||
shots,
|
||
merkle_root: [0u8; 32],
|
||
joules_micro,
|
||
grant,
|
||
parent_receipt_hash,
|
||
signer_pubkey: signer.verifying_key().to_bytes(),
|
||
signer_id: signer_id.into(),
|
||
sig: [0u8; 64],
|
||
};
|
||
r.merkle_root = merkle_root(&r.leaves());
|
||
r.sig = signer.sign(&r.signing_payload()).to_bytes();
|
||
r
|
||
}
|
||
|
||
pub fn honors_budget(&self) -> bool {
|
||
self.joules_micro <= self.grant.joule_ceiling_micro
|
||
}
|
||
|
||
/// Merkle recomputes (the mitigated number is bound to the exact inputs and
|
||
/// noise model that produced it), joules within the grant ceiling, signature
|
||
/// valid. Tampering with the raw data, the noise model, or the reported
|
||
/// estimate breaks verification.
|
||
pub fn verify(&self) -> bool {
|
||
if merkle_root(&self.leaves()) != self.merkle_root {
|
||
return false;
|
||
}
|
||
if !self.honors_budget() {
|
||
return false;
|
||
}
|
||
let Ok(k) = VerifyingKey::from_bytes(&self.signer_pubkey) else {
|
||
return false;
|
||
};
|
||
k.verify(&self.signing_payload(), &Signature::from_bytes(&self.sig))
|
||
.is_ok()
|
||
}
|
||
|
||
/// Confirm the raw measured data binds to this receipt's `input_hash` — what
|
||
/// makes "mitigated ⟨ZZ⟩ = 0.87" checkable against the shots it came from.
|
||
pub fn input_matches(&self, input_bytes: &[u8]) -> bool {
|
||
content_hash(input_bytes) == self.input_hash
|
||
}
|
||
|
||
/// Confirm a supplied noise model binds to this receipt's `noise_model_hash`.
|
||
pub fn noise_model_matches(&self, model_bytes: &[u8]) -> bool {
|
||
self.noise_model_hash == Some(content_hash(model_bytes))
|
||
}
|
||
|
||
pub fn receipt_hash(&self) -> [u8; 32] {
|
||
let mut h = blake3::Hasher::new();
|
||
h.update(DOMAIN_MIT_ID);
|
||
h.update(&self.signing_payload());
|
||
h.update(&self.sig);
|
||
*h.finalize().as_bytes()
|
||
}
|
||
|
||
pub fn to_json(&self) -> String {
|
||
let nm = match self.noise_model_hash {
|
||
Some(m) => format!("\"{}\"", hx(&m)),
|
||
None => "null".into(),
|
||
};
|
||
let parent = match self.parent_receipt_hash {
|
||
Some(h) => format!("\"{}\"", hx(&h)),
|
||
None => "null".into(),
|
||
};
|
||
format!(
|
||
"{{\"kind\":\"quantum-mitigate\",\"backend_id\":{},\"error_bar_fx\":{},\
|
||
\"grant\":{},\"input_hash\":\"{}\",\"joules_micro\":{},\"merkle_root\":\"{}\",\
|
||
\"method\":{},\"mitigated_estimate_fx\":{},\"noise_model_hash\":{},\
|
||
\"observable\":{},\"parent_receipt_hash\":{},\"raw_estimate_fx\":{},\
|
||
\"receipt_hash\":\"{}\",\"shots\":{},\"sig\":\"{}\",\"signer_id\":{},\
|
||
\"signer_pubkey\":\"{}\"}}",
|
||
serde_json::to_string(&self.backend_id).unwrap(),
|
||
self.error_bar_fx,
|
||
self.grant.to_json(),
|
||
hx(&self.input_hash),
|
||
self.joules_micro,
|
||
hx(&self.merkle_root),
|
||
serde_json::to_string(&self.method).unwrap(),
|
||
self.mitigated_estimate_fx,
|
||
nm,
|
||
serde_json::to_string(&self.observable).unwrap(),
|
||
parent,
|
||
self.raw_estimate_fx,
|
||
hx(&self.receipt_hash()),
|
||
self.shots,
|
||
hx(&self.sig),
|
||
serde_json::to_string(&self.signer_id).unwrap(),
|
||
hx(&self.signer_pubkey),
|
||
)
|
||
}
|
||
|
||
pub fn from_json(s: &str) -> Option<MitigationReceipt> {
|
||
let v: serde_json::Value = serde_json::from_str(s).ok()?;
|
||
let o = v.as_object()?;
|
||
let opt_hash = |k: &str| match o.get(k) {
|
||
Some(serde_json::Value::String(s)) => from_hex32(s),
|
||
_ => None,
|
||
};
|
||
Some(MitigationReceipt {
|
||
backend_id: o.get("backend_id")?.as_str()?.to_owned(),
|
||
method: o.get("method")?.as_str()?.to_owned(),
|
||
observable: o.get("observable")?.as_str()?.to_owned(),
|
||
input_hash: from_hex32(o.get("input_hash")?.as_str()?)?,
|
||
noise_model_hash: opt_hash("noise_model_hash"),
|
||
raw_estimate_fx: o.get("raw_estimate_fx")?.as_i64()?,
|
||
mitigated_estimate_fx: o.get("mitigated_estimate_fx")?.as_i64()?,
|
||
error_bar_fx: o.get("error_bar_fx")?.as_i64()?,
|
||
shots: o.get("shots")?.as_u64()?,
|
||
merkle_root: from_hex32(o.get("merkle_root")?.as_str()?)?,
|
||
joules_micro: o.get("joules_micro")?.as_u64()?,
|
||
grant: GrantRef::from_json(o.get("grant")?)?,
|
||
parent_receipt_hash: opt_hash("parent_receipt_hash"),
|
||
signer_pubkey: from_hex32(o.get("signer_pubkey")?.as_str()?)?,
|
||
signer_id: o.get("signer_id")?.as_str()?.to_owned(),
|
||
sig: from_hex64(o.get("sig")?.as_str()?)?,
|
||
})
|
||
}
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// DecodeReceipt
|
||
// ---------------------------------------------------------------------------
|
||
|
||
/// A signed, joule-metered, grant-authorized record of ONE quantum
|
||
/// error-correction DECODE: it binds the code (its parity-check matrix), the
|
||
/// measured syndrome, and the decoder parameters that went IN, to the correction
|
||
/// and convergence that came OUT — plus the classical **work** (message passes)
|
||
/// and **joules** the decode cost.
|
||
///
|
||
/// A real-time QEC decoder is the classical bottleneck of a fault-tolerant
|
||
/// machine: it must return a correction for every syndrome round inside the code
|
||
/// cycle, and its energy budget is a hard system constraint. Yet no decoder today
|
||
/// reports **joules-per-decode**, and none signs the correction it emitted. This
|
||
/// receipt does both — `(check-matrix hash, syndrome hash, decoder params) →
|
||
/// (correction hash, converged, work, joules)`, content-addressed and budget-
|
||
/// gated. The decode is byte-exact (a deterministic fixed-point decoder), so the
|
||
/// correction reproduces from the receipt and verifies with a public key.
|
||
///
|
||
/// `logical_success` is deliberately NOT a field: whether a correction leaves a
|
||
/// logical error is knowable only in simulation (it needs the true error), not to
|
||
/// a real decoder seeing only a syndrome. The receipt attests what a real decoder
|
||
/// actually knows: it converged (the correction reproduces the syndrome) at this
|
||
/// energy cost.
|
||
#[derive(Clone, Debug, PartialEq)]
|
||
pub struct DecodeReceipt {
|
||
/// The code decoded: `"toric:L5"`, `"bb:[[72,12]]"`.
|
||
pub code_id: String,
|
||
/// The decoder: `"relay-bp.min-sum"`.
|
||
pub decoder: String,
|
||
/// Content hash of the canonical parity-check matrix (the code's identity).
|
||
pub check_matrix_hash: [u8; 32],
|
||
/// Content hash of the input syndrome.
|
||
pub syndrome_hash: [u8; 32],
|
||
/// Content hash of the emitted correction.
|
||
pub correction_hash: [u8; 32],
|
||
/// The correction reproduces the syndrome (`H·correction == syndrome`).
|
||
pub converged: bool,
|
||
/// Relay legs / BP rounds the decode used (informational, part of the work).
|
||
pub rounds: u32,
|
||
/// Classical work: total messages passed. Deterministic and byte-portable —
|
||
/// the exact figure the modeled/measured joules is derived from.
|
||
pub work_messages: u64,
|
||
/// Measured (or modeled) energy of the decode (µJ). Attested by the signature.
|
||
pub joules_micro: u64,
|
||
pub grant: GrantRef,
|
||
pub parent_receipt_hash: Option<[u8; 32]>,
|
||
pub signer_pubkey: [u8; 32],
|
||
pub signer_id: String,
|
||
pub sig: [u8; 64],
|
||
}
|
||
|
||
impl DecodeReceipt {
|
||
fn leaves(&self) -> Vec<[u8; 32]> {
|
||
vec![
|
||
self.check_matrix_hash,
|
||
self.syndrome_hash,
|
||
self.correction_hash,
|
||
]
|
||
}
|
||
|
||
fn signing_payload(&self) -> Vec<u8> {
|
||
let mut o = DOMAIN_DEC.to_vec();
|
||
o.extend_from_slice(self.code_id.as_bytes());
|
||
o.push(0);
|
||
o.extend_from_slice(self.decoder.as_bytes());
|
||
o.push(0);
|
||
o.extend_from_slice(&self.check_matrix_hash);
|
||
o.extend_from_slice(&self.syndrome_hash);
|
||
o.extend_from_slice(&self.correction_hash);
|
||
o.push(self.converged as u8);
|
||
o.extend_from_slice(&self.rounds.to_be_bytes());
|
||
o.extend_from_slice(&self.work_messages.to_be_bytes());
|
||
o.extend_from_slice(&self.merkle_root_calc());
|
||
o.extend_from_slice(&self.joules_micro.to_be_bytes());
|
||
self.grant.append_to(&mut o);
|
||
match self.parent_receipt_hash {
|
||
Some(h) => {
|
||
o.push(1);
|
||
o.extend_from_slice(&h);
|
||
}
|
||
None => o.push(0),
|
||
}
|
||
o.extend_from_slice(&self.signer_pubkey);
|
||
o.extend_from_slice(self.signer_id.as_bytes());
|
||
o
|
||
}
|
||
|
||
fn merkle_root_calc(&self) -> [u8; 32] {
|
||
merkle_root(&self.leaves())
|
||
}
|
||
|
||
/// Build and sign a decode receipt.
|
||
#[allow(clippy::too_many_arguments)]
|
||
pub fn seal(
|
||
signer: &SigningKey,
|
||
signer_id: impl Into<String>,
|
||
code_id: impl Into<String>,
|
||
decoder: impl Into<String>,
|
||
check_matrix_hash: [u8; 32],
|
||
syndrome_hash: [u8; 32],
|
||
correction_hash: [u8; 32],
|
||
converged: bool,
|
||
rounds: u32,
|
||
work_messages: u64,
|
||
joules_micro: u64,
|
||
grant: GrantRef,
|
||
parent_receipt_hash: Option<[u8; 32]>,
|
||
) -> DecodeReceipt {
|
||
let mut r = DecodeReceipt {
|
||
code_id: code_id.into(),
|
||
decoder: decoder.into(),
|
||
check_matrix_hash,
|
||
syndrome_hash,
|
||
correction_hash,
|
||
converged,
|
||
rounds,
|
||
work_messages,
|
||
joules_micro,
|
||
grant,
|
||
parent_receipt_hash,
|
||
signer_pubkey: signer.verifying_key().to_bytes(),
|
||
signer_id: signer_id.into(),
|
||
sig: [0u8; 64],
|
||
};
|
||
r.sig = signer.sign(&r.signing_payload()).to_bytes();
|
||
r
|
||
}
|
||
|
||
pub fn honors_budget(&self) -> bool {
|
||
self.joules_micro <= self.grant.joule_ceiling_micro
|
||
}
|
||
|
||
/// Merkle recomputes (the correction binds to the exact code + syndrome),
|
||
/// joules within the grant ceiling, signature valid.
|
||
pub fn verify(&self) -> bool {
|
||
if !self.honors_budget() {
|
||
return false;
|
||
}
|
||
let Ok(k) = VerifyingKey::from_bytes(&self.signer_pubkey) else {
|
||
return false;
|
||
};
|
||
k.verify(&self.signing_payload(), &Signature::from_bytes(&self.sig))
|
||
.is_ok()
|
||
}
|
||
|
||
/// Confirm the correction bytes bind to this receipt's `correction_hash`.
|
||
pub fn correction_matches(&self, correction_bytes: &[u8]) -> bool {
|
||
content_hash(correction_bytes) == self.correction_hash
|
||
}
|
||
|
||
/// Confirm a supplied parity-check matrix binds to this receipt's code.
|
||
pub fn code_matches(&self, matrix_bytes: &[u8]) -> bool {
|
||
content_hash(matrix_bytes) == self.check_matrix_hash
|
||
}
|
||
|
||
pub fn receipt_hash(&self) -> [u8; 32] {
|
||
let mut h = blake3::Hasher::new();
|
||
h.update(DOMAIN_DEC_ID);
|
||
h.update(&self.signing_payload());
|
||
h.update(&self.sig);
|
||
*h.finalize().as_bytes()
|
||
}
|
||
|
||
pub fn to_json(&self) -> String {
|
||
let parent = match self.parent_receipt_hash {
|
||
Some(h) => format!("\"{}\"", hx(&h)),
|
||
None => "null".into(),
|
||
};
|
||
format!(
|
||
"{{\"kind\":\"quantum-decode\",\"check_matrix_hash\":\"{}\",\"code_id\":{},\
|
||
\"converged\":{},\"correction_hash\":\"{}\",\"decoder\":{},\"grant\":{},\
|
||
\"joules_micro\":{},\"merkle_root\":\"{}\",\"parent_receipt_hash\":{},\
|
||
\"receipt_hash\":\"{}\",\"rounds\":{},\"sig\":\"{}\",\"signer_id\":{},\
|
||
\"signer_pubkey\":\"{}\",\"syndrome_hash\":\"{}\",\"work_messages\":{}}}",
|
||
hx(&self.check_matrix_hash),
|
||
serde_json::to_string(&self.code_id).unwrap(),
|
||
self.converged,
|
||
hx(&self.correction_hash),
|
||
serde_json::to_string(&self.decoder).unwrap(),
|
||
self.grant.to_json(),
|
||
self.joules_micro,
|
||
hx(&self.merkle_root_calc()),
|
||
parent,
|
||
hx(&self.receipt_hash()),
|
||
self.rounds,
|
||
hx(&self.sig),
|
||
serde_json::to_string(&self.signer_id).unwrap(),
|
||
hx(&self.signer_pubkey),
|
||
hx(&self.syndrome_hash),
|
||
self.work_messages,
|
||
)
|
||
}
|
||
|
||
pub fn from_json(s: &str) -> Option<DecodeReceipt> {
|
||
let v: serde_json::Value = serde_json::from_str(s).ok()?;
|
||
let o = v.as_object()?;
|
||
let parent = match o.get("parent_receipt_hash") {
|
||
Some(serde_json::Value::String(s)) => Some(from_hex32(s)?),
|
||
_ => None,
|
||
};
|
||
Some(DecodeReceipt {
|
||
code_id: o.get("code_id")?.as_str()?.to_owned(),
|
||
decoder: o.get("decoder")?.as_str()?.to_owned(),
|
||
check_matrix_hash: from_hex32(o.get("check_matrix_hash")?.as_str()?)?,
|
||
syndrome_hash: from_hex32(o.get("syndrome_hash")?.as_str()?)?,
|
||
correction_hash: from_hex32(o.get("correction_hash")?.as_str()?)?,
|
||
converged: o.get("converged")?.as_bool()?,
|
||
rounds: o.get("rounds")?.as_u64()? as u32,
|
||
work_messages: o.get("work_messages")?.as_u64()?,
|
||
joules_micro: o.get("joules_micro")?.as_u64()?,
|
||
grant: GrantRef::from_json(o.get("grant")?)?,
|
||
parent_receipt_hash: parent,
|
||
signer_pubkey: from_hex32(o.get("signer_pubkey")?.as_str()?)?,
|
||
signer_id: o.get("signer_id")?.as_str()?.to_owned(),
|
||
sig: from_hex64(o.get("sig")?.as_str()?)?,
|
||
})
|
||
}
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// CompileReceipt
|
||
// ---------------------------------------------------------------------------
|
||
|
||
/// A signed, joule-metered, grant-authorized record that a COMPILED circuit — a
|
||
/// routed / optimized / hardware-mapped circuit — **is equivalent to its source**.
|
||
///
|
||
/// A quantum compiler routes a circuit onto a device's coupling graph (inserting
|
||
/// SWAPs), fuses gates, and remaps qubits. Whether the compiled circuit still
|
||
/// computes the same thing is a correctness obligation that today is checked (if
|
||
/// at all) by an unsigned, non-reproducible tool. This receipt binds the source
|
||
/// circuit, the compiled circuit, and the coupling graph to a `equivalent` verdict
|
||
/// produced by a **deterministic, byte-exact** equivalence check (a Clifford
|
||
/// stabilizer-tableau comparison), sealed the same way as the rest of the family.
|
||
/// A compiled circuit you can put in an auditor's hands, reproduce from the git
|
||
/// repo, and check with a public key.
|
||
///
|
||
/// `equivalent` is what a real compiler DOES know (it can run the same
|
||
/// deterministic check), so unlike a decode's logical-success it belongs in the
|
||
/// receipt.
|
||
#[derive(Clone, Debug, PartialEq)]
|
||
pub struct CompileReceipt {
|
||
/// Content hash of the source (logical) circuit.
|
||
pub source_hash: [u8; 32],
|
||
/// Content hash of the compiled circuit (routed + its final qubit mapping).
|
||
pub target_hash: [u8; 32],
|
||
/// Content hash of the hardware coupling graph the circuit was routed onto.
|
||
pub coupling_hash: [u8; 32],
|
||
/// The equivalence method: `"clifford-tableau"`.
|
||
pub method: String,
|
||
/// The compiled circuit was proven equivalent to the source.
|
||
pub equivalent: bool,
|
||
/// SWAP gates the router inserted (informational, part of the work).
|
||
pub swaps: u32,
|
||
/// Classical work: tableau generator-updates performed. Deterministic.
|
||
pub work: u64,
|
||
/// Measured (or modeled) energy of the compile+check (µJ). Attested.
|
||
pub joules_micro: u64,
|
||
pub grant: GrantRef,
|
||
pub parent_receipt_hash: Option<[u8; 32]>,
|
||
pub signer_pubkey: [u8; 32],
|
||
pub signer_id: String,
|
||
pub sig: [u8; 64],
|
||
}
|
||
|
||
impl CompileReceipt {
|
||
fn leaves(&self) -> Vec<[u8; 32]> {
|
||
vec![self.source_hash, self.target_hash, self.coupling_hash]
|
||
}
|
||
fn merkle(&self) -> [u8; 32] {
|
||
merkle_root(&self.leaves())
|
||
}
|
||
|
||
fn signing_payload(&self) -> Vec<u8> {
|
||
let mut o = DOMAIN_CMP.to_vec();
|
||
o.extend_from_slice(&self.source_hash);
|
||
o.extend_from_slice(&self.target_hash);
|
||
o.extend_from_slice(&self.coupling_hash);
|
||
o.extend_from_slice(self.method.as_bytes());
|
||
o.push(0);
|
||
o.push(self.equivalent as u8);
|
||
o.extend_from_slice(&self.swaps.to_be_bytes());
|
||
o.extend_from_slice(&self.work.to_be_bytes());
|
||
o.extend_from_slice(&self.merkle());
|
||
o.extend_from_slice(&self.joules_micro.to_be_bytes());
|
||
self.grant.append_to(&mut o);
|
||
match self.parent_receipt_hash {
|
||
Some(h) => {
|
||
o.push(1);
|
||
o.extend_from_slice(&h);
|
||
}
|
||
None => o.push(0),
|
||
}
|
||
o.extend_from_slice(&self.signer_pubkey);
|
||
o.extend_from_slice(self.signer_id.as_bytes());
|
||
o
|
||
}
|
||
|
||
#[allow(clippy::too_many_arguments)]
|
||
pub fn seal(
|
||
signer: &SigningKey,
|
||
signer_id: impl Into<String>,
|
||
method: impl Into<String>,
|
||
source_hash: [u8; 32],
|
||
target_hash: [u8; 32],
|
||
coupling_hash: [u8; 32],
|
||
equivalent: bool,
|
||
swaps: u32,
|
||
work: u64,
|
||
joules_micro: u64,
|
||
grant: GrantRef,
|
||
parent_receipt_hash: Option<[u8; 32]>,
|
||
) -> CompileReceipt {
|
||
let mut r = CompileReceipt {
|
||
source_hash,
|
||
target_hash,
|
||
coupling_hash,
|
||
method: method.into(),
|
||
equivalent,
|
||
swaps,
|
||
work,
|
||
joules_micro,
|
||
grant,
|
||
parent_receipt_hash,
|
||
signer_pubkey: signer.verifying_key().to_bytes(),
|
||
signer_id: signer_id.into(),
|
||
sig: [0u8; 64],
|
||
};
|
||
r.sig = signer.sign(&r.signing_payload()).to_bytes();
|
||
r
|
||
}
|
||
|
||
pub fn honors_budget(&self) -> bool {
|
||
self.joules_micro <= self.grant.joule_ceiling_micro
|
||
}
|
||
|
||
/// Signature valid, joules within budget. A verifier who re-runs the
|
||
/// deterministic equivalence check on the bound circuits can confirm the
|
||
/// `equivalent` verdict independently.
|
||
pub fn verify(&self) -> bool {
|
||
if !self.honors_budget() {
|
||
return false;
|
||
}
|
||
let Ok(k) = VerifyingKey::from_bytes(&self.signer_pubkey) else {
|
||
return false;
|
||
};
|
||
k.verify(&self.signing_payload(), &Signature::from_bytes(&self.sig))
|
||
.is_ok()
|
||
}
|
||
|
||
pub fn source_matches(&self, bytes: &[u8]) -> bool {
|
||
content_hash(bytes) == self.source_hash
|
||
}
|
||
pub fn target_matches(&self, bytes: &[u8]) -> bool {
|
||
content_hash(bytes) == self.target_hash
|
||
}
|
||
|
||
pub fn receipt_hash(&self) -> [u8; 32] {
|
||
let mut h = blake3::Hasher::new();
|
||
h.update(DOMAIN_CMP_ID);
|
||
h.update(&self.signing_payload());
|
||
h.update(&self.sig);
|
||
*h.finalize().as_bytes()
|
||
}
|
||
|
||
pub fn to_json(&self) -> String {
|
||
let parent = match self.parent_receipt_hash {
|
||
Some(h) => format!("\"{}\"", hx(&h)),
|
||
None => "null".into(),
|
||
};
|
||
format!(
|
||
"{{\"kind\":\"quantum-compile\",\"coupling_hash\":\"{}\",\"equivalent\":{},\
|
||
\"grant\":{},\"joules_micro\":{},\"merkle_root\":\"{}\",\"method\":{},\
|
||
\"parent_receipt_hash\":{},\"receipt_hash\":\"{}\",\"sig\":\"{}\",\
|
||
\"signer_id\":{},\"signer_pubkey\":\"{}\",\"source_hash\":\"{}\",\"swaps\":{},\
|
||
\"target_hash\":\"{}\",\"work\":{}}}",
|
||
hx(&self.coupling_hash),
|
||
self.equivalent,
|
||
self.grant.to_json(),
|
||
self.joules_micro,
|
||
hx(&self.merkle()),
|
||
serde_json::to_string(&self.method).unwrap(),
|
||
parent,
|
||
hx(&self.receipt_hash()),
|
||
hx(&self.sig),
|
||
serde_json::to_string(&self.signer_id).unwrap(),
|
||
hx(&self.signer_pubkey),
|
||
hx(&self.source_hash),
|
||
self.swaps,
|
||
hx(&self.target_hash),
|
||
self.work,
|
||
)
|
||
}
|
||
|
||
pub fn from_json(s: &str) -> Option<CompileReceipt> {
|
||
let v: serde_json::Value = serde_json::from_str(s).ok()?;
|
||
let o = v.as_object()?;
|
||
let parent = match o.get("parent_receipt_hash") {
|
||
Some(serde_json::Value::String(s)) => Some(from_hex32(s)?),
|
||
_ => None,
|
||
};
|
||
Some(CompileReceipt {
|
||
source_hash: from_hex32(o.get("source_hash")?.as_str()?)?,
|
||
target_hash: from_hex32(o.get("target_hash")?.as_str()?)?,
|
||
coupling_hash: from_hex32(o.get("coupling_hash")?.as_str()?)?,
|
||
method: o.get("method")?.as_str()?.to_owned(),
|
||
equivalent: o.get("equivalent")?.as_bool()?,
|
||
swaps: o.get("swaps")?.as_u64()? as u32,
|
||
work: o.get("work")?.as_u64()?,
|
||
joules_micro: o.get("joules_micro")?.as_u64()?,
|
||
grant: GrantRef::from_json(o.get("grant")?)?,
|
||
parent_receipt_hash: parent,
|
||
signer_pubkey: from_hex32(o.get("signer_pubkey")?.as_str()?)?,
|
||
signer_id: o.get("signer_id")?.as_str()?.to_owned(),
|
||
sig: from_hex64(o.get("sig")?.as_str()?)?,
|
||
})
|
||
}
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// RearrangeReceipt
|
||
// ---------------------------------------------------------------------------
|
||
|
||
/// A signed, joule-metered, grant-authorized record of a neutral-atom
|
||
/// REARRANGEMENT plan.
|
||
///
|
||
/// A neutral-atom processor loads atoms stochastically into an array of optical
|
||
/// traps — a random, ~half-full pattern — then uses mobile tweezers to rearrange
|
||
/// them into a **defect-free** target register before the computation. The plan
|
||
/// that does it (which atom moves to which site, in what order) is produced by a
|
||
/// combinatorial solver whose optimality and validity nobody signs. This receipt
|
||
/// binds the initial occupancy, the target sites, and the move plan to a `success`
|
||
/// verdict (every target site filled) and the plan's cost — produced by a
|
||
/// **deterministic** solver (Hungarian / LSAP, minimizing total move distance),
|
||
/// byte-exact and reproducible.
|
||
///
|
||
/// `success` is what the planner DOES know (it re-checks the plan against the
|
||
/// bound configs deterministically), so it belongs in the receipt.
|
||
#[derive(Clone, Debug, PartialEq)]
|
||
pub struct RearrangeReceipt {
|
||
/// Content hash of the initial (loaded) array occupancy.
|
||
pub initial_hash: [u8; 32],
|
||
/// Content hash of the target site set.
|
||
pub target_hash: [u8; 32],
|
||
/// Content hash of the move plan.
|
||
pub plan_hash: [u8; 32],
|
||
/// The solver: `"hungarian-lsap"`.
|
||
pub method: String,
|
||
/// The plan fills every target site from the available atoms.
|
||
pub success: bool,
|
||
/// Atoms actually relocated (moves of nonzero distance).
|
||
pub atoms_moved: u32,
|
||
/// Total move distance (grid units) — the plan's cost. Deterministic.
|
||
pub total_distance: u64,
|
||
/// Measured (or modeled) energy of the solve (µJ). Attested.
|
||
pub joules_micro: u64,
|
||
pub grant: GrantRef,
|
||
pub parent_receipt_hash: Option<[u8; 32]>,
|
||
pub signer_pubkey: [u8; 32],
|
||
pub signer_id: String,
|
||
pub sig: [u8; 64],
|
||
}
|
||
|
||
impl RearrangeReceipt {
|
||
fn merkle(&self) -> [u8; 32] {
|
||
merkle_root(&[self.initial_hash, self.target_hash, self.plan_hash])
|
||
}
|
||
|
||
fn signing_payload(&self) -> Vec<u8> {
|
||
let mut o = DOMAIN_REA.to_vec();
|
||
o.extend_from_slice(&self.initial_hash);
|
||
o.extend_from_slice(&self.target_hash);
|
||
o.extend_from_slice(&self.plan_hash);
|
||
o.extend_from_slice(self.method.as_bytes());
|
||
o.push(0);
|
||
o.push(self.success as u8);
|
||
o.extend_from_slice(&self.atoms_moved.to_be_bytes());
|
||
o.extend_from_slice(&self.total_distance.to_be_bytes());
|
||
o.extend_from_slice(&self.merkle());
|
||
o.extend_from_slice(&self.joules_micro.to_be_bytes());
|
||
self.grant.append_to(&mut o);
|
||
match self.parent_receipt_hash {
|
||
Some(h) => {
|
||
o.push(1);
|
||
o.extend_from_slice(&h);
|
||
}
|
||
None => o.push(0),
|
||
}
|
||
o.extend_from_slice(&self.signer_pubkey);
|
||
o.extend_from_slice(self.signer_id.as_bytes());
|
||
o
|
||
}
|
||
|
||
#[allow(clippy::too_many_arguments)]
|
||
pub fn seal(
|
||
signer: &SigningKey,
|
||
signer_id: impl Into<String>,
|
||
method: impl Into<String>,
|
||
initial_hash: [u8; 32],
|
||
target_hash: [u8; 32],
|
||
plan_hash: [u8; 32],
|
||
success: bool,
|
||
atoms_moved: u32,
|
||
total_distance: u64,
|
||
joules_micro: u64,
|
||
grant: GrantRef,
|
||
parent_receipt_hash: Option<[u8; 32]>,
|
||
) -> RearrangeReceipt {
|
||
let mut r = RearrangeReceipt {
|
||
initial_hash,
|
||
target_hash,
|
||
plan_hash,
|
||
method: method.into(),
|
||
success,
|
||
atoms_moved,
|
||
total_distance,
|
||
joules_micro,
|
||
grant,
|
||
parent_receipt_hash,
|
||
signer_pubkey: signer.verifying_key().to_bytes(),
|
||
signer_id: signer_id.into(),
|
||
sig: [0u8; 64],
|
||
};
|
||
r.sig = signer.sign(&r.signing_payload()).to_bytes();
|
||
r
|
||
}
|
||
|
||
pub fn honors_budget(&self) -> bool {
|
||
self.joules_micro <= self.grant.joule_ceiling_micro
|
||
}
|
||
|
||
pub fn verify(&self) -> bool {
|
||
if !self.honors_budget() {
|
||
return false;
|
||
}
|
||
let Ok(k) = VerifyingKey::from_bytes(&self.signer_pubkey) else {
|
||
return false;
|
||
};
|
||
k.verify(&self.signing_payload(), &Signature::from_bytes(&self.sig))
|
||
.is_ok()
|
||
}
|
||
|
||
pub fn plan_matches(&self, bytes: &[u8]) -> bool {
|
||
content_hash(bytes) == self.plan_hash
|
||
}
|
||
|
||
pub fn receipt_hash(&self) -> [u8; 32] {
|
||
let mut h = blake3::Hasher::new();
|
||
h.update(DOMAIN_REA_ID);
|
||
h.update(&self.signing_payload());
|
||
h.update(&self.sig);
|
||
*h.finalize().as_bytes()
|
||
}
|
||
|
||
pub fn to_json(&self) -> String {
|
||
let parent = match self.parent_receipt_hash {
|
||
Some(h) => format!("\"{}\"", hx(&h)),
|
||
None => "null".into(),
|
||
};
|
||
format!(
|
||
"{{\"kind\":\"quantum-rearrange\",\"atoms_moved\":{},\"grant\":{},\
|
||
\"initial_hash\":\"{}\",\"joules_micro\":{},\"merkle_root\":\"{}\",\"method\":{},\
|
||
\"parent_receipt_hash\":{},\"plan_hash\":\"{}\",\"receipt_hash\":\"{}\",\"sig\":\"{}\",\
|
||
\"signer_id\":{},\"signer_pubkey\":\"{}\",\"success\":{},\"target_hash\":\"{}\",\
|
||
\"total_distance\":{}}}",
|
||
self.atoms_moved,
|
||
self.grant.to_json(),
|
||
hx(&self.initial_hash),
|
||
self.joules_micro,
|
||
hx(&self.merkle()),
|
||
serde_json::to_string(&self.method).unwrap(),
|
||
parent,
|
||
hx(&self.plan_hash),
|
||
hx(&self.receipt_hash()),
|
||
hx(&self.sig),
|
||
serde_json::to_string(&self.signer_id).unwrap(),
|
||
hx(&self.signer_pubkey),
|
||
self.success,
|
||
hx(&self.target_hash),
|
||
self.total_distance,
|
||
)
|
||
}
|
||
|
||
pub fn from_json(s: &str) -> Option<RearrangeReceipt> {
|
||
let v: serde_json::Value = serde_json::from_str(s).ok()?;
|
||
let o = v.as_object()?;
|
||
let parent = match o.get("parent_receipt_hash") {
|
||
Some(serde_json::Value::String(s)) => Some(from_hex32(s)?),
|
||
_ => None,
|
||
};
|
||
Some(RearrangeReceipt {
|
||
initial_hash: from_hex32(o.get("initial_hash")?.as_str()?)?,
|
||
target_hash: from_hex32(o.get("target_hash")?.as_str()?)?,
|
||
plan_hash: from_hex32(o.get("plan_hash")?.as_str()?)?,
|
||
method: o.get("method")?.as_str()?.to_owned(),
|
||
success: o.get("success")?.as_bool()?,
|
||
atoms_moved: o.get("atoms_moved")?.as_u64()? as u32,
|
||
total_distance: o.get("total_distance")?.as_u64()?,
|
||
joules_micro: o.get("joules_micro")?.as_u64()?,
|
||
grant: GrantRef::from_json(o.get("grant")?)?,
|
||
parent_receipt_hash: parent,
|
||
signer_pubkey: from_hex32(o.get("signer_pubkey")?.as_str()?)?,
|
||
signer_id: o.get("signer_id")?.as_str()?.to_owned(),
|
||
sig: from_hex64(o.get("sig")?.as_str()?)?,
|
||
})
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
fn key(s: u8) -> SigningKey {
|
||
SigningKey::from_bytes(&[s; 32])
|
||
}
|
||
|
||
fn evid() -> Vec<Evidence> {
|
||
vec![Evidence {
|
||
kind: "randomized_benchmarking".into(),
|
||
blob_hash: content_hash(b"<HDF5 RB dataset bytes>"),
|
||
summary: "1q RB fidelity 0.9992".into(),
|
||
}]
|
||
}
|
||
|
||
#[test]
|
||
fn calibration_seals_and_verifies() {
|
||
let grant = GrantRef {
|
||
grant_hash: content_hash(b"grant-A"),
|
||
capability: "quantum.calibrate".into(),
|
||
joule_ceiling_micro: 5_000_000,
|
||
funds_ceiling: Some(100),
|
||
};
|
||
let r = CalibrationReceipt::seal(
|
||
&key(1), "did:key:lab", "qm:opx1000:lab3", "q3",
|
||
content_hash(b"pulse-config-v7"), evid(), 4_200_000, grant, None,
|
||
);
|
||
assert!(r.verify());
|
||
assert!(r.honors_budget());
|
||
// the fidelity claim binds to real bytes
|
||
assert!(r.evidence_matches(0, b"<HDF5 RB dataset bytes>"));
|
||
assert!(!r.evidence_matches(0, b"<tampered dataset>"));
|
||
}
|
||
|
||
#[test]
|
||
fn over_budget_calibration_fails_verify() {
|
||
// The gate ⊗ meter property: joules over the grant ceiling → invalid.
|
||
let grant = GrantRef {
|
||
grant_hash: [0u8; 32],
|
||
capability: "quantum.calibrate".into(),
|
||
joule_ceiling_micro: 1_000_000,
|
||
funds_ceiling: None,
|
||
};
|
||
let r = CalibrationReceipt::seal(
|
||
&key(2), "m", "dev", "q0", content_hash(b"cfg"), evid(),
|
||
2_000_000, // 2 J measured vs 1 J ceiling
|
||
grant, None,
|
||
);
|
||
assert!(!r.honors_budget());
|
||
assert!(!r.verify(), "a run that blew its energy grant must not verify");
|
||
}
|
||
|
||
#[test]
|
||
fn tamper_breaks_calibration() {
|
||
let mut r = CalibrationReceipt::seal(
|
||
&key(3), "m", "dev", "q0", content_hash(b"cfg"), evid(),
|
||
10, GrantRef::unbounded("quantum.calibrate"), None,
|
||
);
|
||
r.joules_micro += 1; // a signed field
|
||
assert!(!r.verify());
|
||
}
|
||
|
||
#[test]
|
||
fn job_binds_to_calibration_state() {
|
||
// Seal a calibration, then a job that ran under it; the cross-link
|
||
// proves "this result ran on a device in this attested state".
|
||
let cal = CalibrationReceipt::seal(
|
||
&key(4), "lab", "dev", "q3", content_hash(b"cfg"), evid(),
|
||
100, GrantRef::unbounded("quantum.calibrate"), None,
|
||
);
|
||
let job = JobReceipt::seal(
|
||
&key(4), "lab", "ionq:aria-1",
|
||
content_hash(b"<QASM circuit>"), content_hash(b"{counts:{00:512,11:488}}"),
|
||
Some(cal.receipt_hash()), 1000, 250, GrantRef::unbounded("quantum.job.run"), None,
|
||
);
|
||
assert!(job.verify());
|
||
assert!(job.verify_against_calibration(&cal));
|
||
assert!(job.result_matches(b"{counts:{00:512,11:488}}"));
|
||
|
||
// a job that names a DIFFERENT calibration state must fail the cross-link
|
||
let other = CalibrationReceipt::seal(
|
||
&key(4), "lab", "dev", "q3", content_hash(b"cfg2"), evid(),
|
||
100, GrantRef::unbounded("quantum.calibrate"), None,
|
||
);
|
||
assert!(!job.verify_against_calibration(&other));
|
||
}
|
||
|
||
#[test]
|
||
fn over_budget_job_fails_verify() {
|
||
let grant = GrantRef {
|
||
grant_hash: [0u8; 32],
|
||
capability: "quantum.job.run".into(),
|
||
joule_ceiling_micro: 200,
|
||
funds_ceiling: None,
|
||
};
|
||
let job = JobReceipt::seal(
|
||
&key(5), "m", "sim", content_hash(b"c"), content_hash(b"r"),
|
||
None, 100, 500, grant, None,
|
||
);
|
||
assert!(!job.verify());
|
||
}
|
||
|
||
#[test]
|
||
fn json_round_trips() {
|
||
let cal = CalibrationReceipt::seal(
|
||
&key(6), "did:key:z", "dev", "cz(q3,q4)", content_hash(b"cfg"), evid(),
|
||
900, GrantRef { grant_hash: content_hash(b"g"), capability: "quantum.calibrate".into(), joule_ceiling_micro: 1_000_000, funds_ceiling: None },
|
||
None,
|
||
);
|
||
assert_eq!(CalibrationReceipt::from_json(&cal.to_json()).unwrap(), cal);
|
||
|
||
let job = JobReceipt::seal(
|
||
&key(6), "did:key:z", "ionq:aria-1", content_hash(b"c"), content_hash(b"r"),
|
||
Some(cal.receipt_hash()), 2048, 42, GrantRef::unbounded("quantum.job.run"), Some(cal.receipt_hash()),
|
||
);
|
||
let back = JobReceipt::from_json(&job.to_json()).unwrap();
|
||
assert_eq!(back, job);
|
||
assert!(back.verify());
|
||
}
|
||
|
||
#[test]
|
||
fn calibration_lineage_chains() {
|
||
let g = GrantRef::unbounded("quantum.calibrate");
|
||
let v1 = CalibrationReceipt::seal(&key(7), "m", "dev", "q0", content_hash(b"c1"), evid(), 10, g.clone(), None);
|
||
let v2 = CalibrationReceipt::seal(&key(7), "m", "dev", "q0", content_hash(b"c2"), evid(), 12, g, Some(v1.receipt_hash()));
|
||
assert!(v2.verify());
|
||
assert_eq!(v2.parent_receipt_hash, Some(v1.receipt_hash()));
|
||
}
|
||
|
||
#[test]
|
||
fn mitigation_seals_and_binds_inputs() {
|
||
// A ZNE step: raw counts in, mitigated estimate out, no learned model.
|
||
let raw = b"<noise-scaled expectation table: (1.0,0.71),(1.5,0.63),(2.0,0.57)>";
|
||
let r = MitigationReceipt::seal(
|
||
&key(1), "did:key:lab", "wai.quantum.circuit", "zne.richardson", "ZZ",
|
||
content_hash(raw), None,
|
||
744_178, // raw 0.71 · 2^20
|
||
912_680, // mit 0.87 · 2^20
|
||
20_971, // ±0.02 · 2^20
|
||
48_000, 900_000, GrantRef::unbounded("quantum.mitigate"), None,
|
||
);
|
||
assert!(r.verify());
|
||
assert!(r.input_matches(raw));
|
||
assert!(!r.input_matches(b"<different shots>"));
|
||
assert!(r.noise_model_hash.is_none());
|
||
}
|
||
|
||
#[test]
|
||
fn mitigation_binds_pinned_noise_model() {
|
||
// A readout-inversion / PEC step binds WHICH noise model produced it.
|
||
let raw = b"<measured counts>";
|
||
let model = b"<readout assignment matrix A>";
|
||
let r = MitigationReceipt::seal(
|
||
&key(2), "lab", "ibm:torino", "readout.tensored", "<Z0>",
|
||
content_hash(raw), Some(content_hash(model)),
|
||
838_861, 1_048_576, 5_242, 32_000, 600_000,
|
||
GrantRef::unbounded("quantum.mitigate"), None,
|
||
);
|
||
assert!(r.verify());
|
||
assert!(r.noise_model_matches(model));
|
||
assert!(!r.noise_model_matches(b"<a different device's matrix>"));
|
||
}
|
||
|
||
#[test]
|
||
fn mitigation_over_budget_and_tamper_fail() {
|
||
let grant = GrantRef {
|
||
grant_hash: [0u8; 32],
|
||
capability: "quantum.mitigate".into(),
|
||
joule_ceiling_micro: 500_000,
|
||
funds_ceiling: None,
|
||
};
|
||
let over = MitigationReceipt::seal(
|
||
&key(3), "m", "sim", "zne.linear", "X",
|
||
content_hash(b"in"), None, 0, 100, 1, 100, 900_000, grant, None,
|
||
);
|
||
assert!(!over.verify(), "a mitigation that blew its energy grant must not verify");
|
||
|
||
let mut r = MitigationReceipt::seal(
|
||
&key(3), "m", "sim", "zne.linear", "X",
|
||
content_hash(b"in"), None, 0, 100, 1, 100, 10,
|
||
GrantRef::unbounded("quantum.mitigate"), None,
|
||
);
|
||
r.mitigated_estimate_fx += 1; // rewrite the reported number
|
||
assert!(!r.verify(), "tampering with the mitigated estimate must break the receipt");
|
||
}
|
||
|
||
#[test]
|
||
fn mitigation_json_round_trips() {
|
||
let r = MitigationReceipt::seal(
|
||
&key(4), "did:key:z", "wai.quantum.circuit", "shadow.pauli", "XYZ",
|
||
content_hash(b"<snapshots>"), None,
|
||
-262_144, -314_573, 15_728, 4096, 42,
|
||
GrantRef { grant_hash: content_hash(b"g"), capability: "quantum.mitigate".into(), joule_ceiling_micro: 1_000_000, funds_ceiling: None },
|
||
None,
|
||
);
|
||
let back = MitigationReceipt::from_json(&r.to_json()).unwrap();
|
||
assert_eq!(back, r);
|
||
assert!(back.verify());
|
||
assert_eq!(back.raw_estimate_fx, -262_144); // signed round-trip
|
||
}
|
||
|
||
#[test]
|
||
fn decode_seals_and_binds() {
|
||
let hmat = b"<parity-check matrix H_Z of the gross code>";
|
||
let synd = b"<syndrome 001011...>";
|
||
let corr = b"<correction 000010...>";
|
||
let r = DecodeReceipt::seal(
|
||
&key(1), "did:key:lab", "bb:[[72,12]]", "relay-bp.min-sum",
|
||
content_hash(hmat), content_hash(synd), content_hash(corr),
|
||
true, 3, 46_656, 900_000, GrantRef::unbounded("quantum.decode"), None,
|
||
);
|
||
assert!(r.verify());
|
||
assert!(r.converged);
|
||
assert!(r.correction_matches(corr));
|
||
assert!(!r.correction_matches(b"<a different correction>"));
|
||
assert!(r.code_matches(hmat));
|
||
}
|
||
|
||
#[test]
|
||
fn decode_over_budget_and_tamper_fail() {
|
||
let grant = GrantRef {
|
||
grant_hash: [0u8; 32],
|
||
capability: "quantum.decode".into(),
|
||
joule_ceiling_micro: 500_000,
|
||
funds_ceiling: None,
|
||
};
|
||
let over = DecodeReceipt::seal(
|
||
&key(2), "m", "toric:L5", "relay-bp.min-sum",
|
||
content_hash(b"H"), content_hash(b"s"), content_hash(b"c"),
|
||
true, 1, 100, 900_000, grant, None,
|
||
);
|
||
assert!(!over.verify(), "a decode that blew its energy grant must not verify");
|
||
|
||
let mut r = DecodeReceipt::seal(
|
||
&key(2), "m", "toric:L5", "relay-bp.min-sum",
|
||
content_hash(b"H"), content_hash(b"s"), content_hash(b"c"),
|
||
true, 1, 100, 10, GrantRef::unbounded("quantum.decode"), None,
|
||
);
|
||
r.work_messages += 1; // rewrite the reported work
|
||
assert!(!r.verify(), "tampering with the reported work must break the receipt");
|
||
}
|
||
|
||
#[test]
|
||
fn decode_json_round_trips() {
|
||
let r = DecodeReceipt::seal(
|
||
&key(3), "did:key:z", "toric:L7", "relay-bp.min-sum",
|
||
content_hash(b"H"), content_hash(b"s"), content_hash(b"c"),
|
||
false, 12, 1_234_567, 42,
|
||
GrantRef { grant_hash: content_hash(b"g"), capability: "quantum.decode".into(), joule_ceiling_micro: 2_000_000, funds_ceiling: None },
|
||
Some(content_hash(b"prev")),
|
||
);
|
||
let back = DecodeReceipt::from_json(&r.to_json()).unwrap();
|
||
assert_eq!(back, r);
|
||
assert!(back.verify());
|
||
assert!(!back.converged); // bool round-trip
|
||
}
|
||
|
||
#[test]
|
||
fn compile_seals_and_binds() {
|
||
let src = b"<logical circuit: H0 CX0,2 ...>";
|
||
let tgt = b"<routed circuit + final map: H0 SWAP1,2 CX0,1 ...>";
|
||
let cpl = b"<coupling: line 0-1-2-3-4>";
|
||
let r = CompileReceipt::seal(
|
||
&key(1), "did:key:lab", "clifford-tableau",
|
||
content_hash(src), content_hash(tgt), content_hash(cpl),
|
||
true, 3, 4096, 300_000, GrantRef::unbounded("quantum.compile"), None,
|
||
);
|
||
assert!(r.verify());
|
||
assert!(r.equivalent);
|
||
assert!(r.source_matches(src));
|
||
assert!(r.target_matches(tgt));
|
||
assert!(!r.target_matches(b"<a tampered routed circuit>"));
|
||
}
|
||
|
||
#[test]
|
||
fn compile_over_budget_and_tamper_fail() {
|
||
let grant = GrantRef {
|
||
grant_hash: [0u8; 32],
|
||
capability: "quantum.compile".into(),
|
||
joule_ceiling_micro: 100_000,
|
||
funds_ceiling: None,
|
||
};
|
||
let over = CompileReceipt::seal(
|
||
&key(2), "m", "clifford-tableau",
|
||
content_hash(b"s"), content_hash(b"t"), content_hash(b"c"),
|
||
true, 0, 10, 900_000, grant, None,
|
||
);
|
||
assert!(!over.verify(), "a compile that blew its energy grant must not verify");
|
||
|
||
let mut r = CompileReceipt::seal(
|
||
&key(2), "m", "clifford-tableau",
|
||
content_hash(b"s"), content_hash(b"t"), content_hash(b"c"),
|
||
true, 0, 10, 5, GrantRef::unbounded("quantum.compile"), None,
|
||
);
|
||
r.equivalent = false; // flip the verdict
|
||
assert!(!r.verify(), "tampering with the equivalence verdict must break the receipt");
|
||
}
|
||
|
||
#[test]
|
||
fn compile_json_round_trips() {
|
||
let r = CompileReceipt::seal(
|
||
&key(3), "did:key:z", "clifford-tableau",
|
||
content_hash(b"s"), content_hash(b"t"), content_hash(b"c"),
|
||
false, 7, 99_999, 21,
|
||
GrantRef { grant_hash: content_hash(b"g"), capability: "quantum.compile".into(), joule_ceiling_micro: 1_000_000, funds_ceiling: None },
|
||
None,
|
||
);
|
||
let back = CompileReceipt::from_json(&r.to_json()).unwrap();
|
||
assert_eq!(back, r);
|
||
assert!(back.verify());
|
||
assert!(!back.equivalent);
|
||
}
|
||
|
||
#[test]
|
||
fn rearrange_seals_binds_and_json_round_trips() {
|
||
let r = RearrangeReceipt::seal(
|
||
&key(1), "did:key:lab", "hungarian-lsap",
|
||
content_hash(b"<loaded 16x16, 58% fill>"),
|
||
content_hash(b"<target 8x8 centered>"),
|
||
content_hash(b"<moves: (12->20),(33->21),...>"),
|
||
true, 41, 318, 450_000, GrantRef::unbounded("quantum.rearrange"), None,
|
||
);
|
||
assert!(r.verify());
|
||
assert!(r.success);
|
||
assert!(r.plan_matches(b"<moves: (12->20),(33->21),...>"));
|
||
assert!(!r.plan_matches(b"<a different plan>"));
|
||
let back = RearrangeReceipt::from_json(&r.to_json()).unwrap();
|
||
assert_eq!(back, r);
|
||
assert!(back.verify());
|
||
}
|
||
|
||
#[test]
|
||
fn rearrange_over_budget_and_tamper_fail() {
|
||
let grant = GrantRef {
|
||
grant_hash: [0u8; 32], capability: "quantum.rearrange".into(),
|
||
joule_ceiling_micro: 100_000, funds_ceiling: None,
|
||
};
|
||
let over = RearrangeReceipt::seal(
|
||
&key(2), "m", "hungarian-lsap",
|
||
content_hash(b"i"), content_hash(b"t"), content_hash(b"p"),
|
||
true, 10, 50, 900_000, grant, None,
|
||
);
|
||
assert!(!over.verify(), "a solve that blew its energy grant must not verify");
|
||
|
||
let mut r = RearrangeReceipt::seal(
|
||
&key(2), "m", "hungarian-lsap",
|
||
content_hash(b"i"), content_hash(b"t"), content_hash(b"p"),
|
||
true, 10, 50, 5, GrantRef::unbounded("quantum.rearrange"), None,
|
||
);
|
||
r.success = false; // flip the verdict
|
||
assert!(!r.verify(), "tampering with the success verdict must break the receipt");
|
||
}
|
||
}
|