darkfi_money_contract/client/
swap_v1.rs1use darkfi::{
22 zk::{Proof, ProvingKey},
23 zkas::ZkBinary,
24 ClientFailed, Result,
25};
26use darkfi_sdk::{
27 crypto::{
28 note::AeadEncryptedNote, pasta_prelude::*, BaseBlind, Blind, FuncId, MerkleTree, PublicKey,
29 ScalarBlind, SecretKey,
30 },
31 pasta::pallas,
32};
33use darkfi_serial::serialize;
34use log::{debug, error};
35use rand::rngs::OsRng;
36
37use crate::{
38 client::{
39 transfer_v1::{
40 proof::{create_transfer_burn_proof, create_transfer_mint_proof},
41 TransferCallInput, TransferCallOutput,
42 },
43 MoneyNote, OwnCoin,
44 },
45 model::{Input, MoneyTransferParamsV1, Output, TokenId},
46};
47
48pub struct SwapCallDebris {
49 pub params: MoneyTransferParamsV1,
50 pub proofs: Vec<Proof>,
51 pub signature_secret: SecretKey,
52}
53
54pub struct SwapCallBuilder {
58 pub pubkey: PublicKey,
60 pub value_send: u64,
62 pub token_id_send: TokenId,
64 pub value_recv: u64,
66 pub token_id_recv: TokenId,
68 pub user_data_blind_send: BaseBlind,
70 pub spend_hook_recv: FuncId,
72 pub user_data_recv: pallas::Base,
74 pub value_blinds: [ScalarBlind; 2],
79 pub token_blinds: [BaseBlind; 2],
81 pub coin: OwnCoin,
83 pub tree: MerkleTree,
85 pub mint_zkbin: ZkBinary,
87 pub mint_pk: ProvingKey,
89 pub burn_zkbin: ZkBinary,
91 pub burn_pk: ProvingKey,
93}
94
95impl SwapCallBuilder {
96 pub fn build(&self) -> Result<SwapCallDebris> {
97 debug!(target: "contract::money::client::swap", "Building half of Money::OtcSwapV1 contract call");
98 if self.value_send == 0 {
99 error!(target: "contract::money::client::swap", "Error: Value send is 0");
100 return Err(ClientFailed::InvalidAmount(self.value_send).into())
101 }
102
103 if self.value_recv == 0 {
104 error!(target: "contract::money::client::swap", "Error: Value receive is 0");
105 return Err(ClientFailed::InvalidAmount(self.value_recv).into())
106 }
107
108 if self.token_id_send.inner() == pallas::Base::ZERO {
109 error!(target: "contract::money::client::swap", "Error: Token send is ZERO");
110 return Err(ClientFailed::InvalidTokenId(self.token_id_send.to_string()).into())
111 }
112
113 if self.token_id_recv.inner() == pallas::Base::ZERO {
114 error!(target: "contract::money::client::swap", "Error: Token receive is ZERO");
115 return Err(ClientFailed::InvalidTokenId(self.token_id_recv.to_string()).into())
116 }
117
118 if self.coin.note.value != self.value_send {
119 return Err(ClientFailed::InvalidAmount(self.coin.note.value).into())
120 }
121
122 if self.coin.note.token_id != self.token_id_send {
123 return Err(ClientFailed::InvalidTokenId(self.coin.note.token_id.to_string()).into())
124 }
125
126 let input = TransferCallInput {
127 coin: self.coin.clone(),
128 merkle_path: self.tree.witness(self.coin.leaf_position, 0).unwrap(),
129 user_data_blind: self.user_data_blind_send,
130 };
131
132 let output = TransferCallOutput {
133 public_key: self.pubkey,
134 value: self.value_recv,
135 token_id: self.token_id_recv,
136 spend_hook: FuncId::none(),
137 user_data: pallas::Base::ZERO,
138 blind: Blind::random(&mut OsRng),
139 };
140
141 let mut params = MoneyTransferParamsV1 { inputs: vec![], outputs: vec![] };
143
144 let signature_secret = SecretKey::random(&mut OsRng);
146
147 let mut proofs = vec![];
148 debug!(target: "contract::money::client::swap", "Creating burn proof for input");
149 let (proof, public_inputs) = create_transfer_burn_proof(
150 &self.burn_zkbin,
151 &self.burn_pk,
152 &input,
153 self.value_blinds[0],
154 self.token_blinds[0],
155 signature_secret,
156 )?;
157
158 params.inputs.push(Input {
159 value_commit: public_inputs.value_commit,
160 token_commit: public_inputs.token_commit,
161 nullifier: public_inputs.nullifier,
162 merkle_root: public_inputs.merkle_root,
163 user_data_enc: public_inputs.user_data_enc,
164 signature_public: public_inputs.signature_public,
165 });
166
167 proofs.push(proof);
168
169 let coin_blind = Blind::random(&mut OsRng);
171
172 debug!(target: "contract::money::client::swap", "Creating mint proof for output");
173 let (proof, public_inputs) = create_transfer_mint_proof(
174 &self.mint_zkbin,
175 &self.mint_pk,
176 &output,
177 self.value_blinds[1],
178 self.token_blinds[1],
179 self.spend_hook_recv,
180 self.user_data_recv,
181 coin_blind,
182 )?;
183
184 proofs.push(proof);
185
186 let note = MoneyNote {
188 value: output.value,
189 token_id: output.token_id,
190 spend_hook: self.spend_hook_recv,
191 user_data: self.user_data_recv,
192 coin_blind,
193 value_blind: self.value_blinds[1],
194 token_blind: self.token_blinds[1],
195 memo: serialize(&signature_secret),
197 };
198
199 let encrypted_note = AeadEncryptedNote::encrypt(¬e, &self.pubkey, &mut OsRng)?;
200
201 params.outputs.push(Output {
202 value_commit: public_inputs.value_commit,
203 token_commit: public_inputs.token_commit,
204 coin: public_inputs.coin,
205 note: encrypted_note,
206 });
207
208 let debris = SwapCallDebris { params, proofs, signature_secret };
211 Ok(debris)
212 }
213}