1use rand::rngs::OsRng;
20use rusqlite::types::Value;
21
22use darkfi::{
23 tx::{ContractCallLeaf, Transaction, TransactionBuilder},
24 util::parse::decode_base10,
25 zk::{halo2::Field, proof::ProvingKey, vm::ZkCircuit, vm_heap::empty_witnesses},
26 zkas::ZkBinary,
27 Error, Result,
28};
29use darkfi_money_contract::{
30 client::{
31 auth_token_freeze_v1::AuthTokenFreezeCallBuilder,
32 auth_token_mint_v1::AuthTokenMintCallBuilder, token_mint_v1::TokenMintCallBuilder,
33 },
34 model::{CoinAttributes, TokenAttributes, TokenId},
35 MoneyFunction, MONEY_CONTRACT_ZKAS_AUTH_TOKEN_MINT_NS_V1, MONEY_CONTRACT_ZKAS_FEE_NS_V1,
36 MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1,
37};
38use darkfi_sdk::{
39 crypto::{
40 contract_id::MONEY_CONTRACT_ID, poseidon_hash, BaseBlind, Blind, FuncId, FuncRef, Keypair,
41 PublicKey, SecretKey,
42 },
43 dark_tree::DarkTree,
44 pasta::pallas,
45 tx::ContractCall,
46};
47use darkfi_serial::{deserialize_async, serialize_async, AsyncEncodable};
48
49use crate::{
50 convert_named_params,
51 error::WalletDbResult,
52 money::{
53 BALANCE_BASE10_DECIMALS, MONEY_TOKENS_COL_IS_FROZEN, MONEY_TOKENS_COL_MINT_AUTHORITY,
54 MONEY_TOKENS_COL_TOKEN_BLIND, MONEY_TOKENS_COL_TOKEN_ID, MONEY_TOKENS_TABLE,
55 },
56 Drk,
57};
58
59impl Drk {
60 fn derive_token_attributes(
62 &self,
63 mint_authority: SecretKey,
64 token_blind: BaseBlind,
65 ) -> TokenAttributes {
66 let auth_func_id = FuncRef {
68 contract_id: *MONEY_CONTRACT_ID,
69 func_code: MoneyFunction::AuthTokenMintV1 as u8,
70 }
71 .to_func_id();
72
73 let (mint_auth_x, mint_auth_y) = PublicKey::from_secret(mint_authority).xy();
75
76 TokenAttributes {
78 auth_parent: auth_func_id,
79 user_data: poseidon_hash([mint_auth_x, mint_auth_y]),
80 blind: token_blind,
81 }
82 }
83
84 pub async fn import_mint_authority(
86 &self,
87 mint_authority: SecretKey,
88 token_blind: BaseBlind,
89 ) -> Result<TokenId> {
90 let token_id = self.derive_token_attributes(mint_authority, token_blind).to_token_id();
91 let is_frozen = 0;
92
93 let query = format!(
94 "INSERT INTO {} ({}, {}, {}, {}) VALUES (?1, ?2, ?3, ?4);",
95 *MONEY_TOKENS_TABLE,
96 MONEY_TOKENS_COL_TOKEN_ID,
97 MONEY_TOKENS_COL_MINT_AUTHORITY,
98 MONEY_TOKENS_COL_TOKEN_BLIND,
99 MONEY_TOKENS_COL_IS_FROZEN,
100 );
101
102 if let Err(e) = self.wallet.exec_sql(
103 &query,
104 rusqlite::params![
105 serialize_async(&token_id).await,
106 serialize_async(&mint_authority).await,
107 serialize_async(&token_blind).await,
108 is_frozen,
109 ],
110 ) {
111 return Err(Error::DatabaseError(format!(
112 "[import_mint_authority] Inserting mint authority failed: {e:?}"
113 )))
114 };
115
116 Ok(token_id)
117 }
118
119 async fn parse_mint_authority_record(
122 &self,
123 row: &[Value],
124 ) -> Result<(TokenId, SecretKey, BaseBlind, bool)> {
125 let Value::Blob(ref token_bytes) = row[0] else {
126 return Err(Error::ParseFailed(
127 "[parse_mint_authority_record] Token ID bytes parsing failed",
128 ))
129 };
130 let token_id = deserialize_async(token_bytes).await?;
131
132 let Value::Blob(ref auth_bytes) = row[1] else {
133 return Err(Error::ParseFailed(
134 "[parse_mint_authority_record] Mint authority bytes parsing failed",
135 ))
136 };
137 let mint_authority = deserialize_async(auth_bytes).await?;
138
139 let Value::Blob(ref token_blind_bytes) = row[2] else {
140 return Err(Error::ParseFailed(
141 "[parse_mint_authority_record] Token blind bytes parsing failed",
142 ))
143 };
144 let token_blind: BaseBlind = deserialize_async(token_blind_bytes).await?;
145
146 let Value::Integer(frozen) = row[3] else {
147 return Err(Error::ParseFailed("[parse_mint_authority_record] Is frozen parsing failed"))
148 };
149 let Ok(frozen) = i32::try_from(frozen) else {
150 return Err(Error::ParseFailed("[parse_mint_authority_record] Is frozen parsing failed"))
151 };
152
153 Ok((token_id, mint_authority, token_blind, frozen != 0))
154 }
155
156 pub fn reset_mint_authorities(&self) -> WalletDbResult<()> {
158 println!("Resetting mint authorities frozen status");
159 let query =
160 format!("UPDATE {} SET {} = 0", *MONEY_TOKENS_TABLE, MONEY_TOKENS_COL_IS_FROZEN,);
161 self.wallet.exec_sql(&query, &[])?;
162 println!("Successfully mint authorities frozen status");
163
164 Ok(())
165 }
166
167 pub async fn get_mint_authorities(&self) -> Result<Vec<(TokenId, SecretKey, BaseBlind, bool)>> {
169 let rows = match self.wallet.query_multiple(&MONEY_TOKENS_TABLE, &[], &[]) {
170 Ok(r) => r,
171 Err(e) => {
172 return Err(Error::DatabaseError(format!(
173 "[get_mint_authorities] Tokens mint autorities retrieval failed: {e:?}"
174 )))
175 }
176 };
177
178 let mut ret = Vec::with_capacity(rows.len());
179 for row in rows {
180 ret.push(self.parse_mint_authority_record(&row).await?);
181 }
182
183 Ok(ret)
184 }
185
186 async fn get_token_mint_authority(
188 &self,
189 token_id: &TokenId,
190 ) -> Result<(TokenId, SecretKey, BaseBlind, bool)> {
191 let row = match self.wallet.query_single(
192 &MONEY_TOKENS_TABLE,
193 &[],
194 convert_named_params! {(MONEY_TOKENS_COL_TOKEN_ID, serialize_async(token_id).await)},
195 ) {
196 Ok(r) => r,
197 Err(e) => {
198 return Err(Error::DatabaseError(format!(
199 "[get_token_mint_authority] Token mint autority retrieval failed: {e:?}"
200 )))
201 }
202 };
203
204 let token = self.parse_mint_authority_record(&row).await?;
205
206 if token.3 {
207 return Err(Error::Custom(
208 "This token mint is marked as frozen in the wallet".to_string(),
209 ))
210 }
211
212 Ok(token)
213 }
214
215 pub async fn mint_token(
217 &self,
218 amount: &str,
219 recipient: PublicKey,
220 token_id: TokenId,
221 spend_hook: Option<FuncId>,
222 user_data: Option<pallas::Base>,
223 ) -> Result<Transaction> {
224 let amount = decode_base10(amount, BALANCE_BASE10_DECIMALS, false)?;
226
227 let token_mint_authority = self.get_token_mint_authority(&token_id).await?;
229 let token_attrs =
230 self.derive_token_attributes(token_mint_authority.1, token_mint_authority.2);
231 let mint_authority = Keypair::new(token_mint_authority.1);
232
233 assert_eq!(token_id, token_attrs.to_token_id());
235
236 let zkas_bins = self.lookup_zkas(&MONEY_CONTRACT_ID).await?;
240
241 let Some(mint_zkbin) =
242 zkas_bins.iter().find(|x| x.0 == MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1)
243 else {
244 return Err(Error::Custom("Token mint circuit not found".to_string()))
245 };
246
247 let Some(auth_mint_zkbin) =
248 zkas_bins.iter().find(|x| x.0 == MONEY_CONTRACT_ZKAS_AUTH_TOKEN_MINT_NS_V1)
249 else {
250 return Err(Error::Custom("Auth token mint circuit not found".to_string()))
251 };
252
253 let Some(fee_zkbin) = zkas_bins.iter().find(|x| x.0 == MONEY_CONTRACT_ZKAS_FEE_NS_V1)
254 else {
255 return Err(Error::Custom("Fee circuit not found".to_string()))
256 };
257
258 let mint_zkbin = ZkBinary::decode(&mint_zkbin.1)?;
259 let auth_mint_zkbin = ZkBinary::decode(&auth_mint_zkbin.1)?;
260 let fee_zkbin = ZkBinary::decode(&fee_zkbin.1)?;
261
262 let mint_circuit = ZkCircuit::new(empty_witnesses(&mint_zkbin)?, &mint_zkbin);
263 let auth_mint_circuit =
264 ZkCircuit::new(empty_witnesses(&auth_mint_zkbin)?, &auth_mint_zkbin);
265 let fee_circuit = ZkCircuit::new(empty_witnesses(&fee_zkbin)?, &fee_zkbin);
266
267 let mint_pk = ProvingKey::build(mint_zkbin.k, &mint_circuit);
269 let auth_mint_pk = ProvingKey::build(auth_mint_zkbin.k, &auth_mint_circuit);
270 let fee_pk = ProvingKey::build(fee_zkbin.k, &fee_circuit);
271
272 let coin_attrs = CoinAttributes {
274 public_key: recipient,
275 value: amount,
276 token_id,
277 spend_hook: spend_hook.unwrap_or(FuncId::none()),
278 user_data: user_data.unwrap_or(pallas::Base::ZERO),
279 blind: Blind::random(&mut OsRng),
280 };
281
282 let builder = AuthTokenMintCallBuilder {
284 coin_attrs: coin_attrs.clone(),
285 token_attrs: token_attrs.clone(),
286 mint_keypair: mint_authority,
287 auth_mint_zkbin,
288 auth_mint_pk,
289 };
290 let auth_debris = builder.build()?;
291 let mut data = vec![MoneyFunction::AuthTokenMintV1 as u8];
292 auth_debris.params.encode_async(&mut data).await?;
293 let auth_call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
294
295 let builder = TokenMintCallBuilder { coin_attrs, token_attrs, mint_zkbin, mint_pk };
297 let mint_debris = builder.build()?;
298 let mut data = vec![MoneyFunction::TokenMintV1 as u8];
299 mint_debris.params.encode_async(&mut data).await?;
300 let mint_call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
301
302 let mut tx_builder = TransactionBuilder::new(
304 ContractCallLeaf { call: mint_call, proofs: mint_debris.proofs },
305 vec![DarkTree::new(
306 ContractCallLeaf { call: auth_call, proofs: auth_debris.proofs },
307 vec![],
308 None,
309 None,
310 )],
311 )?;
312
313 let mut tx = tx_builder.build()?;
316 let auth_sigs = tx.create_sigs(&[mint_authority.secret])?;
317 let mint_sigs = tx.create_sigs(&[])?;
318 tx.signatures = vec![auth_sigs, mint_sigs];
319
320 let tree = self.get_money_tree().await?;
321 let (fee_call, fee_proofs, fee_secrets) =
322 self.append_fee_call(&tx, &tree, &fee_pk, &fee_zkbin, None).await?;
323
324 tx_builder.append(ContractCallLeaf { call: fee_call, proofs: fee_proofs }, vec![])?;
326
327 let mut tx = tx_builder.build()?;
329 let sigs = tx.create_sigs(&[mint_authority.secret])?;
330 tx.signatures.push(sigs);
331 let sigs = tx.create_sigs(&[])?;
332 tx.signatures.push(sigs);
333 let sigs = tx.create_sigs(&fee_secrets)?;
334 tx.signatures.push(sigs);
335
336 Ok(tx)
337 }
338
339 pub async fn freeze_token(&self, token_id: TokenId) -> Result<Transaction> {
341 let token_mint_authority = self.get_token_mint_authority(&token_id).await?;
343 let token_attrs =
344 self.derive_token_attributes(token_mint_authority.1, token_mint_authority.2);
345 let mint_authority = Keypair::new(token_mint_authority.1);
346
347 assert_eq!(token_id, token_attrs.to_token_id());
349
350 let zkas_bins = self.lookup_zkas(&MONEY_CONTRACT_ID).await?;
354
355 let Some(auth_mint_zkbin) =
356 zkas_bins.iter().find(|x| x.0 == MONEY_CONTRACT_ZKAS_AUTH_TOKEN_MINT_NS_V1)
357 else {
358 return Err(Error::Custom("Auth token mint circuit not found".to_string()))
359 };
360
361 let Some(fee_zkbin) = zkas_bins.iter().find(|x| x.0 == MONEY_CONTRACT_ZKAS_FEE_NS_V1)
362 else {
363 return Err(Error::Custom("Fee circuit not found".to_string()))
364 };
365
366 let auth_mint_zkbin = ZkBinary::decode(&auth_mint_zkbin.1)?;
367 let fee_zkbin = ZkBinary::decode(&fee_zkbin.1)?;
368
369 let auth_mint_circuit =
370 ZkCircuit::new(empty_witnesses(&auth_mint_zkbin)?, &auth_mint_zkbin);
371 let fee_circuit = ZkCircuit::new(empty_witnesses(&fee_zkbin)?, &fee_zkbin);
372
373 let auth_mint_pk = ProvingKey::build(auth_mint_zkbin.k, &auth_mint_circuit);
375 let fee_pk = ProvingKey::build(fee_zkbin.k, &fee_circuit);
376
377 let builder = AuthTokenFreezeCallBuilder {
379 mint_keypair: mint_authority,
380 token_attrs,
381 auth_mint_zkbin,
382 auth_mint_pk,
383 };
384 let freeze_debris = builder.build()?;
385 let mut data = vec![MoneyFunction::AuthTokenFreezeV1 as u8];
386 freeze_debris.params.encode_async(&mut data).await?;
387 let freeze_call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
388
389 let mut tx_builder = TransactionBuilder::new(
391 ContractCallLeaf { call: freeze_call, proofs: freeze_debris.proofs },
392 vec![],
393 )?;
394
395 let mut tx = tx_builder.build()?;
398 let sigs = tx.create_sigs(&[mint_authority.secret])?;
399 tx.signatures.push(sigs);
400
401 let tree = self.get_money_tree().await?;
402 let (fee_call, fee_proofs, fee_secrets) =
403 self.append_fee_call(&tx, &tree, &fee_pk, &fee_zkbin, None).await?;
404
405 tx_builder.append(ContractCallLeaf { call: fee_call, proofs: fee_proofs }, vec![])?;
407
408 let mut tx = tx_builder.build()?;
410 let sigs = tx.create_sigs(&[mint_authority.secret])?;
411 tx.signatures.push(sigs);
412 let sigs = tx.create_sigs(&fee_secrets)?;
413 tx.signatures.push(sigs);
414
415 Ok(tx)
416 }
417}