diff --git a/jcp/jcp-rs/crates/jcp-efficiency/examples/session.rs b/jcp/jcp-rs/crates/jcp-efficiency/examples/session.rs index e7b0fdb0b..d7667a575 100644 --- a/jcp/jcp-rs/crates/jcp-efficiency/examples/session.rs +++ b/jcp/jcp-rs/crates/jcp-efficiency/examples/session.rs @@ -59,7 +59,7 @@ fn main() { " {:16} {:>5} tok {:>7} µJ/tok {:>6.0} tok/s {}", t.task, t.tokens_out, - t.uj_per_accepted_token(), + dash(t.uj_per_accepted_token()), t.tokens_per_sec(), mark ); @@ -75,12 +75,20 @@ fn main() { println!(" wasted units : {} (tokens burned, no result)", t.wasted_units); println!( " tokens/completed : {} ← charges EVERY attempt (incl. failures) to results delivered", - t.tokens_per_completed_unit() + dash(t.tokens_per_completed_unit()) ); - println!(" µJ/completed : {}", t.uj_per_completed_unit()); + println!(" µJ/completed : {}", dash(t.uj_per_completed_unit())); println!(" value / joule : {:.4} µUSD/J", t.value_per_joule()); println!( - " WASTE FRACTION : {:.0}% ← the number a 'cost per token' rate hides", - t.waste_fraction() * 100.0 + " WASTE FRACTION : {} ← the number a 'cost per token' rate hides", + t.waste_fraction() + .map_or_else(|| "— (no tokens spent)".to_string(), |f| format!("{:.0}%", f * 100.0)) ); } + +/// Render a figure that may be undefined. An undefined cost-per-result ratio is +/// shown as "—", never as 0: zero is the optimum of these figures, so printing it +/// would rank spend that bought nothing above every run that delivered. +fn dash(v: Option) -> String { + v.map_or_else(|| "—".to_string(), |n| n.to_string()) +} diff --git a/jcp/jcp-rs/crates/jcp-efficiency/src/lib.rs b/jcp/jcp-rs/crates/jcp-efficiency/src/lib.rs index dbe47cc84..57b232263 100644 --- a/jcp/jcp-rs/crates/jcp-efficiency/src/lib.rs +++ b/jcp/jcp-rs/crates/jcp-efficiency/src/lib.rs @@ -83,9 +83,17 @@ impl Efficiency { } /// Microjoules per accepted output token — the headline energy figure, matching - /// the family's `uj_per_accepted_token` convention. `0` if no tokens accepted. - pub fn uj_per_accepted_token(&self) -> u64 { - self.energy.uj().checked_div(self.tokens_out).unwrap_or(0) + /// the family's `uj_per_accepted_token` convention: `None` when no tokens were + /// accepted, because the division is undefined then. + /// + /// It is not `0`. This is a cost-per-result figure, so `0` is its *optimum* — + /// returning it for an uncomputable ratio reported the worst outcome (spend + /// that bought nothing, `completed: false`) as the best one, and the figure is + /// sealed and read straight back out rather than recomputed. `mesh/SPEC.md` + /// §6 states the family convention this matches: "omitted when no tokens were + /// accepted". + pub fn uj_per_accepted_token(&self) -> Option { + self.energy.uj().checked_div(self.tokens_out) } /// Accepted tokens per second — real decode throughput. `0.0` if no time @@ -101,7 +109,10 @@ impl Efficiency { /// Micro-USD of value per accepted token — economic efficiency — when a value /// figure is given. `None` otherwise (never invented). pub fn uusd_per_accepted_token(&self) -> Option { - self.value_uusd.map(|v| v.checked_div(self.tokens_out).unwrap_or(0)) + // `and_then`, not `map(... .unwrap_or(0))`: with no accepted tokens the + // ratio is undefined, and this function's contract is that a value figure + // is never invented. + self.value_uusd.and_then(|v| v.checked_div(self.tokens_out)) } /// Commit this measurement to an external energy attestation — the raw report @@ -141,14 +152,22 @@ impl Efficiency { "energy_uj": self.energy.uj(), "provenance": self.energy.provenance.jcr1_label(), "wall_ns": self.wall_ns, - "uj_per_accepted_token": self.uj_per_accepted_token(), "seq": seq, "prev": prev.to_hex(), "steward": steward, }); + // Omitted when undefined, never sealed as 0 — the same shape + // `mesh-receipt` seals its wedge metric with, and what `mesh/SPEC.md` §6 + // specifies. Canonical CBOR sorts keys, so insertion order here never + // reaches the wire. + if let Some(uj) = self.uj_per_accepted_token() { + o["uj_per_accepted_token"] = json!(uj); + } if let Some(v) = self.value_uusd { o["value_uusd"] = json!(v); - o["uusd_per_accepted_token"] = json!(self.uusd_per_accepted_token().unwrap_or(0)); + if let Some(per) = self.uusd_per_accepted_token() { + o["uusd_per_accepted_token"] = json!(per); + } } if let Some(a) = &self.energy_attestation { o["energy_attestation"] = json!(a); @@ -242,8 +261,10 @@ pub struct EfficiencyReceipt { pub provenance: String, /// Wall-clock nanoseconds. pub wall_ns: u64, - /// Microjoules per accepted token (headline). - pub uj_per_accepted_token: u64, + /// Microjoules per accepted token (headline). `None` when the receipt sealed + /// no figure because no tokens were accepted — an undefined ratio, not zero + /// cost per token. + pub uj_per_accepted_token: Option, /// Value delivered, micro-USD (when stated). pub value_uusd: Option, /// Hex content-id commitment to an external energy attestation (when present) — @@ -298,7 +319,7 @@ pub fn verify(envelope: &[u8], steward: &str, pubkey: &[u8; 32]) -> Result u64 { + /// results actually delivered. `None` when nothing completed. + /// + /// The claim is that it "charges every attempt against the results actually + /// delivered", which it stops doing precisely when nothing was delivered: the + /// denominator is zero and the ratio is undefined. Reporting `0` there named + /// the cheapest possible cost per result for spend that produced none. + pub fn tokens_per_completed_unit(&self) -> Option { self.completed_tokens .saturating_add(self.wasted_tokens) .checked_div(self.completed_units) - .unwrap_or(0) } - /// Total microjoules per completed unit, including waste. - pub fn uj_per_completed_unit(&self) -> u64 { + /// Total microjoules per completed unit, including waste. `None` when nothing + /// completed — see [`Self::tokens_per_completed_unit`]. + pub fn uj_per_completed_unit(&self) -> Option { self.completed_uj .saturating_add(self.wasted_uj) .checked_div(self.completed_units) - .unwrap_or(0) } /// Micro-USD of value delivered per joule spent (including waste) — the ultimate /// efficiency figure. `0.0` if no energy spent. + /// + /// Kept as `0.0`, unlike the cost-per-unit ratios above: higher is better here, + /// so the fallback is this figure's *worst* value, not its optimum. The same + /// holds for [`Efficiency::tokens_per_sec`]. A zero that understates is not the + /// defect those `Option`s fix. pub fn value_per_joule(&self) -> f64 { let total_uj = self.completed_uj.saturating_add(self.wasted_uj); if total_uj == 0 { @@ -384,13 +414,15 @@ impl Totals { } } - /// The share of spent tokens that bought nothing (0.0–1.0). - pub fn waste_fraction(&self) -> f64 { + /// The share of spent tokens that bought nothing (0.0–1.0). `None` when no + /// tokens were spent at all, since `0.0` is this figure's optimum — an empty + /// ledger reported a perfect zero-waste record. + pub fn waste_fraction(&self) -> Option { let total = self.completed_tokens.saturating_add(self.wasted_tokens); if total == 0 { - 0.0 + None } else { - self.wasted_tokens as f64 / total as f64 + Some(self.wasted_tokens as f64 / total as f64) } } } @@ -438,7 +470,7 @@ mod tests { #[test] fn the_four_metrics_compute_from_the_inputs() { let e = measurement("t", true, 500, 1_000_000, Some(2_000_000)); - assert_eq!(e.uj_per_accepted_token(), 2_000); // 1e6 µJ / 500 tok + assert_eq!(e.uj_per_accepted_token(), Some(2_000)); // 1e6 µJ / 500 tok assert_eq!(e.tokens_per_sec(), 500.0); // 500 tok / 1 s assert_eq!(e.uusd_per_accepted_token(), Some(4_000)); // 2e6 µUSD / 500 assert_eq!(e.tokens_total(), 2_500); // 4×500 in + 500 out @@ -450,6 +482,96 @@ mod tests { assert_eq!(e.uusd_per_accepted_token(), None); } + /// The case the sealed headline figure used to misreport: 10 J spent, + /// nothing accepted, `completed: false` — which the crate documents as + /// meaning "the tokens/joules below bought nothing". + /// + /// The ratio is undefined, and `0` is the *optimum* of a cost-per-result + /// figure — better than any successful run — so the receipt seals no figure + /// rather than the best possible one. `mesh/SPEC.md` §6 specifies the same + /// convention: "omitted when no tokens were accepted". + #[test] + fn spend_that_bought_nothing_seals_no_headline_figure() { + let sk = key(); + let e = measurement("t", false, 0, 10_000_000, None); + assert_eq!(e.energy.uj(), 10_000_000, "the spend is real"); + assert_eq!( + e.uj_per_accepted_token(), + None, + "0 uJ/token would read as free, and as better than any completed run" + ); + + let wire = e.wire(0, &ContentId([0u8; 32]), "did:jcp:eff"); + assert!( + wire.get("uj_per_accepted_token").is_none(), + "an undefined figure is omitted from the signed core, not sealed as 0: {wire}" + ); + + let env = e.seal(0, &ContentId([0u8; 32]), &sk, "did:jcp:eff"); + let rec = verify(&env, "did:jcp:eff", &sk.verifying_key().to_bytes()) + .expect("a receipt without the figure still verifies"); + assert_eq!(rec.uj_per_accepted_token, None); + assert_eq!(rec.energy_uj, 10_000_000, "and the spend is still on it"); + } + + /// The value projection is not invented either. `value_uusd` is an input and + /// still seals; the per-token ratio derived from it does not. + #[test] + fn a_value_figure_is_not_invented_without_accepted_tokens() { + let e = measurement("t", false, 0, 10_000_000, Some(2_000_000)); + assert_eq!(e.uusd_per_accepted_token(), None); + + let wire = e.wire(0, &ContentId([0u8; 32]), "did:jcp:eff"); + assert_eq!( + wire.get("value_uusd").and_then(Value::as_u64), + Some(2_000_000), + "the stated value is an input, not a ratio" + ); + assert!(wire.get("uusd_per_accepted_token").is_none()); + } + + /// The aggregate half, through the real seal -> verify -> aggregate path: + /// units that spent energy and completed nothing have no cost per completed + /// unit. Not 0, which would be the cheapest ledger expressible. + #[test] + fn a_ledger_with_nothing_completed_reports_no_cost_per_unit() { + let sk = key(); + let pk = sk.verifying_key().to_bytes(); + let recs: Vec = (0..3) + .map(|i| { + let e = measurement("t", false, 0, 10_000_000, None); + let env = e.seal(i, &ContentId([0u8; 32]), &sk, "did:jcp:eff"); + verify(&env, "did:jcp:eff", &pk).expect("seals and verifies") + }) + .collect(); + for r in &recs { + assert_eq!(r.uj_per_accepted_token, None); + } + + let t = aggregate(&recs); + assert_eq!(t.completed_units, 0); + assert_eq!(t.wasted_units, 3); + assert_eq!(t.wasted_uj, 30_000_000, "30 J of real spend"); + assert_eq!( + t.uj_per_completed_unit(), + None, + "0 uJ per completed unit would be the cheapest record possible" + ); + assert_eq!(t.tokens_per_completed_unit(), None); + + // An empty ledger has no waste share at all, rather than a perfect one. + assert_eq!(Totals::default().waste_fraction(), None); + } + + /// The figures whose zero fallback is their *worst* value keep it: a zero + /// that understates is not the defect the `Option`s above fix. + #[test] + fn a_fallback_toward_the_worst_value_is_left_alone() { + let e = measurement("t", true, 0, 1_000, None); + assert_eq!(e.tokens_per_sec(), 0.0, "throughput: higher is better"); + assert_eq!(Totals::default().value_per_joule(), 0.0, "value: higher is better"); + } + #[test] fn seal_verify_round_trips_and_tamper_fails() { let sk = key(); @@ -459,7 +581,7 @@ mod tests { assert_eq!(rec.task, "fix-123"); assert!(rec.completed); assert_eq!(rec.tokens_out, 400); - assert_eq!(rec.uj_per_accepted_token, 2_000); + assert_eq!(rec.uj_per_accepted_token, Some(2_000)); assert_eq!(rec.value_uusd, Some(1_000_000)); assert_eq!(rec.tokens_per_sec(), 400.0); @@ -520,9 +642,9 @@ mod tests { assert_eq!(t.completed_tokens, 3_000); assert_eq!(t.wasted_tokens, 4_500); // tokens per completed unit charges ALL spend (7500) to the 2 done units. - assert_eq!(t.tokens_per_completed_unit(), 3_750); + assert_eq!(t.tokens_per_completed_unit(), Some(3_750)); assert_eq!(t.value_uusd, 1_000_000); // more than half the tokens bought nothing — the number "cost/token" hides. - assert!(t.waste_fraction() > 0.5); + assert!(t.waste_fraction().expect("tokens were spent") > 0.5); } } diff --git a/jouledesk/jouledesk-rs/crates/jouledesk/examples/channel.rs b/jouledesk/jouledesk-rs/crates/jouledesk/examples/channel.rs index 652e02f6f..62d6819dc 100644 --- a/jouledesk/jouledesk-rs/crates/jouledesk/examples/channel.rs +++ b/jouledesk/jouledesk-rs/crates/jouledesk/examples/channel.rs @@ -66,7 +66,10 @@ fn main() { println!(" wasted units : {} (ran, produced no result)", s.wasted_units); println!( " µJ / completed : {} (charges the failed build's energy across the completed work)", + // "—" when nothing completed: 0 is this figure's optimum, so printing it + // would name the cheapest possible cost for work that delivered none. s.uj_per_completed_unit() + .map_or_else(|| "— (nothing completed)".to_string(), |uj| uj.to_string()) ); println!( "\n Note: 'deploy' was refused (no held skill) and the third 'summarize' was refused\n \ diff --git a/jouledesk/jouledesk-rs/crates/jouledesk/examples/room.rs b/jouledesk/jouledesk-rs/crates/jouledesk/examples/room.rs index 6af85c7d9..82f3e3b77 100644 --- a/jouledesk/jouledesk-rs/crates/jouledesk/examples/room.rs +++ b/jouledesk/jouledesk-rs/crates/jouledesk/examples/room.rs @@ -53,7 +53,9 @@ fn main() { println!(" tokens : {} in · {} out", r.tokens_in, r.tokens_out); println!( " energy (derived): {} µJ ({} µJ / accepted token)", - r.energy_uj, r.uj_per_accepted_token + r.energy_uj, + r.uj_per_accepted_token + .map_or_else(|| "—".to_string(), |uj| uj.to_string()) ); println!(" verified under : {}", call.steward()); println!( diff --git a/jouledesk/jouledesk-rs/crates/jouledesk/src/lib.rs b/jouledesk/jouledesk-rs/crates/jouledesk/src/lib.rs index 928372ed8..4b8f6ffe5 100644 --- a/jouledesk/jouledesk-rs/crates/jouledesk/src/lib.rs +++ b/jouledesk/jouledesk-rs/crates/jouledesk/src/lib.rs @@ -531,7 +531,9 @@ mod tests { // The per-completed-unit cost charges the wasted build across the two summaries, // so it exceeds the summaries' own per-unit cost — the ROI a count would hide. assert!( - s.uj_per_completed_unit() > s.completed_uj / s.completed_units, + s.uj_per_completed_unit() + .expect("two units completed, so the ratio is defined") + > s.completed_uj / s.completed_units, "waste inflates joules-per-completed-unit" ); assert_eq!(t.efficiency_ledger().len(), 3, "one efficiency/1 per task that ran"); diff --git a/joulehook/joulehook-rs/crates/joulehook-meter/src/lib.rs b/joulehook/joulehook-rs/crates/joulehook-meter/src/lib.rs index a56f7363a..c09fe7ab9 100644 --- a/joulehook/joulehook-rs/crates/joulehook-meter/src/lib.rs +++ b/joulehook/joulehook-rs/crates/joulehook-meter/src/lib.rs @@ -172,7 +172,7 @@ mod tests { let r = jcp_efficiency::verify(&sealed, steward, &pk).expect("efficiency/1 verifies"); assert_eq!(r.tokens_out, 900); assert_eq!(r.energy_uj, 2_700_000); - assert_eq!(r.uj_per_accepted_token, 2_700_000 / 900); // 3000 µJ/accepted token + assert_eq!(r.uj_per_accepted_token, Some(2_700_000 / 900)); // 3000 µJ/accepted token assert_eq!(r.provenance, "ModelBased"); } diff --git a/sandbox/sandbox-rs/crates/sandbox-tee/examples/attested_efficiency.rs b/sandbox/sandbox-rs/crates/sandbox-tee/examples/attested_efficiency.rs index ecf63fbf7..b24ddfc5b 100644 --- a/sandbox/sandbox-rs/crates/sandbox-tee/examples/attested_efficiency.rs +++ b/sandbox/sandbox-rs/crates/sandbox-tee/examples/attested_efficiency.rs @@ -185,7 +185,12 @@ fn main() { r.receipt.provenance ); println!(" nonce : fresh (matches the issued challenge)"); - println!(" efficiency : {} µJ / link", r.receipt.uj_per_accepted_token); + println!( + " efficiency : {} µJ / link", + r.receipt + .uj_per_accepted_token + .map_or_else(|| "—".to_string(), |uj| uj.to_string()) + ); println!(" TRUST LEVEL : {:?}, over a `{}` figure", r.trust, counter.tag()); println!();