QuantumReceipt gains gateset_of, which names the gateset under which a circuit's ops hash to the receipt's circuit_hash, and gateset_note, which turns that into the text to show when a reconstruction fails for that reason. verify_reconstruction stays strict. from_json refuses an n_qubits that does not fit a u8 and an n_ops that does not fit a u32 instead of truncating them. The fabric names its gate kernel and random corpus once per gateset: wai.quantum.circuit2/apply and wai.quantum.circuit2/random-v1 join the circuit/1 names. certify_lanes and lanes::random_circuit take a gateset and draw the same ops from a seed in both. detect_local offers the reference kernel of each gateset. The lanes32 corpus digests for seed 7 and 200 cases are pinned: d18454c6 for circuit/1, as 0.3.41 certified it, and 3dbde276 for circuit2. noisy_expect_z and route_line build their circuits with empty_like, so a derived circuit keeps its source's gateset. from_qasm refuses a gate that names one qubit twice as an invalid circuit when it reads it, in every gateset. quantum_spd's controlled_phase refuses a phase on 32 or more qubits instead of shifting out of range. The QIR and QASM export docs say that a circuit/1 circuit exports ideal angles, not its own numerics. The bell and receipt examples print the capability and handle the fallible calls. Co-Authored-By: Claude Opus 5.5 <[email protected]>
47 lines
2 KiB
Rust
47 lines
2 KiB
Rust
//! Sealing a computation into a signed, energy-accounted receipt — and watching
|
|
//! verification reject a doctored one.
|
|
//!
|
|
//! cargo run --example receipt
|
|
|
|
use wai_quantum::ed25519_dalek::SigningKey;
|
|
use wai_quantum::quantum::{Gateset, Measure};
|
|
use wai_quantum::quantum_receipt::QuantumReceipt;
|
|
use wai_quantum::quantum_toolchain as qt;
|
|
|
|
fn main() {
|
|
let circuit = qt::ghz(3);
|
|
let key = SigningKey::from_bytes(&[42u8; 32]); // demo key; use a real one
|
|
|
|
let receipt = QuantumReceipt::seal(
|
|
&key,
|
|
"did:key:example",
|
|
&circuit,
|
|
Some(Measure { seed: 7, shots: 4096 }),
|
|
0, // measured microjoules; 0 means unmetered
|
|
None,
|
|
)
|
|
.expect("seal");
|
|
|
|
println!("capability {}", circuit.gateset.capability());
|
|
println!("verifies {}", receipt.verify());
|
|
println!("re-simulates to claim {}", receipt.verify_reconstruction(&circuit));
|
|
|
|
// The receipt binds the circuit's gateset: the same ops in circuit/1 do not
|
|
// reconstruct it, and the receipt says which gateset they need.
|
|
let mut v1 = circuit.clone();
|
|
v1.gateset = Gateset::V1;
|
|
println!("same ops, circuit/1 {}", receipt.verify_reconstruction(&v1));
|
|
if let Some(note) = receipt.gateset_note(&v1) {
|
|
println!(" {note}");
|
|
}
|
|
println!("work {} amplitude updates", receipt.work_amp_updates);
|
|
println!("energy {} uJ (0 = unmetered)", receipt.joules_micro);
|
|
|
|
// The work is recomputable by anyone from (n_qubits, n_ops), so a sink cannot
|
|
// inflate it. The energy is only as good as the signature, and says so.
|
|
let json = receipt.to_json();
|
|
let tampered = json.replace("\"work_amp_updates\":24", "\"work_amp_updates\":2400");
|
|
assert_ne!(json, tampered, "expected the work field in the JSON");
|
|
let forged = QuantumReceipt::from_json(&tampered).expect("still parses");
|
|
println!("\nclaiming 100x the work: verifies = {}", forged.verify());
|
|
}
|