//! The Mesh inference serving receipt (SPEC §6). //! //! A [`MeshReceipt`] is the signed, hash-chained record of one metered //! inference. Its headline figure — the thing no `$/token` or `tok/s` dashboard //! exposes — is [`MeshReceipt::uj_per_accepted_token`]: microjoules per accepted //! output token, derived from a real energy span. The receipt also records the //! route that was taken and *why*, the recipe that served it, and the honest //! [`Provenance`] of the energy figure. A dishonest receipt (a `HwShunt` claim //! with no measurement) cannot be sealed. use ed25519_dalek::SigningKey; use jcp_schema::ContentId; use mesh_schema::{BackendId, Energy, Fidelity, SchemaError, ServeOutcome}; use serde::{Deserialize, Serialize}; use serde_json::json; pub mod change; pub mod jcr1; pub use mesh_schema::Provenance; /// Errors from building, signing, or verifying a receipt. #[derive(Debug, thiserror::Error)] pub enum ReceiptError { /// `Provenance::HwShunt` with no `measured_uj`. #[error("HwShunt provenance requires a measured figure")] HwShuntRequiresMeasured, /// `Provenance::Estimator` with a `measured_uj` present (dishonest). #[error("Estimator provenance must not carry a measured figure")] EstimatorMustNotCarryMeasured, /// The envelope verified but its payload did not match this receipt's core. #[error("envelope payload does not match receipt")] PayloadMismatch, #[error(transparent)] Jcr1(#[from] jcr1::Jcr1Error), } impl From for ReceiptError { fn from(e: SchemaError) -> Self { match e { SchemaError::HwShuntRequiresMeasured => ReceiptError::HwShuntRequiresMeasured, SchemaError::EstimatorMustNotCarryMeasured => { ReceiptError::EstimatorMustNotCarryMeasured } } } } /// A signed receipt for one metered inference (SPEC §6). #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct MeshReceipt { /// Content id of the [`ServeRequest`](mesh_schema::ServeRequest). pub request_id: ContentId, /// The JCP grant id this inference debited, if it rode on one. pub grant_id: Option, /// The backend that served the request. pub backend: BackendId, /// The recipe (composed model) that served it. /// /// A name. Read it with `weights_digest` and `precision`: alone it says /// which label the operator gave the model, not which weights ran. pub recipe: String, /// The content address of the weights `recipe` named, when the backend /// reported one (SPEC §3.6). /// /// This is what makes `recipe` binding rather than advisory. Sealed into the /// signed core, so re-pointing a name at different weights no longer leaves /// past receipts verifying against a model that did not answer. `None` is /// the historical shape, skipped on the wire — the receipt then binds the /// model by name alone, and says so by the field's absence. pub weights_digest: Option, /// The numeric precision the forward pass ran at (SPEC §3.6), when the /// backend reported one. /// /// Travels with `weights_digest` because neither reproduces alone: the same /// weights at `fp16` and `fp32` give different probabilities even where the /// selected answer agrees. `None` is the historical shape, skipped. pub precision: Option, /// One-line, auditable reason the router chose this route. pub route_justification: String, /// Serving counters (the inputs to the J/accepted-token figure). pub outcome: ServeOutcome, /// The fidelity floor the request demanded. pub fidelity_floor: Fidelity, /// The level the backend is **rated** to deliver — the advertisement the /// router admitted it on (§4). /// /// Sealed beside `fidelity_delivered` so a backend that serves below its /// rating is attributable from the receipt alone. The two travel as a pair — /// their point is the comparison, and the advertisement alone adds nothing a /// verifier does not already have from `backend`. `None` is the historical /// shape and skipped on the wire. #[serde(default, skip_serializing_if = "Option::is_none")] pub fidelity_advertised: Option, /// The level the backend says it actually **delivered**, where it reports /// one (`ServeOutcome::delivered_fidelity`). /// /// `None` is the historical shape and skipped on the wire; `fidelity_met` /// then rests on the advertisement alone. Presence is the basis tag: no /// separate field says which figure `fidelity_met` came from, because /// presence already does, and a receipt predating the field means exactly /// what an absent value means — the figure is an advertisement. #[serde(default, skip_serializing_if = "Option::is_none")] pub fidelity_delivered: Option, /// Whether the floor was met. /// /// Derived from `fidelity_delivered` when the backend reported one, and from /// the advertisement otherwise. Read it together with /// `fidelity_delivered`: without that field it says the route table promised /// the floor, not that the backend delivered it. pub fidelity_met: bool, /// The honest, provenance-tagged energy of the **served span** — the meter /// session around `engine.serve`, and nothing before it. /// /// Routing and grant authorisation run first and are **not** in this /// figure, so it understates the energy of handling the request by /// whatever they cost (SPEC §3.3). Negligible for a table-driven router; /// not negligible where the router itself runs a model, and the shortfall /// falls in the unsafe direction, because the grant is debited this figure /// and admits further work on it. Do not present it as the request's /// energy. pub energy: Energy, /// The JCP denial label when debiting the metered joules against the grant /// breached it (SPEC §2): a per-call cost caveat the measured cost /// exceeded. The work had already run, so the joules were debited anyway; /// this field is what keeps the breach visible instead of absorbed. /// `None`, skipped on the wire, means the debit was within the grant, or /// the serve rode on no grant. #[serde(default, skip_serializing_if = "Option::is_none")] pub grant_breach: Option, /// A commitment to the output the serve produced (SPEC §6.4), for a /// generation: [`output_commitment`] over the output tokens, under an /// opening the steward holds and discloses for this receipt alone. /// /// Without it the receipt bound what was asked (`request_id`, which /// content-addresses the prompt) and what it cost, and nothing about what /// came back, so no one could later show which answer a receipt was for. /// It is a commitment, not a hash of the tokens: a hash of a short output /// (a one-token answer) can be reversed by trying every output, and a /// receipt is shown to parties who were not owed the answer. With the /// opening, anyone checks a disclosed output against the receipt /// ([`MeshReceipt::opens`]); without it the commitment says nothing about /// the output. `None`, skipped on the wire, for a typed decision (whose /// answer is not carried) and for receipts sealed before the field existed. #[serde(default, skip_serializing_if = "Option::is_none")] pub output_commitment: Option, /// Monotonic sequence in the steward's receipt log. pub sequence: u64, /// Content id of the previous envelope (hash chain); `None` at genesis. pub prev: Option, /// Per-receipt nonce (derived from the request id). pub nonce: [u8; 8], } impl MeshReceipt { /// The wedge metric: microjoules per accepted output token. `None` when no /// tokens were accepted (division undefined). pub fn uj_per_accepted_token(&self) -> Option { // `checked_div` yields None on zero accepted tokens (division undefined). self.energy.uj().checked_div(self.outcome.tokens_accepted as u64) } /// The wedge metric for a backend that emits no tokens: microjoules per /// answered decision (SPEC §3.6). `None` when the request was not a /// decision, or answered no questions. /// /// A typed decision's `tokens_accepted` is zero, so /// [`Self::uj_per_accepted_token`] is undefined for every request in that /// category — the receipt would seal with no efficiency figure at all. The /// unit of work here is the answered question, not the accepted token. pub fn uj_per_decision(&self) -> Option { let d = self.outcome.decision?; self.energy.uj().checked_div(d.questions_answered as u64) } /// Microjoules per option scored — the finer figure, since one question /// over 20 options is more work than one over 2. pub fn uj_per_option_scored(&self) -> Option { let d = self.outcome.decision?; self.energy.uj().checked_div(d.options_scored as u64) } /// Project to the JCR-1 `mesh/1` wire JSON. Booleans are encoded as `0|1` /// uints and absent options dropped, so the signed core stays within the /// det-CBOR type set (no bools, no nulls). `steward` is the endpoint /// identity placed in the COSE `kid`. pub fn to_jcr1_wire(&self, steward: &str) -> serde_json::Value { let mut obj = serde_json::Map::new(); obj.insert("mesh_receipt".into(), json!("mesh/1")); obj.insert("request_id".into(), json!(self.request_id.to_hex())); if let Some(g) = &self.grant_id { obj.insert("grant_id".into(), json!(g.to_hex())); } obj.insert("backend".into(), json!(self.backend.0)); obj.insert("recipe".into(), json!(self.recipe)); // Emitted only when the backend reported them, so a receipt that binds // the model by name alone seals to the bytes it sealed to before these // fields existed — the published pack's vectors included. `canonical_cbor` // orders keys itself (by length, then lexicographically), so where these // are inserted here never reaches the wire. if let Some(d) = self.weights_digest { obj.insert("weights_digest".into(), json!(d.to_hex())); } if let Some(p) = &self.precision { obj.insert("precision".into(), json!(p)); } obj.insert("route".into(), json!(self.route_justification)); obj.insert("tokens_in".into(), json!(self.outcome.tokens_in)); obj.insert("tokens_out".into(), json!(self.outcome.tokens_out)); obj.insert("tokens_accepted".into(), json!(self.outcome.tokens_accepted)); obj.insert("cache_hit_tokens".into(), json!(self.outcome.cache_hit_tokens)); obj.insert("spec_proposed".into(), json!(self.outcome.spec_proposed)); obj.insert("spec_accepted".into(), json!(self.outcome.spec_accepted)); obj.insert("fidelity_floor".into(), json!(self.fidelity_floor.0)); // Emitted only when known, so a receipt that rests on the advertisement // alone seals to the bytes it sealed to before these fields existed. if let Some(f) = self.fidelity_advertised { obj.insert("fidelity_advertised".into(), json!(f.0)); } if let Some(f) = self.fidelity_delivered { obj.insert("fidelity_delivered".into(), json!(f.0)); } obj.insert("fidelity_met".into(), json!(u8::from(self.fidelity_met))); obj.insert("estimated_uj".into(), json!(self.energy.estimated_uj)); if let Some(m) = self.energy.measured_uj { obj.insert("measured_uj".into(), json!(m)); } if let Some(jpt) = self.uj_per_accepted_token() { obj.insert("uj_per_accepted_token".into(), json!(jpt)); } // A typed decision (§3.6). Emitted only when the request was one, so a // generation's signed core is byte-identical to before. if let Some(d) = self.outcome.decision { obj.insert("questions_answered".into(), json!(d.questions_answered)); obj.insert("options_scored".into(), json!(d.options_scored)); obj.insert("schema_tokens".into(), json!(d.schema_tokens)); if let Some(jpd) = self.uj_per_decision() { obj.insert("uj_per_decision".into(), json!(jpd)); } if let Some(jpo) = self.uj_per_option_scored() { obj.insert("uj_per_option_scored".into(), json!(jpo)); } } obj.insert( "energy_provenance".into(), json!(self.energy.provenance.jcr1_label()), ); // Emitted only on a breach, so every receipt whose debit was within its // grant seals to the bytes it sealed to before the field existed. if let Some(b) = &self.grant_breach { obj.insert("grant_breach".into(), json!(b)); } // Emitted only for a serve that committed to its output, so a receipt // sealed before the field existed keeps its bytes. if let Some(c) = &self.output_commitment { obj.insert("output_commitment".into(), json!(c.to_hex())); } obj.insert("sequence".into(), json!(self.sequence)); if let Some(p) = &self.prev { obj.insert("prev".into(), json!(p.to_hex())); } obj.insert("nonce".into(), json!(hex_lower(&self.nonce))); obj.insert("steward".into(), json!(steward)); serde_json::Value::Object(obj) } /// Whether `tokens` are the output this receipt committed to, served on /// the prompt payload whose [`payload_digest`] is `payload`, under /// `opening`: the check a party runs on what the steward disclosed for /// this receipt (SPEC §6.4). `false` for a receipt that commits to no /// output. pub fn opens(&self, opening: &[u8; 32], payload: &ContentId, tokens: &[u32]) -> bool { self.output_commitment == Some(output_commitment(opening, &self.request_id, payload, tokens)) } /// The canonical signed-core bytes (JCR-1 det-CBOR). Validates the /// provenance ↔ measured coupling first, so a dishonest receipt cannot be /// turned into bytes a verifier would accept. pub fn signed_core(&self, steward: &str) -> Result, ReceiptError> { self.energy.check_provenance()?; Ok(jcr1::signed_core_bytes(&self.to_jcr1_wire(steward))?) } /// Seal into a JCR-1 COSE_Sign1 envelope (SPEC §6). pub fn seal(&self, key: &SigningKey, steward: &str) -> Result, ReceiptError> { let core = self.signed_core(steward)?; Ok(jcr1::cose_sign1_envelope(&core, key, steward)) } /// Verify a sealed envelope against an endpoint key and confirm its payload /// is exactly this receipt's canonical signed core. pub fn verify_envelope( &self, envelope: &[u8], steward: &str, endpoint: &[u8; 32], ) -> Result<(), ReceiptError> { let payload = jcr1::verify_cose_sign1(envelope, steward, endpoint)?; if payload != self.signed_core(steward)? { return Err(ReceiptError::PayloadMismatch); } Ok(()) } } /// The domain an output commitment is drawn in, so that it can equal no other /// content address the family computes. const OUTPUT_DOMAIN: &[u8] = b"mesh/1 output commitment\0"; /// The domain a prompt payload's digest is drawn in. const PAYLOAD_DOMAIN: &[u8] = b"mesh/1 prompt payload\0"; /// The digest of the prompt payload an engine was handed: the content /// address of the domain and the token ids as little-endian `u32`s (SPEC /// §6.4). /// /// The request's `prompt_hash` addresses the prompt's bytes, and the engine is /// handed token ids. Nothing ties the two together without the tokenizer, so /// the output commitment binds this digest as well. pub fn payload_digest(prompt: &[u32]) -> ContentId { let mut bytes = Vec::with_capacity(PAYLOAD_DOMAIN.len() + prompt.len() * 4); bytes.extend_from_slice(PAYLOAD_DOMAIN); for t in prompt { bytes.extend_from_slice(&t.to_le_bytes()); } ContentId::of_bytes(&bytes) } /// Commit to a generation's output `tokens`, for the request `request_id` /// served on the prompt payload whose [`payload_digest`] is `payload`, under /// `opening` (SPEC §6.4): the content address of the domain, the opening, the /// request id, the payload digest and the tokens as little-endian `u32`s. /// /// BLAKE3 with a secret 32-byte prefix is a commitment: without the opening /// it reveals nothing about the tokens, however few they are, and no one can /// find a second output under the same opening. Binding the request id means /// an opening disclosed for one receipt opens no other. Binding the payload /// means an output that opens it was served on that payload, so a replay can /// tell a different payload from a different output. pub fn output_commitment(opening: &[u8; 32], request_id: &ContentId, payload: &ContentId, tokens: &[u32]) -> ContentId { let mut bytes = Vec::with_capacity(OUTPUT_DOMAIN.len() + 96 + tokens.len() * 4); bytes.extend_from_slice(OUTPUT_DOMAIN); bytes.extend_from_slice(opening); bytes.extend_from_slice(&request_id.0); bytes.extend_from_slice(&payload.0); for t in tokens { bytes.extend_from_slice(&t.to_le_bytes()); } ContentId::of_bytes(&bytes) } /// A steward's hash-chained receipt log. Each [`append`](ReceiptLog::append) /// stamps the next sequence and the prior envelope's content id, seals the /// receipt, and advances the head — so the log is tamper-evident end to end. pub struct ReceiptLog { steward_sk: SigningKey, steward: String, head: Option, seq: u64, entries: Vec>, } impl ReceiptLog { /// Start an empty log for `steward`, signing with `steward_sk`. pub fn new(steward_sk: SigningKey, steward: impl Into) -> Self { Self { steward_sk, steward: steward.into(), head: None, seq: 0, entries: Vec::new(), } } /// The steward identity (COSE `kid`) this log signs under. pub fn steward(&self) -> &str { &self.steward } /// The steward's public key bytes (for verifiers). pub fn verifying_key(&self) -> [u8; 32] { self.steward_sk.verifying_key().to_bytes() } /// Content id of the most recent sealed envelope (the chain head). pub fn head(&self) -> Option { self.head } /// All sealed envelopes appended so far, oldest first. pub fn entries(&self) -> &[Vec] { &self.entries } /// Chain a receipt: stamp `sequence` + `prev`, seal, advance the head. /// Returns the stamped receipt and its sealed envelope. pub fn append(&mut self, receipt: MeshReceipt) -> Result<(MeshReceipt, Vec), ReceiptError> { let (receipt, envelope) = self.seal_next(receipt)?; self.advance(envelope.clone()); Ok((receipt, envelope)) } /// Stamp and seal the next receipt without advancing the log, so a /// steward can record it durably first and [`advance`](Self::advance) /// only once it has. Sealing the same receipt again gives the same bytes. pub fn seal_next(&self, mut receipt: MeshReceipt) -> Result<(MeshReceipt, Vec), ReceiptError> { receipt.sequence = self.seq; receipt.prev = self.head; let envelope = receipt.seal(&self.steward_sk, &self.steward)?; Ok((receipt, envelope)) } /// Advance past an envelope [`seal_next`](Self::seal_next) produced. pub fn advance(&mut self, envelope: Vec) { self.head = Some(ContentId::of_bytes(&envelope)); self.seq += 1; self.entries.push(envelope); } /// Continue a chain an earlier process sealed: the next receipt takes /// `next_sequence` and chains to `head`, the content id of the last /// envelope sealed. Envelopes sealed before are not in /// [`entries`](Self::entries); they were handed out when sealed. pub fn resume(&mut self, next_sequence: u64, head: Option) { self.seq = next_sequence; self.head = head; } /// The sequence the next receipt will take. pub fn next_sequence(&self) -> u64 { self.seq } } /// Lowercase hex without external deps (nonce rendering for the wire form). fn hex_lower(bytes: &[u8]) -> String { let mut s = String::with_capacity(bytes.len() * 2); for b in bytes { s.push(char::from_digit((b >> 4) as u32, 16).unwrap()); s.push(char::from_digit((b & 0x0f) as u32, 16).unwrap()); } s } #[cfg(test)] mod tests { use super::*; use mesh_schema::{DecisionOutcome, Provenance}; fn outcome(accepted: u32) -> ServeOutcome { ServeOutcome { tokens_in: 8, tokens_out: accepted, tokens_accepted: accepted, ..Default::default() } } fn receipt(energy: Energy, accepted: u32) -> MeshReceipt { MeshReceipt { request_id: ContentId::of_bytes(b"req"), grant_id: None, backend: BackendId::new("ref-f1"), recipe: "tiny-greedy".into(), weights_digest: None, precision: None, route_justification: "cheapest-sufficient".into(), outcome: outcome(accepted), fidelity_floor: Fidelity::F1, fidelity_advertised: Some(Fidelity::F2), fidelity_delivered: Some(Fidelity::F2), fidelity_met: true, energy, grant_breach: None, output_commitment: None, sequence: 0, prev: None, nonce: [1, 2, 3, 4, 5, 6, 7, 8], } } /// `recipe` is a name; `weights_digest` and `precision` are what make it /// binding (SPEC §3.6). /// /// Three properties, and the third is the one that matters. Without it the /// other two would pass on fields that reached the struct and never reached /// the signed core — present on the wire, absent from what the signature /// covers, which is exactly the shape of a binding that is not one. #[test] fn the_model_identity_is_sealed_not_merely_carried() { let steward = "did:example:mesh-endpoint"; let sk = SigningKey::from_bytes(&[9u8; 32]); let digest = ContentId::of_bytes(b"the weights that answered"); // 1. Absent is the historical shape: it seals to the bytes it sealed to // before these fields existed, which is what keeps the published // pack's vectors verifying. let bare = receipt(Energy::measured(1_000), 4); assert!(bare.weights_digest.is_none() && bare.precision.is_none()); let bare_wire = bare.to_jcr1_wire(steward); assert!( bare_wire.get("weights_digest").is_none() && bare_wire.get("precision").is_none(), "an unreported identity must be absent from the wire, not null or empty" ); let bare_core = bare.signed_core(steward).unwrap(); // 2. Present, it reaches the wire as hex and as the backend's own name, // and the receipt still seals and verifies. let mut bound = receipt(Energy::measured(1_000), 4); bound.weights_digest = Some(digest); bound.precision = Some("fp16".into()); let bound_wire = bound.to_jcr1_wire(steward); assert_eq!(bound_wire["weights_digest"], json!(digest.to_hex())); assert_eq!(bound_wire["precision"], json!("fp16")); let env = bound.seal(&sk, steward).unwrap(); assert!(bound .verify_envelope(&env, steward, &sk.verifying_key().to_bytes()) .is_ok()); // 3. The binding is in the signed core. Same receipt but for the // identity ⇒ different sealed bytes, so a signature over one cannot // be read as a signature over the other. let bound_core = bound.signed_core(steward).unwrap(); assert_ne!( bare_core, bound_core, "the identity must change the bytes the signature covers, or it binds nothing" ); // And the digest is what distinguishes them, not just its presence: // a different digest is a different core again. let mut other = bound.clone(); other.weights_digest = Some(ContentId::of_bytes(b"different weights")); assert_ne!(bound_core, other.signed_core(steward).unwrap()); } /// A typed decision accepts no output tokens, so the token metric is /// undefined and the decision metric is the one that exists. /// /// The figure is the **CPU-only, process-attributed** one: 486 000 µJ for a /// three-question decision on an M5 Max, 162 mJ per question, from the /// kernel's per-process counter over a span far longer than its measured /// 10 ms period. /// /// Deliberately not the GPU figure. A GPU reading here comes from an SoC /// rail that counts the whole machine, and on this host another process /// was measured holding **99.5% of GPU time** in a 4 s window — so a rail /// delta over a decision's span is an upper bound on the machine, not that /// decision's energy. The per-process CPU counter is unaffected by that /// contention, which is why the sound example uses it. #[test] fn a_decision_has_an_efficiency_figure_where_the_token_metric_has_none() { let mut o = outcome(0); o.decision = Some(DecisionOutcome { questions_answered: 3, options_scored: 8, // choice:3 + score:3 + noul:2 schema_tokens: 121, }); let mut r = receipt(Energy::measured(486_000), 0); r.outcome = o; // The defect: the documented "wedge metric" is undefined here. assert_eq!(r.uj_per_accepted_token(), None); // The fix: the unit of work is the answered question. assert_eq!(r.uj_per_decision(), Some(162_000)); assert_eq!(r.uj_per_option_scored(), Some(60_750)); // It survives the wire, and the wire says what it measured. let wire = r.to_jcr1_wire("did:example:mesh-endpoint"); assert_eq!(wire["uj_per_decision"], json!(162_000)); assert_eq!(wire["questions_answered"], json!(3)); assert_eq!(wire["schema_tokens"], json!(121)); assert!( wire.get("uj_per_accepted_token").is_none(), "an undefined metric must be absent, not zero" ); // And it still seals. let sk = SigningKey::from_bytes(&[9u8; 32]); let steward = "did:example:mesh-endpoint"; let env = r.seal(&sk, steward).unwrap(); assert!(r .verify_envelope(&env, steward, &sk.verifying_key().to_bytes()) .is_ok()); } /// The addition must be invisible to a generation: no new keys, so every /// receipt sealed before it verifies byte-identically. #[test] fn a_generation_receipt_gains_no_wire_keys() { let r = receipt(Energy::measured(3_200_000), 64); let wire = r.to_jcr1_wire("did:example:mesh-endpoint"); for k in [ "questions_answered", "options_scored", "schema_tokens", "uj_per_decision", "uj_per_option_scored", ] { assert!(wire.get(k).is_none(), "{k} must not appear for a generation"); } assert_eq!(r.uj_per_decision(), None); } #[test] fn measured_receipt_seal_verify_and_metric() { let sk = SigningKey::from_bytes(&[9u8; 32]); let steward = "did:example:mesh-endpoint"; // 64 accepted tokens cost 3_200_000 µJ measured → 50_000 µJ/token. let r = receipt(Energy::measured(3_200_000), 64); assert_eq!(r.uj_per_accepted_token(), Some(50_000)); let env = r.seal(&sk, steward).unwrap(); assert!(r .verify_envelope(&env, steward, &sk.verifying_key().to_bytes()) .is_ok()); // Wrong steward must not verify. assert!(r .verify_envelope(&env, "did:example:other", &sk.verifying_key().to_bytes()) .is_err()); } #[test] fn dishonest_hwshunt_cannot_seal() { let sk = SigningKey::from_bytes(&[9u8; 32]); let r = receipt( Energy { measured_uj: None, estimated_uj: 1_000, provenance: Provenance::HwShunt, }, 10, ); assert!(matches!( r.seal(&sk, "did:example:x"), Err(ReceiptError::HwShuntRequiresMeasured) )); } #[test] fn chain_links_and_verifies() { let sk = SigningKey::from_bytes(&[3u8; 32]); let mut log = ReceiptLog::new(sk, "did:example:mesh"); let (r0, _e0) = log.append(receipt(Energy::estimator(1_000), 4)).unwrap(); let (r1, _e1) = log.append(receipt(Energy::estimator(2_000), 8)).unwrap(); assert_eq!(r0.sequence, 0); assert!(r0.prev.is_none()); assert_eq!(r1.sequence, 1); assert_eq!(r1.prev, Some(ContentId::of_bytes(&log.entries()[0]))); // Every entry verifies under the steward key. let vk = log.verifying_key(); for (r, env) in [r0, r1].iter().zip(log.entries()) { assert!(r.verify_envelope(env, log.steward(), &vk).is_ok()); } } /// The commitment binds the request, the opening, the prompt payload and /// every token: change any one and it is another commitment, so an opening /// disclosed for one receipt opens no other, and no other output, nor the /// same output on another payload, opens it. #[test] fn an_output_commitment_binds_request_opening_payload_and_tokens() { let (a, b) = (ContentId::of_bytes(b"request a"), ContentId::of_bytes(b"request b")); let o = [3u8; 32]; let p = payload_digest(&[1, 2]); let t = [7u32, 9, 11]; let c = output_commitment(&o, &a, &p, &t); assert_ne!(c, output_commitment(&o, &b, &p, &t), "the request"); assert_ne!(c, output_commitment(&[4u8; 32], &a, &p, &t), "the opening"); assert_ne!(c, output_commitment(&o, &a, &payload_digest(&[1, 3]), &t), "the payload"); assert_ne!(c, output_commitment(&o, &a, &p, &[7, 9, 12]), "a token"); assert_ne!(c, output_commitment(&o, &a, &p, &[7, 9]), "the length"); assert_eq!(c, output_commitment(&o, &a, &p, &t), "and nothing else"); assert_ne!(payload_digest(&[]), payload_digest(&[0]), "an empty payload is not a zero token"); } }