zk_protocol/lib.rs
1/// General protocol for ZK attestation between agents
2/// This library provides common types and serialization helpers
3/// that any agent can use without depending on other agents' code.
4
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7
8/// Request to the attester service to generate a ZK proof
9#[derive(Serialize, Deserialize, Debug, Clone)]
10pub struct AttestRequest {
11 pub program_id: String,
12 /// Input data as raw bytes (bincode-serialized)
13 /// Will be passed to the zkVM program via stdin as a single buffer entry.
14 /// For programs that call io::read() multiple times, use `stdin_items` instead.
15 pub input_bytes: Vec<u8>,
16 /// Multiple stdin buffer entries (each pushed separately).
17 /// When present, each entry maps to one sp1_zkvm::io::read() call.
18 /// Takes precedence over `input_bytes` when non-empty.
19 #[serde(default)]
20 pub stdin_items: Vec<Vec<u8>>,
21 /// Expected output for verification (optional, format defined by agent)
22 pub claimed_output: Option<Value>,
23 /// Whether to verify the proof locally before returning
24 #[serde(default = "default_verify")]
25 pub verify_locally: bool,
26 /// Optional external transaction ID (payment processor binding).
27 /// When present, the attester auto-saves the proof keyed by this ID
28 /// so the payment processor can pull it directly — bypassing the LLM.
29 #[serde(default, skip_serializing_if = "Option::is_none")]
30 pub external_id: Option<String>,
31 /// Intent commitment: SHA-256(proof_hash || "||" || external_id).
32 /// Stored alongside the proof for retrieval by the payment processor.
33 #[serde(default, skip_serializing_if = "Option::is_none")]
34 pub intent_commitment: Option<String>,
35 /// Fields verified in the proof (e.g. ["amount", "quantity", "product_id"]).
36 #[serde(default, skip_serializing_if = "Option::is_none")]
37 pub verified_fields: Option<Vec<String>>,
38 /// Actual field values proven (e.g. {"recipient_address": "0x...", "token": "ETH"}).
39 /// Stored alongside the proof so the payment processor can validate values
40 /// without re-running the ZKP — the commitment scheme ensures correctness.
41 #[serde(default, skip_serializing_if = "Option::is_none")]
42 pub field_values: Option<std::collections::HashMap<String, String>>,
43 /// Intent type detected by the ZPI tool (e.g. "send_intent", "spend_intent",
44 /// "exchange_intent"). Stored as workflow_stage on the attester side.
45 #[serde(default, skip_serializing_if = "Option::is_none")]
46 pub intent_type: Option<String>,
47}
48
49fn default_verify() -> bool {
50 true
51}
52
53/// A single field commitment extracted from the proof's public values.
54#[derive(Serialize, Deserialize, Debug, Clone)]
55pub struct FieldCommitment {
56 /// Field name (e.g., "amount", "product_id")
57 pub field: String,
58 /// Hex-encoded SHA-256 commitment: SHA-256(field:value:external_id:secret_salt)
59 pub commitment: String,
60}
61
62/// Response from the attester service
63#[derive(Serialize, Deserialize, Debug, Clone)]
64pub struct AttestResponse {
65 /// Hex-encoded Groth16 proof for on-chain verification
66 pub proof: String,
67 /// Public values committed by the zkVM program (hex-encoded)
68 pub public_values: String,
69 /// VK hash for on-chain verifier (bytes32)
70 pub vk_hash: String,
71 /// Output from the zkVM program
72 pub verified_output: Value,
73 /// Echo back the external_id if one was provided in the request.
74 #[serde(default, skip_serializing_if = "Option::is_none")]
75 pub external_id: Option<String>,
76 /// Field commitments extracted from the proof's public values.
77 /// Each entry is SHA-256(field:value:external_id:secret_salt).
78 #[serde(default, skip_serializing_if = "Option::is_none")]
79 pub field_commitments: Option<Vec<FieldCommitment>>,
80}
81
82/// Response from POST /register-elf
83#[derive(Serialize, Deserialize, Debug, Clone)]
84pub struct RegisterResponse {
85 /// Content-addressable program ID (e.g. "sha256:<hex>")
86 pub program_id: String,
87 /// Version number (defaults to 1)
88 #[serde(default = "default_version")]
89 pub version: i32,
90 /// ISO-8601 timestamp
91 pub registered_at: String,
92}
93
94fn default_version() -> i32 {
95 1
96}
97
98/// Response from an agent's pricing/booking endpoint
99#[derive(Serialize, Deserialize, Debug, Clone)]
100pub struct AgentResponse {
101 /// Agent-specific response data (price, booking ID, etc.)
102 #[serde(flatten)]
103 pub data: Value,
104 /// Program ID for ZK verification
105 pub program_id: String,
106 /// ELF hash of the zkVM program
107 pub elf_hash: String,
108}
109
110/// Helper to serialize any serde-compatible type to bincode bytes
111pub fn serialize_input<T: Serialize>(input: &T) -> Result<Vec<u8>, bincode::Error> {
112 bincode::serialize(input)
113}
114
115/// Helper to deserialize bincode bytes to any serde-compatible type
116pub fn deserialize_output<T: for<'de> Deserialize<'de>>(bytes: &[u8]) -> Result<T, bincode::Error> {
117 bincode::deserialize(bytes)
118}
119
120/// Convert bincode bytes to JSON array format for HTTP transport
121pub fn bytes_to_json_array(bytes: &[u8]) -> Value {
122 Value::Array(bytes.iter().map(|b| Value::Number((*b).into())).collect())
123}
124
125/// Extract bytes from JSON array format
126pub fn json_array_to_bytes(value: &Value) -> Option<Vec<u8>> {
127 if let Value::Array(arr) = value {
128 Some(arr.iter().filter_map(|v| v.as_u64().map(|n| n as u8)).collect())
129 } else {
130 None
131 }
132}