//! A Joule Code coding operation under a JouleContract. //! //! The mapping is not an analogy; the objects line up field by field: //! //! - **The edit set is the frame.** A `CodingOp`'s `edits[].path` list is the //! declared mutable footprint (JouleContract §3.4). An edit outside the //! contracted paths is an out-of-frame effect — the breach primitive, made //! concrete for code, caught at the boundary by [`frame_check`]. //! - **The tier cost is the ceiling.** The cascade's per-tier joule class //! prices the contract's EnPI ceiling. //! - **JCR provenance maps onto the energy classes.** `HwShunt` → `Metered` //! (with a meter binding), `ModelBased` → `ModelBased` (with an anchor), //! `Estimator` → `Unaccounted`. Honest in, honest out — an `Estimator` //! figure can never serialize as `Metered` through this binding. //! //! The binding produces receipts that pass JouleContract's **receipt-local** //! validation (at most JR-2 — JouleContract SPEC §10's two-stage rule); //! composing them to JR-3+ with sealed evidence, a key log, and anchored //! intervals is the deployment's verifier-of-record work, not this crate's. use joule_code_cargo::{CodingOp, SignedOp}; use joule_code_receipt::{Energy, Provenance}; use joule_code_schema::ContentId; use joulecontract_receipt::{ DischargeEntry, DischargeResult, Engagement, FrameMode, Generator, KeyId, MeterBinding, Qualification, Receipt, TierClass, }; use joulecontract_schema::{ Ceiling, ContentAddress, Contract, DerivationBinding, EnergyClass, GovernanceClass, Profile, Rigor, }; use std::collections::BTreeSet; /// Microjoules per joule: JCR energy records are integer µJ; JouleContract /// ceilings are EOC joules. const UJ_PER_J: f64 = 1_000_000.0; /// Bridge a Joule Code content identity (`b3:…`, BLAKE3/base64) into a /// JouleContract content address (`sha2-256:…`). The foreign identity string /// is the addressed content — a cross-standard *reference*, not a re-hash /// claim: resolving it back to the Joule Code object goes through the /// original `ContentId`. pub fn bridge_id(id: &ContentId) -> ContentAddress { ContentAddress::of_bytes(id.0.as_bytes()) } /// JCR provenance → JouleContract energy class. The mapping only ever moves /// trust *down*: a coarse estimator is `Unaccounted`, never promoted. pub fn energy_class(p: Provenance) -> EnergyClass { match p { Provenance::HwShunt => EnergyClass::Metered, Provenance::ModelBased => EnergyClass::ModelBased, Provenance::Estimator => EnergyClass::Unaccounted, } } /// Build the JouleContract for a coding request: what must hold, what may be /// touched, what may be spent. pub fn contract_for_op( intent: &str, prior: &ContentId, frame_paths: &[String], ceiling_joules: f64, enb_ref: ContentAddress, derivation: Option, rigor_target: Rigor, ) -> Contract { Contract { joulecontract: "0.8.0".into(), subject: intent.to_string(), rigor_target, pre: vec![format!("op builds on prior tree {}", prior.0)], post: vec!["every edit applied within the declared frame".into()], inv: vec![ "op signature verifies over op_id ‖ prior".into(), "edits are ordered and pairwise disjoint".into(), ], frame: frame_paths.to_vec(), ceiling: Ceiling { enpi_joules: ceiling_joules, unit: "EOC-J".into(), enb_ref, derivation, }, sector_equivalence: None, amends: None, } } #[derive(Debug, thiserror::Error, PartialEq, Eq)] pub enum BindingError { /// An edit outside the contracted footprint: the out-of-frame effect, /// caught at the boundary instead of discovered in review. #[error("out-of-frame edit: `{path}` is not within the contracted footprint")] OutOfFrame { path: String }, #[error("contract addressing failed: {0}")] Address(String), /// The op's own id could not be derived, so no receipt can name the /// operation it is about. #[error("op id could not be derived: {0}")] OpId(String), } /// Decide one declared obligation against what this binder actually holds. /// /// Every arm either evaluates the obligation or reports it `Undischarged` /// with the reason it was not evaluated. **There is no default `Closed`.** /// `Closed` is JouleContract's positive "this obligation was discharged", and /// it used to be the fall-through for every Pre, Post and Inv obligation — /// signed, attributed to a named `Deterministic` tier, with nothing having /// evaluated them. The worst case was the Post obligation below: it states /// the same property as the frame obligation, so a single receipt could carry /// `Frame = Undischarged { "out-of-frame edit: deploy/secrets.env" }` and /// `Post = Closed` for "every edit applied within the declared frame" — one /// signature asserting both that an edit escaped the frame and that every /// edit stayed inside it. fn discharge_for( signed: &SignedOp, prior: &ContentId, frame_result: &DischargeResult, ob: &joulecontract_schema::Obligation, ) -> DischargeResult { use joulecontract_schema::ObligationKind as Kind; match (&ob.kind, ob.text.as_str()) { // Decided by `frame_check`. (Kind::Frame, _) => frame_result.clone(), // The same property as the frame obligation, so it carries the same // evidence rather than a second, contradictory verdict. (Kind::Post, "every edit applied within the declared frame") => frame_result.clone(), // The contract names the tree the op must build on. `SignedOp::verify` // below binds the op to `prior`; this checks the *contract* was // written for that same prior, so the two cannot disagree silently. (Kind::Pre, text) if text.starts_with("op builds on prior tree ") => { let expected = format!("op builds on prior tree {}", prior.0); if text == expected { DischargeResult::Closed } else { DischargeResult::Undischarged { reason: format!("contract declares `{text}`, this receipt states prior {}", prior.0), } } } // Exactly what `SignedOp::verify` decides: it recomputes `op_id`, // rebuilds `op_id ‖ prior`, checks the signature against `op.author`, // and rejects replay onto a different base. (Kind::Inv, "op signature verifies over op_id ‖ prior") => match signed.verify(prior) { Ok(()) => DischargeResult::Closed, Err(e) => DischargeResult::Undischarged { reason: format!("signature does not verify over op_id ‖ prior: {e}"), }, }, // Exactly what `CodingOp::validate` decides (SPEC §2: known alg, // non-empty edits, ascending `(path, start)`, no overlap). (Kind::Inv, "edits are ordered and pairwise disjoint") => match signed.op.validate() { Ok(()) => DischargeResult::Closed, Err(e) => DischargeResult::Undischarged { reason: e.to_string() }, }, // An obligation this binder does not recognise. A caller may declare // any prose it likes; none of it is discharged by being written down. (_, text) => DischargeResult::Undischarged { reason: format!("not evaluated by this binder: {text}"), }, } } /// Check every edit path against the contract's frame. A path is in-frame /// when it equals a frame entry or sits under a frame entry treated as a /// directory prefix (`src/` covers `src/lib.rs`). pub fn frame_check(contract: &Contract, op: &CodingOp) -> Result<(), BindingError> { for edit in &op.edits { let in_frame = contract.frame.iter().any(|entry| { let dir = if entry.ends_with('/') { entry.clone() } else { format!("{entry}/") }; edit.path == *entry || edit.path.starts_with(&dir) }); if !in_frame { return Err(BindingError::OutOfFrame { path: edit.path.clone(), }); } } Ok(()) } /// The static identities a deployment binds once: who verifies, under what /// qualification, paid by whom; who generates; where the meter attestation /// comes from when the platform has one. pub struct Binder { pub verifier_keys: BTreeSet, pub verifier_entity: Option, pub engagement: Engagement, pub recourse: Option, pub qualification: Qualification, pub tier_operator_keys: BTreeSet, pub generator: Generator, pub receipt_signer: KeyId, /// Meter attestation for `HwShunt` readings (`Metered` binds or demotes). pub meter: Option, /// Calibration anchor for `ModelBased` readings. pub energy_anchor: Option, pub register_pin: ContentAddress, /// The sealed evidence bundle address, where the deployment seals one. pub evidence: Option, } impl Binder { /// Discharge a coding operation against its contract into a JouleContract /// receipt. The frame is checked **here** — its discharge mode is /// `Observed` because the footprint was compared against the actual edit /// set, not analyzed from the artifact. An out-of-frame op still gets a /// receipt: the frame serializes `undischarged` with the offending path, /// because a breach recorded honestly beats a breach driven underground. /// Seal a receipt for one signed op against a contract. /// /// Takes the **signed** op and the `prior` the verifier believes current, /// because two of the contract's obligations are statements about exactly /// those: the signature over `op_id ‖ prior`, and the tree the op builds /// on. With an unsigned `CodingOp` and a caller-supplied `op_id` neither /// could be decided — and both were nonetheless sealed as discharged. /// /// The op id is **derived** from the op rather than supplied, so /// `transition` always names the operation whose obligations were checked; /// a caller-supplied id was trusted and never re-derived, so a receipt /// could carry one op's frame verdict under another op's id. /// /// # Errors /// [`BindingError::OpId`] if the op's id cannot be derived (it is /// structurally invalid), or [`BindingError::Address`] if the contract /// cannot be addressed. A failing *obligation* is not an error: it is /// recorded as `Undischarged` on the receipt, which is the point. pub fn receipt_for_op( &self, contract: &Contract, signed: &SignedOp, prior: &ContentId, energy: &Energy, ) -> Result { let op: &CodingOp = &signed.op; let op_id = op.op_id().map_err(|e| BindingError::OpId(e.to_string()))?; let frame_result = match frame_check(contract, op) { Ok(()) => DischargeResult::Closed, Err(BindingError::OutOfFrame { path }) => DischargeResult::Undischarged { reason: format!("out-of-frame edit: {path}"), }, Err(e) => return Err(e), }; let provenance = energy_class(energy.provenance); let spent_uj = energy.measured_uj.unwrap_or(energy.estimated_uj); let joules_spent = spent_uj as f64 / UJ_PER_J; let binding_tier = ContentAddress::of_bytes(b"joule-code-joulecontract deterministic binding checks v1"); let discharge = contract .obligations() .into_iter() .map(|ob| { let is_frame = ob.kind == joulecontract_schema::ObligationKind::Frame; let result = discharge_for(signed, prior, &frame_result, &ob); DischargeEntry { obligation: ob, tier: binding_tier.clone(), tier_class: TierClass::Deterministic, tier_operator_keys: self.tier_operator_keys.clone(), result, mode: is_frame.then_some(FrameMode::Observed), qualification: Some(self.qualification.clone()), verifier_keys: self.verifier_keys.clone(), verifier_entity: self.verifier_entity.clone(), engagement: Some(self.engagement.clone()), recourse: self.recourse.clone(), re_discharged_by: None, } }) .collect(); Ok(Receipt { contract: contract .content_address() .map_err(|e| BindingError::Address(e.to_string()))?, transition: bridge_id(&op_id), generator: self.generator.clone(), discharge, execution: vec![ContentAddress::of_bytes( format!("joule-code tier {:?}", op.tier).as_bytes(), )], joules_spent, meter: match provenance { EnergyClass::Metered => self.meter.clone(), _ => None, }, energy_anchor: match provenance { EnergyClass::ModelBased => self.energy_anchor.clone(), _ => None, }, joules_allowed: contract.ceiling.enpi_joules, provenance, enb_ref: contract.ceiling.enb_ref.clone(), grid_mix: None, register_pin: self.register_pin.clone(), evidence: self.evidence.clone(), claimed_profile: Profile { rigor: Rigor::Jr2, energy: provenance, governance: GovernanceClass::Managed, }, receipt_signer: self.receipt_signer.clone(), }) } } #[cfg(test)] mod tests { use super::*; use joule_code_cargo::{Anchor, CodingOp, Edit}; use joule_code_schema::Tier; use joulecontract_receipt::{validate, LoopGovernance, TrustRoot}; use joulecontract_schema::Demotion; fn key(label: &str) -> KeyId { ContentAddress::of_bytes(label.as_bytes()) } fn author_key() -> ed25519_dalek::SigningKey { ed25519_dalek::SigningKey::from_bytes(&[42u8; 32]) } /// The tree `contract()` declares its op must build on. fn prior() -> ContentId { ContentId::of_bytes(b"prior tree") } /// A real signed op. `author` is the signing key's own verifying key, and /// the edits are sorted into canonical `(path, start)` order, because /// `op_id` runs `validate` and `sign` runs `op_id` — an op that is not /// canonical has no id and therefore no signature. /// /// This replaces a fixture that set `author: [7u8; 32]` (not a key) and /// returned `ContentId::of_bytes(b"the op canonical bytes")` as the id — /// a constant unrelated to the op, which is what let the untrusted /// `op_id` parameter go unnoticed. fn op(paths: &[&str]) -> SignedOp { let key = author_key(); let mut sorted: Vec<&&str> = paths.iter().collect(); sorted.sort(); let op = CodingOp { alg: 0x01, prior: prior(), edits: sorted .into_iter() .map(|p| Edit { path: (*p).to_string(), anchor: Anchor::ByteRange { start: 0, end: 4 }, replacement: b"new!".to_vec(), }) .collect(), author: key.verifying_key().to_bytes(), intent: "rename the settlement index field".into(), tier: Tier::L1, }; op.sign(&key).expect("a canonical op signs") } fn binder() -> Binder { Binder { verifier_keys: [key("audit-a")].into(), verifier_entity: Some("vLEI:AUDIT-A".into()), engagement: Engagement { selected_by: "did:web:relying.example".into(), compensated_by: "did:web:relying.example".into(), }, recourse: Some("ISAE3000".into()), qualification: Qualification { attestation: key("binding qualification"), signer_key: key("assessor"), valid_until: 20_000, validated_at: 10_000, root: Some(TrustRoot::ReferenceImpl), }, tier_operator_keys: [key("binding-operator")].into(), generator: Generator { system: "joule-code cascade".into(), version_digest: key("cascade build"), attestation: Some(key("cascade attestation")), attestation_signer: Some(key("registry")), key_set: [key("producer")].into(), entity: Some("vLEI:PRODUCER".into()), human_principals: vec!["did:web:eng.example".into()], loop_governance: Some(LoopGovernance { contract: key("loop contract"), discharged_by: [key("audit-a")].into(), root: TrustRoot::HumanAttested, }), }, receipt_signer: key("producer"), meter: Some(MeterBinding { attestation: key("rapl attestation"), signer_key: key("substrate-telemetry"), valid_until: 20_000, validated_at: 10_000, }), energy_anchor: Some(key("calibration anchor")), register_pin: key("register snapshot"), evidence: None, } } fn contract(frame: &[&str]) -> Contract { contract_for_op( "rename the settlement index field", &ContentId::of_bytes(b"prior tree"), &frame.iter().map(|s| s.to_string()).collect::>(), 0.01, key("enb"), Some(DerivationBinding { attestation: key("derivation"), basis: "metered median of comparable L1 ops".into(), signer: "did:web:energy-office.example".into(), }), Rigor::Jr2, ) } #[test] fn in_frame_op_discharges_and_validates_to_jr2() { let c = contract(&["src/", "Cargo.toml"]); let signed = op(&["src/lib.rs", "Cargo.toml"]); let energy = Energy { measured_uj: Some(1_200), estimated_uj: 1_000, provenance: Provenance::HwShunt, }; let r = binder().receipt_for_op(&c, &signed, &prior(), &energy).unwrap(); let v = validate(&c, &r).unwrap(); assert_eq!(v.max_profile.rigor, Rigor::Jr2); assert_eq!(v.max_profile.energy, EnergyClass::Metered); assert!(v.permits(&r.claimed_profile), "demotions: {:?}", v.demotions); assert!((r.joules_spent - 0.0012).abs() < 1e-9); assert!(r.residual() > 0.0); } #[test] fn out_of_frame_edit_is_caught_at_the_boundary() { let c = contract(&["src/"]); let signed = op(&["src/lib.rs", "deploy/secrets.env"]); assert_eq!( frame_check(&c, &signed.op), Err(BindingError::OutOfFrame { path: "deploy/secrets.env".into() }) ); // The receipt still issues — with the frame undischarged and the // offending path on the record. Honest breach beats hidden breach. let energy = Energy { measured_uj: None, estimated_uj: 900, provenance: Provenance::ModelBased, }; let r = binder().receipt_for_op(&c, &signed, &prior(), &energy).unwrap(); let v = validate(&c, &r).unwrap(); assert!(v.demotions.iter().any(|d| matches!( d, Demotion::Undischarged { reason } if reason.contains("deploy/secrets.env") ))); assert_eq!(v.max_profile.rigor, Rigor::Jr1, "undischarged frame caps rigor"); } /// The contradiction this fix removes. The Post obligation states the same /// property as the Frame obligation, so one receipt cannot carry /// "an edit escaped the frame" and "every edit stayed inside it" at once. #[test] fn the_post_obligation_carries_the_frame_verdict() { let c = contract(&["src/"]); let signed = op(&["src/lib.rs", "deploy/secrets.env"]); let energy = Energy { measured_uj: Some(900), estimated_uj: 900, provenance: Provenance::HwShunt, }; let r = binder().receipt_for_op(&c, &signed, &prior(), &energy).unwrap(); let post = r .discharge .iter() .find(|d| d.obligation.text == "every edit applied within the declared frame") .expect("the contract declares it"); let frame = r .discharge .iter() .find(|d| d.obligation.kind == joulecontract_schema::ObligationKind::Frame) .expect("the contract declares it"); assert_eq!(post.result, frame.result, "same property, same verdict"); assert!( matches!(&post.result, DischargeResult::Undischarged { reason } if reason.contains("deploy/secrets.env")), "got {:?}", post.result ); } /// The signature obligation is decided, not assumed. A tampered signature /// leaves it undischarged instead of sealing "verifies" over bytes that /// do not. #[test] fn the_signature_obligation_is_decided_not_assumed() { let c = contract(&["src/"]); let mut signed = op(&["src/lib.rs"]); let energy = Energy { measured_uj: Some(900), estimated_uj: 900, provenance: Provenance::HwShunt, }; // Intact: the obligation closes on a real check. let good = binder().receipt_for_op(&c, &signed, &prior(), &energy).unwrap(); let sig_of = |r: &Receipt| { r.discharge .iter() .find(|d| d.obligation.text == "op signature verifies over op_id \u{2016} prior") .map(|d| d.result.clone()) .expect("the contract declares it") }; assert_eq!(sig_of(&good), DischargeResult::Closed); // Tampered: undischarged, with the reason on the record. *signed.sig.last_mut().expect("signature bytes") ^= 1; let bad = binder().receipt_for_op(&c, &signed, &prior(), &energy).unwrap(); assert!( matches!(sig_of(&bad), DischargeResult::Undischarged { .. }), "got {:?}", sig_of(&bad) ); } /// A caller may declare any prose it likes; none of it is discharged by /// being written down. The fall-through used to be `Closed`. #[test] fn an_unrecognised_obligation_is_never_closed() { let mut c = contract(&["src/"]); c.post.push("the author was well rested".into()); let signed = op(&["src/lib.rs"]); let energy = Energy { measured_uj: Some(900), estimated_uj: 900, provenance: Provenance::HwShunt, }; let r = binder().receipt_for_op(&c, &signed, &prior(), &energy).unwrap(); let entry = r .discharge .iter() .find(|d| d.obligation.text == "the author was well rested") .expect("declared"); assert!( matches!(&entry.result, DischargeResult::Undischarged { reason } if reason.contains("not evaluated")), "got {:?}", entry.result ); } /// The receipt names the operation whose obligations it checked: the id is /// derived from the op, not supplied beside it. #[test] fn the_transition_names_the_op_it_checked() { let c = contract(&["src/"]); let signed = op(&["src/lib.rs"]); let energy = Energy { measured_uj: Some(900), estimated_uj: 900, provenance: Provenance::HwShunt, }; let r = binder().receipt_for_op(&c, &signed, &prior(), &energy).unwrap(); assert_eq!(r.transition, bridge_id(&signed.op.op_id().unwrap())); } /// A contract written for a different tree does not have its precondition /// discharged by a receipt that states another prior. #[test] fn a_contract_for_a_different_prior_leaves_the_precondition_open() { let c = contract_for_op( "rename the settlement index field", &ContentId::of_bytes(b"some other tree"), &["src/".to_string()], 0.01, key("enb"), None, Rigor::Jr2, ); let signed = op(&["src/lib.rs"]); let energy = Energy { measured_uj: Some(900), estimated_uj: 900, provenance: Provenance::HwShunt, }; let r = binder().receipt_for_op(&c, &signed, &prior(), &energy).unwrap(); let pre = r .discharge .iter() .find(|d| d.obligation.kind == joulecontract_schema::ObligationKind::Pre) .expect("declared"); assert!( matches!(&pre.result, DischargeResult::Undischarged { .. }), "got {:?}", pre.result ); } #[test] fn estimator_energy_is_unaccounted_never_promoted() { let c = contract(&["src/"]); let signed = op(&["src/lib.rs"]); let energy = Energy::from_tokens(100, 10); let r = binder().receipt_for_op(&c, &signed, &prior(), &energy).unwrap(); assert_eq!(r.provenance, EnergyClass::Unaccounted); assert!(r.meter.is_none(), "an estimator figure never carries a meter binding"); let v = validate(&c, &r).unwrap(); assert_eq!(v.max_profile.energy, EnergyClass::Unaccounted); } #[test] fn overrun_is_recorded_not_blocked() { // Receipt-time enforcement (JouleContract §5.3): the deviation is on // the record as a negative residual; amend-or-fix is the resolution. let c = contract(&["src/"]); let signed = op(&["src/lib.rs"]); let energy = Energy { measured_uj: Some(50_000), // 0.05 J against a 0.01 J ceiling estimated_uj: 50_000, provenance: Provenance::HwShunt, }; let r = binder().receipt_for_op(&c, &signed, &prior(), &energy).unwrap(); assert!(r.residual() < 0.0); assert!(validate(&c, &r).is_ok(), "the system keeps running; the record decides"); } #[test] fn provenance_mapping_is_total_and_downward_honest() { assert_eq!(energy_class(Provenance::HwShunt), EnergyClass::Metered); assert_eq!(energy_class(Provenance::ModelBased), EnergyClass::ModelBased); assert_eq!(energy_class(Provenance::Estimator), EnergyClass::Unaccounted); } }