//! Emit the AR-1 conformance vector pack. //! //! ```text //! cargo run -p jcp-action-receipt --example emit_vectors -- jcp/conformance/v1 //! ``` //! //! Writes `action_receipt.json` and `action_receipt_meta.json`, following the //! shape the existing JCP packs use: a `meta` file declaring the algorithm, //! profile, public key and steward, and a vectors file carrying signed //! envelopes as hex. //! //! The signing key is a **published fixture**, deliberately: a conformance //! vector nobody else can verify is not a conformance vector. It is the //! all-`0x2a` key and must never be used for anything real. //! //! Both positive and negative vectors ship. The negatives are the point — they //! pin the three structural rules, so an implementation that accepts a //! well-signed but ill-formed receipt fails the pack rather than passing it. use ed25519_dalek::SigningKey; use jcp_action_receipt::{ ActionReceipt, Effect, EffectClass, Egress, EgressClass, verify, }; use jcp_receipt::jcr1; use jcp_schema::ContentId; use serde_json::{Value, json}; /// Published fixture key. Not a secret; never use it for anything real. const FIXTURE_SEED: [u8; 32] = [0x2a; 32]; const STEWARD: &str = "did:example:ar-reference"; fn cid(tag: &str) -> ContentId { ContentId::of_bytes(tag.as_bytes()) } fn base() -> ActionReceipt { ActionReceipt { closed_at: "2026-08-25T12:00:00Z".into(), grant_id: cid("grant/root"), chain_ids: vec![cid("grant/root"), cid("grant/delegated")], authority_receipt: cid("jcp-receipt/allow"), verdict: "allow".into(), attestation_id: cid("attest/eat"), proof_cell_id: Some(cid("proof/cell")), tier: "S5".into(), grounding: "hardware".into(), work_receipt: cid("jcr1/resolution"), input_hash: "a".repeat(64), output_hash: None, joules_uj: 19_538_280, energy_provenance: "HwShunt".into(), effect: Effect { class: EffectClass::Reversible, target: cid("target/document"), before: Some("b".repeat(64)), after: Some("c".repeat(64)), compensator: None, plan_receipt: None, }, egress: Egress { class: EgressClass::None, bytes: 0, peer: None, }, steward: STEWARD.into(), } } /// A signed vector plus the properties a verifier must reproduce. fn positive(name: &str, note: &str, r: &ActionReceipt, key: &SigningKey) -> Value { let env = r.seal(key).expect("valid receipt must seal"); // Prove the vector before publishing it — a pack that ships an // unverifiable vector is worse than no pack. let v = verify(&env, &key.verifying_key(), STEWARD).expect("emitted vector must verify"); assert_eq!(v.id, r.id().unwrap()); json!({ "name": name, "note": note, "expect": "verify", "id_hex": r.id().unwrap().to_hex(), "core_hex": hex::encode(r.signed_core().unwrap()), "envelope_hex": hex::encode(&env), "wire": r.to_wire(), }) } /// A negative vector: correctly signed, structurally invalid. Built by signing /// a hand-edited projection so the signature is genuine — an implementation /// that only checks signatures will accept it, which is exactly what the pack /// is designed to catch. fn negative(name: &str, note: &str, expect: &str, wire: Value, key: &SigningKey) -> Value { let core = jcr1::signed_core_bytes(&wire).expect("core"); let env = jcr1::cose_sign1_envelope(&core, key, STEWARD); // The signature really is valid... jcr1::verify_cose_sign1(&env, STEWARD, key.verifying_key().as_bytes()) .expect("negative vector must carry a genuine signature"); // ...and AR-1 must still refuse it. assert!( verify(&env, &key.verifying_key(), STEWARD).is_err(), "negative vector `{name}` was accepted — the rule it pins is not enforced" ); json!({ "name": name, "note": note, "expect": expect, "envelope_hex": hex::encode(&env), "wire": wire, }) } fn main() -> Result<(), Box> { let out_dir = std::env::args().nth(1).unwrap_or_else(|| ".".into()); let key = SigningKey::from_bytes(&FIXTURE_SEED); let mut vectors = Vec::new(); // ── positives: one per effect class, one per egress class ────────── vectors.push(positive( "reversible-local", "Read-modify-write resolved on-device. Nothing left the machine, so egress is `none` with zero bytes.", &base(), &key, )); let mut r = base(); r.effect.class = EffectClass::None; r.effect.before = None; r.effect.after = None; vectors.push(positive( "read-only", "A query that changed nothing. Safe to replay; no before/after state to record.", &r, &key, )); let mut r = base(); r.effect.class = EffectClass::Compensable; r.effect.compensator = Some(cid("grant/refund")); vectors.push(positive( "compensable-with-compensator", "Cannot be undone, but the grant that could compensate is named at the time of acting — not discovered afterwards.", &r, &key, )); let mut r = base(); r.effect.class = EffectClass::Irreversible; r.effect.plan_receipt = Some(cid("plan/receipt")); vectors.push(positive( "irreversible-with-plan", "An irreversible commit carrying the plan receipt that preceded it. This is the only shape in which `irreversible` is well-formed.", &r, &key, )); let mut r = base(); r.egress.class = EgressClass::AttestedEnclave; r.egress.bytes = 4_096; r.egress.peer = Some(cid("attest/peer-enclave")); vectors.push(positive( "egress-attested-enclave", "Work left the device to somewhere that proved what it was running. The peer attestation is bound in the receipt.", &r, &key, )); let mut r = base(); r.egress.class = EgressClass::ThirdParty; r.egress.bytes = 8_192; r.egress.peer = None; r.tier = "S0".into(); r.grounding = "software".into(); vectors.push(positive( "egress-third-party-honest", "An unattested destination, honestly recorded rather than upgraded. Trust is not an egress class.", &r, &key, )); let mut r = base(); r.energy_provenance = "Estimator".into(); r.joules_uj = 1_200_000; vectors.push(positive( "estimated-energy", "Energy from an estimator, not a hardware counter. `HwShunt` and `Estimator` are different claims and must never collapse into one number.", &r, &key, )); // ── negatives: the three structural rules ────────────────────────── let mut w = base().to_wire(); w["effect"]["class"] = json!("irreversible"); vectors.push(negative( "irreversible-without-plan", "Correctly signed, structurally invalid: an irreversible effect with no plan receipt. A verifier that only checks signatures accepts this.", "reject:irreversible-without-plan", w, &key, )); let mut w = base().to_wire(); w["egress"]["bytes"] = json!(4_096u64); vectors.push(negative( "egress-none-with-bytes", "Claims nothing left the device while counting 4096 bytes that did. The claim is falsified by its own byte count.", "reject:egress-none-with-bytes", w, &key, )); let mut w = base().to_wire(); w["egress"]["class"] = json!("attested_enclave"); w["egress"]["bytes"] = json!(4_096u64); vectors.push(negative( "attested-egress-without-peer", "Claims an attested destination with no peer attestation to bind. The router may not claim a stronger class than its evidence supports.", "reject:attested-egress-without-peer", w, &key, )); let meta = json!({ "alg": "EdDSA (COSE -8)", "profile": jcp_action_receipt::AR_PROFILE, "pubkey_hex": hex::encode(key.verifying_key().as_bytes()), "steward": STEWARD, "note": "Fixture key, published deliberately so third parties can verify. Never use it for anything real.", "positives": vectors.iter().filter(|v| v["expect"] == "verify").count(), "negatives": vectors.iter().filter(|v| v["expect"] != "verify").count(), }); let vpath = format!("{out_dir}/action_receipt.json"); let mpath = format!("{out_dir}/action_receipt_meta.json"); std::fs::write(&vpath, serde_json::to_string_pretty(&json!({ "vectors": vectors }))? + "\n")?; std::fs::write(&mpath, serde_json::to_string_pretty(&meta)? + "\n")?; println!("wrote {vpath}"); println!("wrote {mpath}"); println!( " {} positive, {} negative", meta["positives"], meta["negatives"] ); Ok(()) }