jcp-x402: demonstrate the settlement coupling in the runnable example #25
1 changed files with 37 additions and 2 deletions
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
use ed25519_dalek::SigningKey;
|
use ed25519_dalek::SigningKey;
|
||||||
use jcp_schema::{Did, Funds, Verdict};
|
use jcp_schema::{Did, Funds, Verdict};
|
||||||
use jcp_x402::{ingest_requirements, Gated, X402Profile, X402Requirements};
|
use jcp_x402::{ingest_requirements, Gated, X402Profile, X402Requirements, X402Settlement};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
@ -64,7 +64,42 @@ fn main() {
|
||||||
let eurc = X402Requirements { asset: "EURC".into(), max_amount: 100, ..req.clone() };
|
let eurc = X402Requirements { asset: "EURC".into(), max_amount: 100, ..req.clone() };
|
||||||
println!("charge in EURC (grant is USDC) → {}", verdict(&profile.authorize(&eurc, 1)));
|
println!("charge in EURC (grant is USDC) → {}", verdict(&profile.authorize(&eurc, 1)));
|
||||||
|
|
||||||
// 6. Every decision sealed a JCR-1 receipt that verifies under the steward
|
// 6. THE COUPLING. The host settles the authorized charge on chain and gets
|
||||||
|
// back a transaction; the meter reports what the paid compute actually
|
||||||
|
// consumed. Sealing them together produces the artifact neither side has:
|
||||||
|
// the payment rail knows the transaction but not the energy, an energy
|
||||||
|
// meter knows the joules but not the transaction.
|
||||||
|
let settled = X402Settlement {
|
||||||
|
transaction: Some("0xdeadbeefcafe".into()),
|
||||||
|
network: "base".into(),
|
||||||
|
payer: Some("0xAgentWallet".into()),
|
||||||
|
};
|
||||||
|
let measured_uj = 5_812; // what the meter read, not the 6000 µJ estimate
|
||||||
|
let g = profile.seal_settlement(&req, &settled, measured_uj);
|
||||||
|
println!("\nsettled {} → sealed {}", settled.transaction.as_deref().unwrap_or("-"), verdict(&g));
|
||||||
|
|
||||||
|
// A third party verifies the binding without trusting the gateway: it
|
||||||
|
// recomputes the coupling id from the settlement record it holds and
|
||||||
|
// checks it against the receipt's payload_hash (jcp/2, SPEC §8).
|
||||||
|
let core = jcp_receipt::extract(&g.receipt, profile.steward(), &profile.steward_pubkey())
|
||||||
|
.expect("settlement receipt verifies");
|
||||||
|
let expected = settled.coupling_id(&req.resource, measured_uj).to_hex();
|
||||||
|
println!(
|
||||||
|
" coupling verified: {} (tx + {} µJ bound into payload_hash {}…)",
|
||||||
|
core.payload_hash.as_deref() == Some(expected.as_str()),
|
||||||
|
measured_uj,
|
||||||
|
&expected[..16]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Re-pointing the receipt at a different transaction — or a different
|
||||||
|
// energy figure — yields a different id, so the binding cannot be moved.
|
||||||
|
let other = X402Settlement { transaction: Some("0xfeed".into()), ..settled.clone() };
|
||||||
|
println!(
|
||||||
|
" re-point to another tx → id matches: {}",
|
||||||
|
core.payload_hash.as_deref() == Some(other.coupling_id(&req.resource, measured_uj).to_hex().as_str())
|
||||||
|
);
|
||||||
|
|
||||||
|
// 7. Every decision sealed a JCR-1 receipt that verifies under the steward
|
||||||
// key — a third party trusts the decision, not the gateway.
|
// key — a third party trusts the decision, not the gateway.
|
||||||
let steward = profile.steward().to_string();
|
let steward = profile.steward().to_string();
|
||||||
let pk = profile.steward_pubkey();
|
let pk = profile.steward_pubkey();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue