use wasm_bindgen::prelude::*;
use zk402_prover::{build_payload as prover_build_payload, ClientWitness, ProverConfig};
#[wasm_bindgen(start)]
pub fn init() {
console_error_panic_hook::set_once();
}
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct WasmWitnessInput {
secret_key: String, from: String, payment_nonce: String, proving_key: String, valid_after: u64,
valid_before: u64,
}
#[wasm_bindgen]
pub fn build_payload(requirements_json: &str, witness_json: &str) -> Result<String, JsValue> {
let requirements: zk402_core::PaymentRequirements = serde_json::from_str(requirements_json)
.map_err(|e| JsValue::from_str(&format!("failed to parse requirements: {}", e)))?;
let witness_input: WasmWitnessInput = serde_json::from_str(witness_json)
.map_err(|e| JsValue::from_str(&format!("failed to parse witness: {}", e)))?;
let secret_key = decode_hex(&witness_input.secret_key)?;
let from = decode_address(&witness_input.from)?;
let payment_nonce = decode_nonce(&witness_input.payment_nonce)?;
let proving_key = decode_hex(&witness_input.proving_key)?;
let witness = ClientWitness {
secret_key,
from,
payment_nonce,
};
let config = ProverConfig {
proving_key,
valid_after: witness_input.valid_after,
valid_before: witness_input.valid_before,
};
let payload = prover_build_payload(&requirements, &witness, &config)
.map_err(|e| JsValue::from_str(&format!("proof generation failed: {}", e)))?;
serde_json::to_string(&payload)
.map_err(|e| JsValue::from_str(&format!("failed to serialize payload: {}", e)))
}
fn decode_hex(s: &str) -> Result<Vec<u8>, JsValue> {
let s = s.strip_prefix("0x").unwrap_or(s);
hex::decode(s).map_err(|e| JsValue::from_str(&format!("invalid hex: {}", e)))
}
fn decode_address(s: &str) -> Result<[u8; 20], JsValue> {
let bytes = decode_hex(s)?;
bytes
.try_into()
.map_err(|_| JsValue::from_str("address must be 20 bytes"))
}
fn decode_nonce(s: &str) -> Result<[u8; 32], JsValue> {
let bytes = decode_hex(s)?;
bytes
.try_into()
.map_err(|_| JsValue::from_str("nonce must be 32 bytes"))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn decodes_hex_with_prefix() {
let result = decode_hex("0x1234");
assert!(result.is_ok());
assert_eq!(result.unwrap(), vec![0x12, 0x34]);
}
#[test]
fn decodes_hex_without_prefix() {
let result = decode_hex("1234");
assert!(result.is_ok());
assert_eq!(result.unwrap(), vec![0x12, 0x34]);
}
#[test]
fn decodes_address() {
let addr = "0x0000000000000000000000000000000000000001";
let result = decode_address(addr);
assert!(result.is_ok());
assert_eq!(result.unwrap()[19], 1);
}
#[test]
fn rejects_invalid_address_length() {
let addr = "0x1234"; let result = decode_address(addr);
assert!(result.is_err());
}
}