1use std::sync::Arc;
4
5use nox_core::traits::interfaces::{IProverService, InfrastructureError, ZKProofData};
6
7use crate::proof_inputs::{
8 DepositInputs, GasPaymentInputs, JoinInputs, ProverInput, PublicClaimInputs, SplitInputs,
9 TransferInputs, WithdrawInputs,
10};
11
12pub mod circuits {
13 pub const DEPOSIT: &str = "deposit";
14 pub const WITHDRAW: &str = "withdraw";
15 pub const TRANSFER: &str = "transfer";
16 pub const GAS_PAYMENT: &str = "gas_payment";
17 pub const JOIN: &str = "join";
18 pub const SPLIT: &str = "split";
19 pub const PUBLIC_CLAIM: &str = "public_claim";
20}
21
22pub struct ClientProver {
23 backend: Arc<dyn IProverService>,
24}
25
26impl ClientProver {
27 #[must_use]
28 pub fn with_service(backend: Arc<dyn IProverService>) -> Self {
29 Self { backend }
30 }
31
32 pub async fn prove_deposit(
33 &self,
34 inputs: &DepositInputs,
35 ) -> Result<ZKProofData, InfrastructureError> {
36 self.backend
37 .prove(circuits::DEPOSIT, inputs.to_prover_map())
38 .await
39 }
40
41 pub async fn prove_withdraw(
42 &self,
43 inputs: &WithdrawInputs,
44 ) -> Result<ZKProofData, InfrastructureError> {
45 self.backend
46 .prove(circuits::WITHDRAW, inputs.to_prover_map())
47 .await
48 }
49
50 pub async fn prove_transfer(
51 &self,
52 inputs: &TransferInputs,
53 ) -> Result<ZKProofData, InfrastructureError> {
54 self.backend
55 .prove(circuits::TRANSFER, inputs.to_prover_map())
56 .await
57 }
58
59 pub async fn prove_gas_payment(
60 &self,
61 inputs: &GasPaymentInputs,
62 ) -> Result<ZKProofData, InfrastructureError> {
63 self.backend
64 .prove(circuits::GAS_PAYMENT, inputs.to_prover_map())
65 .await
66 }
67
68 pub async fn prove_join(
69 &self,
70 inputs: &JoinInputs,
71 ) -> Result<ZKProofData, InfrastructureError> {
72 self.backend
73 .prove(circuits::JOIN, inputs.to_prover_map())
74 .await
75 }
76
77 pub async fn prove_split(
78 &self,
79 inputs: &SplitInputs,
80 ) -> Result<ZKProofData, InfrastructureError> {
81 self.backend
82 .prove(circuits::SPLIT, inputs.to_prover_map())
83 .await
84 }
85
86 pub async fn prove_public_claim(
87 &self,
88 inputs: &PublicClaimInputs,
89 ) -> Result<ZKProofData, InfrastructureError> {
90 self.backend
91 .prove(circuits::PUBLIC_CLAIM, inputs.to_prover_map())
92 .await
93 }
94
95 pub async fn prove<T: ProverInput>(
96 &self,
97 circuit_name: &str,
98 inputs: &T,
99 ) -> Result<ZKProofData, InfrastructureError> {
100 self.backend
101 .prove(circuit_name, inputs.to_prover_map())
102 .await
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use crate::proof_inputs::{DLEQProof, NotePlaintext};
110 use ethers::types::{Address, U256};
111
112 fn create_test_note() -> NotePlaintext {
113 NotePlaintext {
114 value: U256::from(100),
115 asset_id: U256::from(1),
116 secret: U256::from(12345),
117 nullifier: U256::from(67890),
118 timelock: U256::zero(),
119 hashlock: U256::zero(),
120 }
121 }
122
123 fn create_compliance_pk() -> (U256, U256) {
124 (U256::from(1), U256::from(2))
125 }
126
127 #[test]
128 fn test_deposit_inputs_map_contains_required_keys() {
129 let note = create_test_note();
130 let inputs = DepositInputs::new(note, U256::from(999), create_compliance_pk());
131
132 let map = inputs.to_prover_map();
133
134 assert!(map.contains_key("ephemeral_sk"));
135 assert!(map.contains_key("note_plaintext.value"));
136 assert!(map.contains_key("note_plaintext.asset_id"));
137 assert!(map.contains_key("note_plaintext.secret"));
138 assert!(map.contains_key("note_plaintext.nullifier"));
139 assert!(map.contains_key("note_plaintext.timelock"));
140 assert!(map.contains_key("note_plaintext.hashlock"));
141 assert!(map.contains_key("compliance_pubkey_x"));
142 assert!(map.contains_key("compliance_pubkey_y"));
143 }
144
145 #[test]
146 fn test_withdraw_inputs_map_contains_required_keys() {
147 let old_note = create_test_note();
148 let change_note = create_test_note();
149 let merkle_path = vec![U256::zero(); 32];
150
151 let inputs = WithdrawInputs {
152 withdraw_value: U256::from(50),
153 recipient: Address::zero(),
154 merkle_root: U256::from(1111),
155 current_timestamp: 0,
156 intent_hash: U256::zero(),
157 compliance_pk: create_compliance_pk(),
158 old_note,
159 old_shared_secret: U256::from(2222),
160 old_note_index: 0,
161 old_note_path: merkle_path,
162 hashlock_preimage: U256::zero(),
163 change_note,
164 change_ephemeral_sk: U256::from(3333),
165 };
166
167 let map = inputs.to_prover_map();
168
169 assert!(map.contains_key("withdraw_value"));
170 assert!(map.contains_key("_recipient"));
171 assert!(map.contains_key("merkle_root"));
172 assert!(map.contains_key("current_timestamp"));
173 assert!(map.contains_key("old_note.value"));
174 assert!(map.contains_key("old_shared_secret"));
175 assert!(map.contains_key("old_note_index"));
176 assert!(map.contains_key("old_note_path"));
177 assert!(map.contains_key("change_note.value"));
178 assert!(map.contains_key("change_ephemeral_sk"));
179 }
180
181 #[test]
182 fn test_transfer_inputs_map_contains_required_keys() {
183 let old_note = create_test_note();
184 let memo_note = create_test_note();
185 let change_note = create_test_note();
186 let merkle_path = vec![U256::zero(); 32];
187
188 let inputs = TransferInputs {
189 merkle_root: U256::from(1111),
190 current_timestamp: 0,
191 compliance_pk: create_compliance_pk(),
192 recipient_b: (U256::from(100), U256::from(200)),
193 recipient_p: (U256::from(300), U256::from(400)),
194 recipient_proof: DLEQProof {
195 u: (U256::from(1), U256::from(2)),
196 v: (U256::from(3), U256::from(4)),
197 z: U256::from(5),
198 },
199 old_note,
200 old_shared_secret: U256::from(2222),
201 old_note_index: 0,
202 old_note_path: merkle_path,
203 hashlock_preimage: U256::zero(),
204 memo_note,
205 memo_ephemeral_sk: U256::from(4444),
206 change_note,
207 change_ephemeral_sk: U256::from(5555),
208 };
209
210 let map = inputs.to_prover_map();
211
212 assert!(map.contains_key("merkle_root"));
213 assert!(map.contains_key("recipient_B.x"));
214 assert!(map.contains_key("recipient_B.y"));
215 assert!(map.contains_key("recipient_P.x"));
216 assert!(map.contains_key("recipient_P.y"));
217 assert!(map.contains_key("recipient_proof.U.x"));
218 assert!(map.contains_key("recipient_proof.U.y"));
219 assert!(map.contains_key("recipient_proof.V.x"));
220 assert!(map.contains_key("recipient_proof.V.y"));
221 assert!(map.contains_key("recipient_proof.z"));
222 assert!(map.contains_key("memo_note.value"));
223 assert!(map.contains_key("memo_ephemeral_sk"));
224 assert!(map.contains_key("change_note.value"));
225 }
226
227 #[test]
228 fn test_gas_payment_inputs_map_contains_required_keys() {
229 let old_note = create_test_note();
230 let change_note = create_test_note();
231 let merkle_path = vec![U256::zero(); 32];
232
233 let inputs = GasPaymentInputs {
234 merkle_root: U256::from(1111),
235 current_timestamp: 0,
236 payment_value: U256::from(10),
237 payment_asset_id: U256::from(1),
238 relayer_address: Address::zero(),
239 execution_hash: U256::from(9999),
240 compliance_pk: create_compliance_pk(),
241 old_note,
242 old_shared_secret: U256::from(2222),
243 old_note_index: 0,
244 old_note_path: merkle_path,
245 hashlock_preimage: U256::zero(),
246 change_note,
247 change_ephemeral_sk: U256::from(3333),
248 };
249
250 let map = inputs.to_prover_map();
251
252 assert!(map.contains_key("merkle_root"));
253 assert!(map.contains_key("current_timestamp"));
254 assert!(map.contains_key("payment_value"));
255 assert!(map.contains_key("payment_asset_id"));
256 assert!(map.contains_key("_relayer_address"));
257 assert!(map.contains_key("_execution_hash"));
258 assert!(map.contains_key("old_note.value"));
259 assert!(map.contains_key("old_shared_secret"));
260 assert!(map.contains_key("old_note_index"));
261 assert!(map.contains_key("old_note_path"));
262 assert!(map.contains_key("hashlock_preimage"));
263 assert!(map.contains_key("change_note.value"));
264 }
265
266 #[test]
267 fn test_join_inputs_map_contains_required_keys() {
268 let note_a = create_test_note();
269 let note_b = create_test_note();
270 let note_out = create_test_note();
271 let path = vec![U256::zero(); 32];
272
273 let inputs = JoinInputs {
274 merkle_root: U256::from(1111),
275 current_timestamp: 0,
276 compliance_pk: create_compliance_pk(),
277 note_a,
278 secret_a: U256::from(111),
279 index_a: 0,
280 path_a: path.clone(),
281 preimage_a: U256::zero(),
282 note_b,
283 secret_b: U256::from(222),
284 index_b: 1,
285 path_b: path,
286 preimage_b: U256::zero(),
287 note_out,
288 sk_out: U256::from(333),
289 };
290
291 let map = inputs.to_prover_map();
292
293 assert!(map.contains_key("merkle_root"));
294 assert!(map.contains_key("note_a.value"));
295 assert!(map.contains_key("secret_a"));
296 assert!(map.contains_key("index_a"));
297 assert!(map.contains_key("path_a"));
298 assert!(map.contains_key("note_b.value"));
299 assert!(map.contains_key("secret_b"));
300 assert!(map.contains_key("index_b"));
301 assert!(map.contains_key("path_b"));
302 assert!(map.contains_key("note_out.value"));
303 assert!(map.contains_key("sk_out"));
304 }
305
306 #[test]
307 fn test_split_inputs_map_contains_required_keys() {
308 let note_in = create_test_note();
309 let note_out_1 = create_test_note();
310 let note_out_2 = create_test_note();
311 let path = vec![U256::zero(); 32];
312
313 let inputs = SplitInputs {
314 merkle_root: U256::from(1111),
315 current_timestamp: 0,
316 compliance_pk: create_compliance_pk(),
317 note_in,
318 secret_in: U256::from(111),
319 index_in: 0,
320 path_in: path,
321 preimage_in: U256::zero(),
322 note_out_1,
323 sk_out_1: U256::from(222),
324 note_out_2,
325 sk_out_2: U256::from(333),
326 };
327
328 let map = inputs.to_prover_map();
329
330 assert!(map.contains_key("merkle_root"));
331 assert!(map.contains_key("note_in.value"));
332 assert!(map.contains_key("secret_in"));
333 assert!(map.contains_key("index_in"));
334 assert!(map.contains_key("path_in"));
335 assert!(map.contains_key("note_out_1.value"));
336 assert!(map.contains_key("sk_out_1"));
337 assert!(map.contains_key("note_out_2.value"));
338 assert!(map.contains_key("sk_out_2"));
339 }
340
341 #[test]
342 fn test_hex_formatting() {
343 let note = create_test_note();
344 let inputs = DepositInputs::new(note, U256::from(12345), create_compliance_pk());
345
346 let map = inputs.to_prover_map();
347
348 for value in map.values() {
349 assert!(value.starts_with("0x"), "Value should start with 0x");
350 assert_eq!(value.len(), 66, "Hex value should be 66 chars (0x + 64)");
351 }
352 }
353
354 #[test]
355 fn test_merkle_path_array_formatting() {
356 let note = create_test_note();
357 let change_note = create_test_note();
358 let merkle_path = vec![U256::from(1), U256::from(2), U256::from(3)];
359
360 let inputs = WithdrawInputs {
361 withdraw_value: U256::from(50),
362 recipient: Address::zero(),
363 merkle_root: U256::from(1111),
364 current_timestamp: 0,
365 intent_hash: U256::zero(),
366 compliance_pk: create_compliance_pk(),
367 old_note: note,
368 old_shared_secret: U256::from(2222),
369 old_note_index: 0,
370 old_note_path: merkle_path,
371 hashlock_preimage: U256::zero(),
372 change_note,
373 change_ephemeral_sk: U256::from(3333),
374 };
375
376 let map = inputs.to_prover_map();
377 let path_value = map.get("old_note_path").unwrap();
378
379 assert!(path_value.starts_with("[\"0x"));
380 assert!(path_value.ends_with("\"]"));
381 assert!(path_value.contains(", "));
382 }
383}