open-standards/proof/proof-rs/crates/proof-receipt/src/lib.rs
Transaction Science 13df129cb0 Add the proof open standard — a carrier for verified work
The verification cargo class: energy-accounted formal verification as a
vendor-neutral standard. A proof obligation is content-addressed; a solver
attempts one at a declared tier; a discharged obligation seals as a signed
JCR-1 receipt recording which tier closed it and the joules it cost. The
cheapest sufficient solver wins, correctness stays the verifier's, and the
joules a cheaper solver spent before failing are disclosed, never hidden.

- SPEC.md (§1–§9): the obligation, canonical det-CBOR addressing, the
  cheapest-sufficient cascade with disclosed escalation, the honest
  energy/provenance model, the proof/1 JCR-1 receipt, the cache, conformance.
- proof-rs/: a 6-crate Apache-2.0 reference workspace. Two real rungs with
  measured energy — an in-process propositional decider (T0) and z3, shelled
  or kept resident (T1) — over honest model-tier stand-ins; the escalation
  router with the proof-cache flywheel and frontier-only baseline accounting;
  the proof/1 receipt vendored byte-compatible with the family JCR-1 verifiers;
  C1–C5 + a negative pack, all checks real, with published vectors.
- proof carries spec_grounding in the signed receipt: a proof is not a
  specification, and a prover may never self-certify that the statement
  encodes intent.

cargo test --workspace green; cargo clippy --workspace --all-targets clean.
JouleProof (jouleproof.transaction.science) is the steward + services layer
over this standard.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-07 15:30:07 -04:00

134 lines
5.4 KiB
Rust

//! The Proof receipt (SPEC §6).
//!
//! A verified proof seals as a signed record of *what it cost*: the goal it
//! discharged, the tier that closed it, the joules spent (with `wasted_uj` and
//! the frontier-only `baseline_uj`), the honest energy provenance, and the
//! statement's spec-grounding. Receipts seal as **JCR-1** envelopes — the
//! family's shared signed-receipt format (canonical det-CBOR + COSE_Sign1/EdDSA)
//! — so a Proof receipt verifies with the same tooling as a Map, Sandbox, or
//! Joule Code receipt.
//!
//! The receipt attests the **proof**, not the specification: a goal whose
//! `spec_grounding` is `Ungrounded` says, in the signed artifact itself, that
//! the statement was never validated against intent.
use ed25519_dalek::SigningKey;
use proof_cascade::CascadeResult;
use serde_json::{json, Value};
pub mod jcr1;
/// Errors from projecting, sealing, or verifying a receipt.
#[derive(Debug, thiserror::Error)]
pub enum ReceiptError {
/// A receipt was requested for a goal that was not proved.
#[error("cannot seal a receipt for an unproved goal")]
NotProved,
/// The envelope payload did not match this result's canonical core.
#[error("envelope payload does not match the result")]
PayloadMismatch,
#[error(transparent)]
Jcr1(#[from] jcr1::Jcr1Error),
}
/// Project a cascade result to JCR-1 wire JSON — the `proof/1` profile of the
/// family's receipt format (SPEC §6). The signed core is a flat uint/text map;
/// fields that don't apply (e.g. `closed_by` for an unproved goal) are omitted.
pub fn to_jcr1_wire(r: &CascadeResult, steward: &str) -> Value {
let mut obj = serde_json::Map::new();
obj.insert("proof_receipt".into(), json!("proof/1"));
obj.insert("goal_id".into(), json!(r.goal_id));
obj.insert("verdict".into(), json!(if r.proved { "proved" } else { "unproved" }));
if let Some(t) = r.closed_by {
obj.insert("closed_by".into(), json!(t.wire_tag()));
}
if let Some(a) = r.artifact {
obj.insert("artifact".into(), json!(a.to_hex()));
}
obj.insert("total_uj".into(), json!(r.total_uj));
obj.insert("wasted_uj".into(), json!(r.wasted_uj));
obj.insert("baseline_uj".into(), json!(r.baseline_uj));
obj.insert("energy_provenance".into(), json!(r.provenance.jcr1_label()));
obj.insert("spec_grounding".into(), json!(r.grounding.label()));
obj.insert("from_cache".into(), json!(if r.from_cache { 1 } else { 0 }));
obj.insert("steward".into(), json!(steward));
Value::Object(obj)
}
/// The canonical signed-core bytes (JCR-1 det-CBOR) for a result.
pub fn signed_core(r: &CascadeResult, steward: &str) -> Result<Vec<u8>, ReceiptError> {
Ok(jcr1::signed_core_bytes(&to_jcr1_wire(r, steward))?)
}
/// Seal a *proved* result into a JCR-1 COSE_Sign1 envelope. Refuses to seal a
/// receipt for an unproved goal.
pub fn seal(r: &CascadeResult, key: &SigningKey, steward: &str) -> Result<Vec<u8>, ReceiptError> {
if !r.proved {
return Err(ReceiptError::NotProved);
}
let core = signed_core(r, steward)?;
Ok(jcr1::cose_sign1_envelope(&core, key, steward))
}
/// Verify a JCR-1 envelope against a steward key and confirm its payload is
/// exactly this result's canonical signed core.
pub fn verify(
r: &CascadeResult,
envelope: &[u8],
steward: &str,
pubkey: &[u8; 32],
) -> Result<(), ReceiptError> {
let payload = jcr1::verify_cose_sign1(envelope, steward, pubkey)?;
if payload != signed_core(r, steward)? {
return Err(ReceiptError::PayloadMismatch);
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use proof_cascade::{ProofCache, Router};
use proof_schema::{GoalKind, ProofGoal};
use proof_solvers::reference_ladder;
fn prove_one(stmt: &str, kind: GoalKind) -> CascadeResult {
let router = Router::new(reference_ladder());
let mut cache = ProofCache::new();
router.prove(&ProofGoal::new(stmt, "lean4", kind), &mut cache)
}
#[test]
fn proved_receipt_seals_and_verifies() {
let r = prove_one("x = x", GoalKind::Trivial);
let sk = SigningKey::from_bytes(&[7u8; 32]);
let steward = "did:example:prover1";
let env = seal(&r, &sk, steward).unwrap();
assert!(verify(&r, &env, steward, &sk.verifying_key().to_bytes()).is_ok());
// A different steward must not verify.
assert!(verify(&r, &env, "did:example:other", &sk.verifying_key().to_bytes()).is_err());
}
#[test]
fn wire_records_grounding_and_energy_fields() {
let r = prove_one("dec", GoalKind::Decidable);
let wire = to_jcr1_wire(&r, "did:example:p");
let o = wire.as_object().unwrap();
assert_eq!(o["proof_receipt"], json!("proof/1"));
assert_eq!(o["verdict"], json!("proved"));
assert_eq!(o["closed_by"], json!("T1"));
assert_eq!(o["spec_grounding"], json!("Ungrounded")); // honest by default
assert!(o.contains_key("baseline_uj") && o.contains_key("wasted_uj"));
}
#[test]
fn unproved_goal_cannot_be_sealed() {
// No solver in the reference ladder is unable to close General/Hard, so
// build an explicitly unproved result by hand.
let mut r = prove_one("x = x", GoalKind::Trivial);
r.proved = false;
r.closed_by = None;
let sk = SigningKey::from_bytes(&[7u8; 32]);
assert!(matches!(seal(&r, &sk, "s"), Err(ReceiptError::NotProved)));
}
}