darkfi_contract_test_harness/
money_transfer.rs1use darkfi::{
20 tx::{ContractCallLeaf, Transaction, TransactionBuilder},
21 Result,
22};
23use darkfi_money_contract::{
24 client::{transfer_v1::make_transfer_call, MoneyNote, OwnCoin},
25 model::{MoneyFeeParamsV1, MoneyTransferParamsV1, TokenId},
26 MoneyFunction, MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
27};
28use darkfi_sdk::{
29 crypto::{contract_id::MONEY_CONTRACT_ID, MerkleNode},
30 ContractCall,
31};
32use darkfi_serial::AsyncEncodable;
33use log::debug;
34
35use super::{Holder, TestHarness};
36
37impl TestHarness {
38 #[allow(clippy::too_many_arguments)]
40 pub async fn transfer(
41 &mut self,
42 amount: u64,
43 holder: &Holder,
44 recipient: &Holder,
45 owncoins: &[OwnCoin],
46 token_id: TokenId,
47 block_height: u32,
48 half_split: bool,
49 ) -> Result<(Transaction, (MoneyTransferParamsV1, Option<MoneyFeeParamsV1>), Vec<OwnCoin>)>
50 {
51 let wallet = self.holders.get(holder).unwrap();
52 let rcpt = self.holders.get(recipient).unwrap().keypair.public;
53
54 let (mint_pk, mint_zkbin) = self.proving_keys.get(MONEY_CONTRACT_ZKAS_MINT_NS_V1).unwrap();
55 let (burn_pk, burn_zkbin) = self.proving_keys.get(MONEY_CONTRACT_ZKAS_BURN_NS_V1).unwrap();
56
57 let (params, secrets, mut spent_coins) = make_transfer_call(
59 wallet.keypair,
60 rcpt,
61 amount,
62 token_id,
63 owncoins.to_owned(),
64 wallet.money_merkle_tree.clone(),
65 None,
66 None,
67 mint_zkbin.clone(),
68 mint_pk.clone(),
69 burn_zkbin.clone(),
70 burn_pk.clone(),
71 half_split,
72 )?;
73
74 let mut data = vec![MoneyFunction::TransferV1 as u8];
76 params.encode_async(&mut data).await?;
77 let call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
78
79 let mut tx_builder =
81 TransactionBuilder::new(ContractCallLeaf { call, proofs: secrets.proofs }, vec![])?;
82
83 let mut fee_params = None;
91 let mut fee_signature_secrets = None;
92 if self.verify_fees {
93 let mut tx = tx_builder.build()?;
94 let sigs = tx.create_sigs(&secrets.signature_secrets)?;
95 tx.signatures = vec![sigs];
96
97 let (fee_call, fee_proofs, fee_secrets, spent_fee_coins, fee_call_params) =
98 self.append_fee_call(holder, tx, block_height, &spent_coins).await?;
99
100 tx_builder.append(ContractCallLeaf { call: fee_call, proofs: fee_proofs }, vec![])?;
102 fee_signature_secrets = Some(fee_secrets);
103 spent_coins.extend_from_slice(&spent_fee_coins);
104 fee_params = Some(fee_call_params);
105 }
106
107 let mut tx = tx_builder.build()?;
109 let sigs = tx.create_sigs(&secrets.signature_secrets)?;
110 tx.signatures = vec![sigs];
111 if let Some(fee_signature_secrets) = fee_signature_secrets {
112 let sigs = tx.create_sigs(&fee_signature_secrets)?;
113 tx.signatures.push(sigs);
114 }
115
116 Ok((tx, (params, fee_params), spent_coins))
117 }
118
119 pub async fn execute_transfer_tx(
123 &mut self,
124 holder: &Holder,
125 tx: Transaction,
126 call_params: &MoneyTransferParamsV1,
127 fee_params: &Option<MoneyFeeParamsV1>,
128 block_height: u32,
129 append: bool,
130 ) -> Result<Vec<OwnCoin>> {
131 let wallet = self.holders.get_mut(holder).unwrap();
132
133 wallet.add_transaction("money::transfer", tx, block_height).await?;
135
136 let nullifiers =
138 call_params.inputs.iter().map(|i| i.nullifier.inner()).map(|l| (l, l)).collect();
139 wallet.money_null_smt.insert_batch(nullifiers).expect("smt.insert_batch()");
140
141 let mut found_owncoins = vec![];
142 if append {
143 for input in &call_params.inputs {
144 if let Some(spent_coin) = wallet
145 .unspent_money_coins
146 .iter()
147 .find(|x| x.nullifier() == input.nullifier)
148 .cloned()
149 {
150 debug!("Found spent OwnCoin({}) for {:?}", spent_coin.coin, holder);
151 wallet.unspent_money_coins.retain(|x| x.nullifier() != input.nullifier);
152 wallet.spent_money_coins.push(spent_coin.clone());
153 }
154 }
155
156 for output in &call_params.outputs {
158 wallet.money_merkle_tree.append(MerkleNode::from(output.coin.inner()));
159
160 let Ok(note) = output.note.decrypt::<MoneyNote>(&wallet.keypair.secret) else {
162 continue
163 };
164
165 let owncoin = OwnCoin {
166 coin: output.coin,
167 note: note.clone(),
168 secret: wallet.keypair.secret,
169 leaf_position: wallet.money_merkle_tree.mark().unwrap(),
170 };
171
172 debug!("Found new OwnCoin({}) for {:?}", owncoin.coin, holder);
173 wallet.unspent_money_coins.push(owncoin.clone());
174 found_owncoins.push(owncoin);
175 }
176 }
177
178 if let Some(ref fee_params) = fee_params {
180 let nullifier = fee_params.input.nullifier.inner();
182 wallet
183 .money_null_smt
184 .insert_batch(vec![(nullifier, nullifier)])
185 .expect("smt.insert_batch()");
186
187 if append {
188 if let Some(spent_coin) = wallet
189 .unspent_money_coins
190 .iter()
191 .find(|x| x.nullifier() == fee_params.input.nullifier)
192 .cloned()
193 {
194 debug!("Found spent OwnCoin({}) for {:?}", spent_coin.coin, holder);
195 wallet
196 .unspent_money_coins
197 .retain(|x| x.nullifier() != fee_params.input.nullifier);
198 wallet.spent_money_coins.push(spent_coin.clone());
199 }
200
201 wallet.money_merkle_tree.append(MerkleNode::from(fee_params.output.coin.inner()));
203
204 if let Ok(note) =
206 fee_params.output.note.decrypt::<MoneyNote>(&wallet.keypair.secret)
207 {
208 let owncoin = OwnCoin {
209 coin: fee_params.output.coin,
210 note: note.clone(),
211 secret: wallet.keypair.secret,
212 leaf_position: wallet.money_merkle_tree.mark().unwrap(),
213 };
214
215 debug!("Found new OwnCoin({}) for {:?}", owncoin.coin, holder);
216 wallet.unspent_money_coins.push(owncoin.clone());
217 found_owncoins.push(owncoin);
218 };
219 }
220 }
221
222 Ok(found_owncoins)
223 }
224}