open-standards/jcp/jcp-rs/crates/jcp-cite/examples/cite.rs
Transaction Science 68cb3c1b13 jcp-cite: the cite example's illustrative retrieval is sealed as an estimate
The `cite` example records a retrieval of 4_200 µJ that no fetch spent
(no document is read) and decided and sealed it as Energy::measured, so
its jcp receipt claimed a hardware reading of work no meter saw.

The cost now comes from `retrieval_cost()`, the citation's figure graded
Estimator with no measured figure; the docs and the first output line say
the retrieval is illustrative. The figure, and so the receipt's cost_uj
and the citation commitment, are unchanged.

tests/cite_example.rs compiles the example in as a module: its cost must
be Estimator with no measured figure, and the receipt sealed over it must
carry energy_provenance "Estimator", decoded from the signed core.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 17:47:12 -04:00

111 lines
4.7 KiB
Rust

//! An agent answers a question, records what it READ, and seals a receipt that
//! commits the provenance. Then a reviewer checks the three things a rendered
//! citation link cannot tell them: is the answer unedited, are the sources
//! intact, and which sources may this reviewer not open.
//!
//! ```text
//! cargo run -p jcp-cite --example cite
//! ```
//!
//! The retrieval is illustrative: no document is fetched, so the energy the
//! citation records is a chosen figure, and the decision it is debited under
//! seals it as an estimate ([`retrieval_cost`]) — never as a hardware reading
//! of work no meter saw.
use ed25519_dalek::SigningKey;
use jcp_cite::{verify, Citation, Source};
use jcp_grant::issue;
use jcp_receipt::{extract, seal};
use jcp_runtime::Runtime;
use jcp_schema::{Capability, ContentId, Did, Energy, Grant};
fn main() {
let issuer = SigningKey::from_bytes(&[1u8; 32]);
let agent = SigningKey::from_bytes(&[2u8; 32]);
let agent_did = Did::from_verifying_key(&agent.verifying_key());
let corpus = ContentId::of_bytes(b"acme-workspace");
// The agent holds two grants — one per sensitivity domain.
let g_finance = ContentId::of_bytes(b"grant:finance");
let g_people = ContentId::of_bytes(b"grant:people");
// It reads two documents to answer, and records EACH with the content it saw
// and the grant it read under.
let doc_a = b"Q3 revenue was 4.2M, up 18% YoY.";
let doc_b = b"Headcount plan: 12 hires in Q4.";
let answer = b"Revenue grew 18%, and 12 hires are planned for Q4.";
let citation = Citation::new(
agent_did,
ContentId::of_bytes(answer),
vec![
Source::new(ContentId::of_bytes(doc_a), g_finance).at("/finance/q3-report"),
Source::new(ContentId::of_bytes(doc_b), g_people).at("/people/q4-plan"),
],
4_200, // µJ the retrieval is put at: illustrative, nothing read it
[7; 16],
);
println!(
"agent read {} sources → answer, {} µJ (illustrative: sealed as an estimate)",
citation.sources.len(),
citation.energy_uj
);
// The retrieval is decided and metered under a real grant, and the receipt
// commits the citation by content id.
let grant = Grant {
issuer: Did::from_verifying_key(&issuer.verifying_key()),
subject: agent_did,
capability: Capability::new(corpus, ["retrieve"]),
budget_uj: 1_000_000,
budget_funds: None,
caveats: Vec::new(),
expiry: 0,
parent: None,
delegate_depth: None,
nonce: [0; 16],
};
let sg = issue(&issuer, grant.clone()).unwrap();
let frame = citation.frame(grant.id(), corpus).unwrap();
let decision = Runtime::new().decide(&frame, &sg, agent_did, retrieval_cost(&citation), 1);
let steward = SigningKey::from_bytes(&[9u8; 32]);
let receipt = seal(&frame, &grant, &decision, &steward, "did:example:steward").unwrap();
let core = extract(&receipt, "did:example:steward", &steward.verifying_key().to_bytes()).unwrap();
let sealed = citation.clone().seal(&agent).unwrap();
println!(
"receipt commits the citation: {} ({} µJ on the receipt)\n",
core.payload_hash.as_deref() == Some(citation.id().unwrap().to_hex().as_str()),
core.cost_uj
);
// ---- a reviewer, holding only the receipt + the signed citation ----
println!("── reviewer checks ──");
println!(" citation signature valid: {}", verify(&sealed).is_ok());
println!(" answer unedited: {}", sealed.citation.verify_answer(answer).is_ok());
println!(" source 0 intact: {}", sealed.citation.verify_source(0, doc_a).is_ok());
// Now the finance doc is edited after the fact. A URL would still resolve —
// to the NEW text. The content-bound citation catches it.
let edited = b"Q3 revenue was 9.9M.";
println!(
" source 0 after an edit: {} ← dangling detected, not silently wrong",
sealed.citation.verify_source(0, edited).is_ok()
);
// A reviewer cleared only for finance is TOLD which sources they may not open.
let unreadable = sealed.citation.unreadable_by(&[g_finance]);
println!(" sources this reviewer may not open: {:?} (of {})", unreadable, sealed.citation.sources.len());
for i in &unreadable {
if let Some(loc) = sealed.citation.sources[*i].locator.as_deref() {
println!(" → {loc} requires authority you do not hold");
}
}
}
/// The cost the retrieval is debited and sealed at: the citation's recorded
/// figure, graded `Estimator` with no measured figure, because nothing in
/// this example read it.
pub fn retrieval_cost(citation: &Citation) -> Energy {
Energy::estimator(citation.energy_uj)
}