darkfi_contract_test_harness/
money_otc_swap.rs
1use darkfi::{
20 tx::{ContractCallLeaf, Transaction, TransactionBuilder},
21 zk::halo2::Field,
22 Result,
23};
24use darkfi_money_contract::{
25 client::{swap_v1::SwapCallBuilder, MoneyNote, OwnCoin},
26 model::{MoneyFeeParamsV1, MoneyTransferParamsV1},
27 MoneyFunction, MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
28};
29use darkfi_sdk::{
30 crypto::{contract_id::MONEY_CONTRACT_ID, BaseBlind, Blind, FuncId, MerkleNode},
31 pasta::pallas,
32 ContractCall,
33};
34use darkfi_serial::AsyncEncodable;
35use log::debug;
36use rand::rngs::OsRng;
37
38use super::{Holder, TestHarness};
39
40impl TestHarness {
41 pub async fn otc_swap(
45 &mut self,
46 holder0: &Holder,
47 owncoin0: &OwnCoin,
48 holder1: &Holder,
49 owncoin1: &OwnCoin,
50 block_height: u32,
51 ) -> Result<(Transaction, MoneyTransferParamsV1, Option<MoneyFeeParamsV1>)> {
52 let wallet0 = self.holders.get(holder0).unwrap();
53 let wallet1 = self.holders.get(holder1).unwrap();
54
55 let (mint_pk, mint_zkbin) = self.proving_keys.get(MONEY_CONTRACT_ZKAS_MINT_NS_V1).unwrap();
56 let (burn_pk, burn_zkbin) = self.proving_keys.get(MONEY_CONTRACT_ZKAS_BURN_NS_V1).unwrap();
57
58 let rcpt_spend_hook = FuncId::none();
60 let rcpt_user_data = pallas::Base::ZERO;
61 let rcpt_user_data_blind = Blind::random(&mut OsRng);
62
63 let value_send_blind = Blind::random(&mut OsRng);
65 let value_recv_blind = Blind::random(&mut OsRng);
66 let token_send_blind = BaseBlind::random(&mut OsRng);
67 let token_recv_blind = BaseBlind::random(&mut OsRng);
68
69 let builder = SwapCallBuilder {
71 pubkey: wallet0.keypair.public,
72 value_send: owncoin0.note.value,
73 token_id_send: owncoin0.note.token_id,
74 value_recv: owncoin1.note.value,
75 token_id_recv: owncoin1.note.token_id,
76 user_data_blind_send: rcpt_user_data_blind,
77 spend_hook_recv: rcpt_spend_hook,
78 user_data_recv: rcpt_user_data,
79 value_blinds: [value_send_blind, value_recv_blind],
80 token_blinds: [token_send_blind, token_recv_blind],
81 coin: owncoin0.clone(),
82 tree: wallet0.money_merkle_tree.clone(),
83 mint_zkbin: mint_zkbin.clone(),
84 mint_pk: mint_pk.clone(),
85 burn_zkbin: burn_zkbin.clone(),
86 burn_pk: burn_pk.clone(),
87 };
88
89 let debris0 = builder.build()?;
90 assert!(debris0.params.inputs.len() == 1);
91 assert!(debris0.params.outputs.len() == 1);
92
93 let builder = SwapCallBuilder {
95 pubkey: wallet1.keypair.public,
96 value_send: owncoin1.note.value,
97 token_id_send: owncoin1.note.token_id,
98 value_recv: owncoin0.note.value,
99 token_id_recv: owncoin0.note.token_id,
100 user_data_blind_send: rcpt_user_data_blind,
101 spend_hook_recv: rcpt_spend_hook,
102 user_data_recv: rcpt_user_data,
103 value_blinds: [value_recv_blind, value_send_blind],
104 token_blinds: [token_recv_blind, token_send_blind],
105 coin: owncoin1.clone(),
106 tree: wallet1.money_merkle_tree.clone(),
107 mint_zkbin: mint_zkbin.clone(),
108 mint_pk: mint_pk.clone(),
109 burn_zkbin: burn_zkbin.clone(),
110 burn_pk: burn_pk.clone(),
111 };
112
113 let debris1 = builder.build()?;
114 assert!(debris1.params.inputs.len() == 1);
115 assert!(debris1.params.outputs.len() == 1);
116
117 let swap_full_params = MoneyTransferParamsV1 {
119 inputs: vec![debris0.params.inputs[0].clone(), debris1.params.inputs[0].clone()],
120 outputs: vec![debris0.params.outputs[0].clone(), debris1.params.outputs[0].clone()],
121 };
122
123 let swap_full_proofs = vec![
124 debris0.proofs[0].clone(),
125 debris1.proofs[0].clone(),
126 debris0.proofs[1].clone(),
127 debris1.proofs[1].clone(),
128 ];
129
130 let mut data = vec![MoneyFunction::OtcSwapV1 as u8];
132 swap_full_params.encode_async(&mut data).await?;
133 let call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
134 let mut tx_builder =
135 TransactionBuilder::new(ContractCallLeaf { call, proofs: swap_full_proofs }, vec![])?;
136
137 let mut fee_params = None;
139 let mut fee_signature_secrets = None;
140 if self.verify_fees {
141 let mut tx = tx_builder.build()?;
142 let sigs = tx.create_sigs(&[debris1.signature_secret])?;
143 tx.signatures = vec![sigs];
144
145 let sigs = tx.create_sigs(&[debris0.signature_secret])?;
147 tx.signatures[0].insert(0, sigs[0]);
148
149 let (fee_call, fee_proofs, fee_secrets, _spent_fee_coins, fee_call_params) =
150 self.append_fee_call(holder0, tx, block_height, &[owncoin0.clone()]).await?;
151
152 tx_builder.append(ContractCallLeaf { call: fee_call, proofs: fee_proofs }, vec![])?;
154 fee_signature_secrets = Some(fee_secrets);
155 fee_params = Some(fee_call_params);
156 }
157
158 let mut tx = tx_builder.build()?;
160 let sigs = tx.create_sigs(&[debris1.signature_secret])?;
161 tx.signatures = vec![sigs];
162 let sigs = tx.create_sigs(&[debris0.signature_secret])?;
164 tx.signatures[0].insert(0, sigs[0]);
165
166 if let Some(fee_signature_secrets) = fee_signature_secrets {
167 let sigs = tx.create_sigs(&fee_signature_secrets)?;
168 tx.signatures.push(sigs);
169 }
170
171 Ok((tx, swap_full_params, fee_params))
172 }
173
174 pub async fn execute_otc_swap_tx(
178 &mut self,
179 holder: &Holder,
180 tx: Transaction,
181 swap_params: &MoneyTransferParamsV1,
182 fee_params: &Option<MoneyFeeParamsV1>,
183 block_height: u32,
184 append: bool,
185 ) -> Result<Vec<OwnCoin>> {
186 let wallet = self.holders.get_mut(holder).unwrap();
187
188 wallet.add_transaction("money::otc_swap", tx, block_height).await?;
190
191 let mut found_owncoins = vec![];
192
193 if !append {
194 return Ok(found_owncoins)
195 }
196
197 let mut inputs = swap_params.inputs.to_vec();
198 let mut outputs = swap_params.outputs.to_vec();
199 if let Some(ref fee_params) = fee_params {
200 inputs.push(fee_params.input.clone());
201 outputs.push(fee_params.output.clone());
202 }
203
204 let nullifiers = inputs.iter().map(|i| i.nullifier.inner()).map(|l| (l, l)).collect();
205 wallet.money_null_smt.insert_batch(nullifiers).expect("smt.insert_batch()");
206
207 for input in inputs {
208 if let Some(spent_coin) = wallet
209 .unspent_money_coins
210 .iter()
211 .find(|x| x.nullifier() == input.nullifier)
212 .cloned()
213 {
214 debug!("Found spent OwnCoin({}) for {:?}", spent_coin.coin, holder);
215 wallet.unspent_money_coins.retain(|x| x.nullifier() != input.nullifier);
216 wallet.spent_money_coins.push(spent_coin.clone());
217 }
218 }
219
220 for output in outputs {
221 wallet.money_merkle_tree.append(MerkleNode::from(output.coin.inner()));
222
223 let Ok(note) = output.note.decrypt::<MoneyNote>(&wallet.keypair.secret) else {
225 continue
226 };
227
228 let owncoin = OwnCoin {
229 coin: output.coin,
230 note: note.clone(),
231 secret: wallet.keypair.secret,
232 leaf_position: wallet.money_merkle_tree.mark().unwrap(),
233 };
234
235 debug!("Found new OwnCoin({}) for {:?}", owncoin.coin, holder);
236 wallet.unspent_money_coins.push(owncoin.clone());
237 found_owncoins.push(owncoin);
238 }
239
240 Ok(found_owncoins)
241 }
242}