Skip to main content

darkpool_client/
prover.rs

1//! Typed proof generation methods for all `DarkPool` circuits, wrapping an `IProverService` backend.
2
3use 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}