1use crate::crypto_helpers::to_noir_hex;
4use ethers::types::{Address, U256};
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7
8pub trait ProverInput {
9 fn to_prover_map(&self) -> HashMap<String, String>;
11}
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct NotePlaintext {
15 pub value: U256,
16 pub asset_id: U256,
17 pub secret: U256,
18 pub nullifier: U256,
19 pub timelock: U256,
20 pub hashlock: U256,
21}
22
23impl NotePlaintext {
24 #[must_use]
25 #[allow(clippy::expect_used)]
26 pub fn random(value: U256, asset: Address) -> Self {
27 use rand::RngCore;
28 let mut rng = rand::rngs::OsRng;
29
30 let modulus = U256::from_dec_str(
31 "21888242871839275222246405745257275088548364400416034343698204186575808495617",
32 )
33 .expect("BN254 scalar field modulus is a valid decimal string");
34
35 let mut secret_bytes = [0u8; 32];
36 rng.fill_bytes(&mut secret_bytes);
37 let secret = U256::from_big_endian(&secret_bytes) % modulus;
38
39 let mut nullifier_bytes = [0u8; 32];
40 rng.fill_bytes(&mut nullifier_bytes);
41 let nullifier = U256::from_big_endian(&nullifier_bytes) % modulus;
42
43 Self {
44 value,
45 asset_id: address_to_field(asset),
46 secret,
47 nullifier,
48 timelock: U256::zero(),
49 hashlock: U256::zero(),
50 }
51 }
52
53 pub fn add_to_map(&self, map: &mut HashMap<String, String>, prefix: &str) {
54 map.insert(format!("{prefix}.value"), to_noir_hex(self.value));
55 map.insert(format!("{prefix}.asset_id"), to_noir_hex(self.asset_id));
56 map.insert(format!("{prefix}.secret"), to_noir_hex(self.secret));
57 map.insert(format!("{prefix}.nullifier"), to_noir_hex(self.nullifier));
58 map.insert(format!("{prefix}.timelock"), to_noir_hex(self.timelock));
59 map.insert(format!("{prefix}.hashlock"), to_noir_hex(self.hashlock));
60 }
61}
62
63pub type CompliancePk = (U256, U256);
64
65#[derive(Debug, Clone)]
66pub struct DepositInputs {
67 pub note_plaintext: NotePlaintext,
68 pub ephemeral_sk: U256,
69 pub compliance_pk: CompliancePk,
70}
71
72impl DepositInputs {
73 #[must_use]
74 pub fn new(note: NotePlaintext, ephemeral_sk: U256, compliance_pk: CompliancePk) -> Self {
75 Self {
76 note_plaintext: note,
77 ephemeral_sk,
78 compliance_pk,
79 }
80 }
81}
82
83impl ProverInput for DepositInputs {
84 fn to_prover_map(&self) -> HashMap<String, String> {
85 let mut map = HashMap::new();
86
87 map.insert("ephemeral_sk".into(), to_noir_hex(self.ephemeral_sk));
88 self.note_plaintext.add_to_map(&mut map, "note_plaintext");
89
90 map.insert(
91 "compliance_pubkey_x".into(),
92 to_noir_hex(self.compliance_pk.0),
93 );
94 map.insert(
95 "compliance_pubkey_y".into(),
96 to_noir_hex(self.compliance_pk.1),
97 );
98
99 map
100 }
101}
102
103#[derive(Debug, Clone)]
104pub struct WithdrawInputs {
105 pub withdraw_value: U256,
106 pub recipient: Address,
107 pub merkle_root: U256,
108 pub current_timestamp: u64,
109 pub intent_hash: U256,
110 pub compliance_pk: CompliancePk,
111
112 pub old_note: NotePlaintext,
114 pub old_shared_secret: U256,
115 pub old_note_index: u64,
116 pub old_note_path: Vec<U256>,
117 pub hashlock_preimage: U256,
118
119 pub change_note: NotePlaintext,
121 pub change_ephemeral_sk: U256,
122}
123
124impl ProverInput for WithdrawInputs {
125 fn to_prover_map(&self) -> HashMap<String, String> {
126 let mut map = HashMap::new();
127
128 map.insert("withdraw_value".into(), to_noir_hex(self.withdraw_value));
129 map.insert(
130 "_recipient".into(),
131 to_noir_hex(address_to_field(self.recipient)),
132 );
133 map.insert("merkle_root".into(), to_noir_hex(self.merkle_root));
134 map.insert(
135 "current_timestamp".into(),
136 to_noir_hex(U256::from(self.current_timestamp)),
137 );
138 map.insert("_intent_hash".into(), to_noir_hex(self.intent_hash));
139 map.insert(
140 "compliance_pubkey_x".into(),
141 to_noir_hex(self.compliance_pk.0),
142 );
143 map.insert(
144 "compliance_pubkey_y".into(),
145 to_noir_hex(self.compliance_pk.1),
146 );
147
148 self.old_note.add_to_map(&mut map, "old_note");
149 map.insert(
150 "old_shared_secret".into(),
151 to_noir_hex(self.old_shared_secret),
152 );
153 map.insert(
154 "old_note_index".into(),
155 to_noir_hex(U256::from(self.old_note_index)),
156 );
157 map.insert(
158 "old_note_path".into(),
159 format_path_array(&self.old_note_path),
160 );
161 map.insert(
162 "hashlock_preimage".into(),
163 to_noir_hex(self.hashlock_preimage),
164 );
165
166 self.change_note.add_to_map(&mut map, "change_note");
167 map.insert(
168 "change_ephemeral_sk".into(),
169 to_noir_hex(self.change_ephemeral_sk),
170 );
171
172 map
173 }
174}
175
176#[derive(Debug, Clone)]
177pub struct DLEQProof {
178 pub u: (U256, U256),
179 pub v: (U256, U256),
180 pub z: U256,
181}
182
183impl DLEQProof {
184 pub fn add_to_map(&self, map: &mut HashMap<String, String>, prefix: &str) {
185 map.insert(format!("{prefix}.U.x"), to_noir_hex(self.u.0));
186 map.insert(format!("{prefix}.U.y"), to_noir_hex(self.u.1));
187 map.insert(format!("{prefix}.V.x"), to_noir_hex(self.v.0));
188 map.insert(format!("{prefix}.V.y"), to_noir_hex(self.v.1));
189 map.insert(format!("{prefix}.z"), to_noir_hex(self.z));
190 }
191}
192
193#[derive(Debug, Clone)]
194pub struct TransferInputs {
195 pub merkle_root: U256,
196 pub current_timestamp: u64,
197 pub compliance_pk: CompliancePk,
198
199 pub recipient_b: (U256, U256), pub recipient_p: (U256, U256), pub recipient_proof: DLEQProof,
203
204 pub old_note: NotePlaintext,
206 pub old_shared_secret: U256,
207 pub old_note_index: u64,
208 pub old_note_path: Vec<U256>,
209 pub hashlock_preimage: U256,
210
211 pub memo_note: NotePlaintext,
213 pub memo_ephemeral_sk: U256,
214
215 pub change_note: NotePlaintext,
217 pub change_ephemeral_sk: U256,
218}
219
220impl ProverInput for TransferInputs {
221 fn to_prover_map(&self) -> HashMap<String, String> {
222 let mut map = HashMap::new();
223
224 map.insert("merkle_root".into(), to_noir_hex(self.merkle_root));
225 map.insert(
226 "current_timestamp".into(),
227 to_noir_hex(U256::from(self.current_timestamp)),
228 );
229 map.insert(
230 "compliance_pubkey_x".into(),
231 to_noir_hex(self.compliance_pk.0),
232 );
233 map.insert(
234 "compliance_pubkey_y".into(),
235 to_noir_hex(self.compliance_pk.1),
236 );
237
238 map.insert("recipient_B.x".into(), to_noir_hex(self.recipient_b.0));
239 map.insert("recipient_B.y".into(), to_noir_hex(self.recipient_b.1));
240 map.insert("recipient_P.x".into(), to_noir_hex(self.recipient_p.0));
241 map.insert("recipient_P.y".into(), to_noir_hex(self.recipient_p.1));
242
243 self.recipient_proof.add_to_map(&mut map, "recipient_proof");
244
245 self.old_note.add_to_map(&mut map, "old_note");
246 map.insert(
247 "old_shared_secret".into(),
248 to_noir_hex(self.old_shared_secret),
249 );
250 map.insert(
251 "old_note_index".into(),
252 to_noir_hex(U256::from(self.old_note_index)),
253 );
254 map.insert(
255 "old_note_path".into(),
256 format_path_array(&self.old_note_path),
257 );
258 map.insert(
259 "hashlock_preimage".into(),
260 to_noir_hex(self.hashlock_preimage),
261 );
262
263 self.memo_note.add_to_map(&mut map, "memo_note");
264 map.insert(
265 "memo_ephemeral_sk".into(),
266 to_noir_hex(self.memo_ephemeral_sk),
267 );
268
269 self.change_note.add_to_map(&mut map, "change_note");
270 map.insert(
271 "change_ephemeral_sk".into(),
272 to_noir_hex(self.change_ephemeral_sk),
273 );
274
275 map
276 }
277}
278
279#[derive(Debug, Clone)]
280pub struct GasPaymentInputs {
281 pub merkle_root: U256,
283 pub current_timestamp: u64,
284 pub payment_value: U256,
285 pub payment_asset_id: U256,
286 pub relayer_address: Address,
287 pub execution_hash: U256,
288 pub compliance_pk: CompliancePk,
289
290 pub old_note: NotePlaintext,
292 pub old_shared_secret: U256,
293 pub old_note_index: u64,
294 pub old_note_path: Vec<U256>,
295 pub hashlock_preimage: U256,
296
297 pub change_note: NotePlaintext,
299 pub change_ephemeral_sk: U256,
300}
301
302impl ProverInput for GasPaymentInputs {
303 fn to_prover_map(&self) -> HashMap<String, String> {
304 let mut map = HashMap::new();
305
306 map.insert("merkle_root".into(), to_noir_hex(self.merkle_root));
307 map.insert(
308 "current_timestamp".into(),
309 to_noir_hex(U256::from(self.current_timestamp)),
310 );
311 map.insert("payment_value".into(), to_noir_hex(self.payment_value));
312 map.insert(
313 "payment_asset_id".into(),
314 to_noir_hex(self.payment_asset_id),
315 );
316 map.insert(
317 "_relayer_address".into(),
318 to_noir_hex(address_to_field(self.relayer_address)),
319 );
320 map.insert("_execution_hash".into(), to_noir_hex(self.execution_hash));
321 map.insert(
322 "compliance_pubkey_x".into(),
323 to_noir_hex(self.compliance_pk.0),
324 );
325 map.insert(
326 "compliance_pubkey_y".into(),
327 to_noir_hex(self.compliance_pk.1),
328 );
329
330 self.old_note.add_to_map(&mut map, "old_note");
331 map.insert(
332 "old_shared_secret".into(),
333 to_noir_hex(self.old_shared_secret),
334 );
335 map.insert(
336 "old_note_index".into(),
337 to_noir_hex(U256::from(self.old_note_index)),
338 );
339 map.insert(
340 "old_note_path".into(),
341 format_path_array(&self.old_note_path),
342 );
343 map.insert(
344 "hashlock_preimage".into(),
345 to_noir_hex(self.hashlock_preimage),
346 );
347
348 self.change_note.add_to_map(&mut map, "change_note");
349 map.insert(
350 "change_ephemeral_sk".into(),
351 to_noir_hex(self.change_ephemeral_sk),
352 );
353
354 map
355 }
356}
357
358#[derive(Debug, Clone)]
359pub struct JoinInputs {
360 pub merkle_root: U256,
361 pub current_timestamp: u64,
362 pub compliance_pk: CompliancePk,
363
364 pub note_a: NotePlaintext,
366 pub secret_a: U256,
367 pub index_a: u64,
368 pub path_a: Vec<U256>,
369 pub preimage_a: U256,
370
371 pub note_b: NotePlaintext,
373 pub secret_b: U256,
374 pub index_b: u64,
375 pub path_b: Vec<U256>,
376 pub preimage_b: U256,
377
378 pub note_out: NotePlaintext,
380 pub sk_out: U256,
381}
382
383impl ProverInput for JoinInputs {
384 fn to_prover_map(&self) -> HashMap<String, String> {
385 let mut map = HashMap::new();
386
387 map.insert("merkle_root".into(), to_noir_hex(self.merkle_root));
388 map.insert(
389 "current_timestamp".into(),
390 to_noir_hex(U256::from(self.current_timestamp)),
391 );
392 map.insert(
393 "compliance_pubkey_x".into(),
394 to_noir_hex(self.compliance_pk.0),
395 );
396 map.insert(
397 "compliance_pubkey_y".into(),
398 to_noir_hex(self.compliance_pk.1),
399 );
400
401 self.note_a.add_to_map(&mut map, "note_a");
402 map.insert("secret_a".into(), to_noir_hex(self.secret_a));
403 map.insert("index_a".into(), to_noir_hex(U256::from(self.index_a)));
404 map.insert("path_a".into(), format_path_array(&self.path_a));
405 map.insert("preimage_a".into(), to_noir_hex(self.preimage_a));
406
407 self.note_b.add_to_map(&mut map, "note_b");
408 map.insert("secret_b".into(), to_noir_hex(self.secret_b));
409 map.insert("index_b".into(), to_noir_hex(U256::from(self.index_b)));
410 map.insert("path_b".into(), format_path_array(&self.path_b));
411 map.insert("preimage_b".into(), to_noir_hex(self.preimage_b));
412
413 self.note_out.add_to_map(&mut map, "note_out");
414 map.insert("sk_out".into(), to_noir_hex(self.sk_out));
415
416 map
417 }
418}
419
420#[derive(Debug, Clone)]
421pub struct SplitInputs {
422 pub merkle_root: U256,
423 pub current_timestamp: u64,
424 pub compliance_pk: CompliancePk,
425
426 pub note_in: NotePlaintext,
428 pub secret_in: U256,
429 pub index_in: u64,
430 pub path_in: Vec<U256>,
431 pub preimage_in: U256,
432
433 pub note_out_1: NotePlaintext,
435 pub sk_out_1: U256,
436 pub note_out_2: NotePlaintext,
437 pub sk_out_2: U256,
438}
439
440impl ProverInput for SplitInputs {
441 fn to_prover_map(&self) -> HashMap<String, String> {
442 let mut map = HashMap::new();
443
444 map.insert("merkle_root".into(), to_noir_hex(self.merkle_root));
445 map.insert(
446 "current_timestamp".into(),
447 to_noir_hex(U256::from(self.current_timestamp)),
448 );
449 map.insert(
450 "compliance_pubkey_x".into(),
451 to_noir_hex(self.compliance_pk.0),
452 );
453 map.insert(
454 "compliance_pubkey_y".into(),
455 to_noir_hex(self.compliance_pk.1),
456 );
457
458 self.note_in.add_to_map(&mut map, "note_in");
459 map.insert("secret_in".into(), to_noir_hex(self.secret_in));
460 map.insert("index_in".into(), to_noir_hex(U256::from(self.index_in)));
461 map.insert("path_in".into(), format_path_array(&self.path_in));
462 map.insert("preimage_in".into(), to_noir_hex(self.preimage_in));
463
464 self.note_out_1.add_to_map(&mut map, "note_out_1");
465 map.insert("sk_out_1".into(), to_noir_hex(self.sk_out_1));
466 self.note_out_2.add_to_map(&mut map, "note_out_2");
467 map.insert("sk_out_2".into(), to_noir_hex(self.sk_out_2));
468
469 map
470 }
471}
472
473#[derive(Debug, Clone)]
474pub struct PublicClaimInputs {
475 pub memo_id: U256,
476 pub compliance_pk: CompliancePk,
477
478 pub val: U256,
479 pub asset_id: U256,
480 pub timelock: U256,
481 pub owner_x: U256,
482 pub owner_y: U256,
483 pub salt: U256,
484
485 pub recipient_sk: U256,
486 pub note_out: NotePlaintext,
487 pub sk_out: U256,
488}
489
490impl ProverInput for PublicClaimInputs {
491 fn to_prover_map(&self) -> HashMap<String, String> {
492 let mut map = HashMap::new();
493
494 map.insert("memo_id".into(), to_noir_hex(self.memo_id));
495 map.insert(
496 "compliance_pubkey_x".into(),
497 to_noir_hex(self.compliance_pk.0),
498 );
499 map.insert(
500 "compliance_pubkey_y".into(),
501 to_noir_hex(self.compliance_pk.1),
502 );
503
504 map.insert("val".into(), to_noir_hex(self.val));
505 map.insert("asset_id".into(), to_noir_hex(self.asset_id));
506 map.insert("timelock".into(), to_noir_hex(self.timelock));
507 map.insert("owner_x".into(), to_noir_hex(self.owner_x));
508 map.insert("owner_y".into(), to_noir_hex(self.owner_y));
509 map.insert("salt".into(), to_noir_hex(self.salt));
510
511 map.insert("recipient_sk".into(), to_noir_hex(self.recipient_sk));
512 self.note_out.add_to_map(&mut map, "note_out");
513 map.insert("sk_out".into(), to_noir_hex(self.sk_out));
514
515 map
516 }
517}
518
519#[must_use]
520pub fn address_to_field(addr: Address) -> U256 {
521 U256::from_big_endian(addr.as_bytes())
522}
523
524fn format_path_array(path: &[U256]) -> String {
525 let items: Vec<_> = path
526 .iter()
527 .map(|p| format!("\"{}\"", to_noir_hex(*p)))
528 .collect();
529 format!("[{}]", items.join(", "))
530}
531
532#[cfg(test)]
533mod tests {
534 use super::*;
535
536 #[test]
537 fn test_note_plaintext_random() {
538 let note = NotePlaintext::random(U256::from(1000), Address::zero());
539 assert!(!note.secret.is_zero());
540 assert!(!note.nullifier.is_zero());
541 }
542
543 #[test]
544 fn test_deposit_inputs_to_map() {
545 let note = NotePlaintext::random(U256::from(1000), Address::zero());
546 let inputs = DepositInputs::new(note, U256::from(12345), (U256::from(1), U256::from(2)));
547
548 let map = inputs.to_prover_map();
549 assert!(map.contains_key("note_plaintext.value"));
550 assert!(map.contains_key("ephemeral_sk"));
551
552 let value = map.get("note_plaintext.value").unwrap();
553 assert!(value.starts_with("0x"));
554 assert_eq!(value.len(), 66); }
556
557 #[test]
558 fn test_format_path_array() {
559 let path = vec![U256::from(1)];
560 let formatted = format_path_array(&path);
561 assert!(formatted.starts_with("[\"0x"));
562 }
563
564 #[test]
565 fn test_proof_inputs_field_element_bounds() {
566 let modulus = U256::from_dec_str(
567 "21888242871839275222246405745257275088548364400416034343698204186575808495617",
568 )
569 .unwrap();
570
571 let note = NotePlaintext::random(U256::from(999), Address::zero());
572 let inputs = DepositInputs::new(note, U256::from(12345), (U256::from(7), U256::from(11)));
573
574 let map = inputs.to_prover_map();
575
576 for (key, hex_val) in &map {
577 if let Some(stripped) = hex_val.strip_prefix("0x") {
578 let num = U256::from_str_radix(stripped, 16)
579 .unwrap_or_else(|_| panic!("key {key} has non-hex value: {hex_val}"));
580 assert!(num < modulus, "key {key} value {num} >= BN254 modulus");
581 }
582 }
583 }
584
585 #[test]
586 fn test_deposit_inputs_map_has_required_keys() {
587 let note = NotePlaintext::random(U256::from(1), Address::zero());
588 let inputs = DepositInputs::new(note, U256::from(99), (U256::from(1), U256::from(2)));
589 let map = inputs.to_prover_map();
590
591 let required = [
592 "note_plaintext.value",
593 "note_plaintext.secret",
594 "note_plaintext.nullifier",
595 "ephemeral_sk",
596 "compliance_pubkey_x",
597 "compliance_pubkey_y",
598 ];
599 for key in required {
600 assert!(map.contains_key(key), "missing required key: {key}");
601 }
602 }
603}