1use std::{collections::HashMap, str::FromStr};
20
21use lazy_static::lazy_static;
22use num_bigint::BigUint;
23use rand::rngs::OsRng;
24use rusqlite::types::Value;
25
26use darkfi::{
27 tx::Transaction,
28 validator::fees::compute_fee,
29 zk::{halo2::Field, proof::ProvingKey, vm::ZkCircuit, vm_heap::empty_witnesses, Proof},
30 zkas::ZkBinary,
31 Error, Result,
32};
33use darkfi_money_contract::{
34 client::{
35 compute_remainder_blind,
36 fee_v1::{create_fee_proof, FeeCallInput, FeeCallOutput, FEE_CALL_GAS},
37 MoneyNote, OwnCoin,
38 },
39 model::{
40 Coin, Input, MoneyAuthTokenFreezeParamsV1, MoneyAuthTokenMintParamsV1, MoneyFeeParamsV1,
41 MoneyGenesisMintParamsV1, MoneyPoWRewardParamsV1, MoneyTokenMintParamsV1,
42 MoneyTransferParamsV1, Nullifier, Output, TokenId, DARK_TOKEN_ID,
43 },
44 MoneyFunction, MONEY_CONTRACT_ZKAS_FEE_NS_V1,
45};
46use darkfi_sdk::{
47 bridgetree,
48 crypto::{
49 note::AeadEncryptedNote,
50 pasta_prelude::PrimeField,
51 smt::{PoseidonFp, EMPTY_NODES_FP},
52 BaseBlind, FuncId, Keypair, MerkleNode, MerkleTree, PublicKey, ScalarBlind, SecretKey,
53 MONEY_CONTRACT_ID,
54 },
55 dark_tree::DarkLeaf,
56 pasta::pallas,
57 ContractCall,
58};
59use darkfi_serial::{deserialize_async, serialize_async, AsyncEncodable};
60
61use crate::{
62 cli_util::kaching,
63 convert_named_params,
64 error::WalletDbResult,
65 walletdb::{WalletSmt, WalletStorage},
66 Drk,
67};
68
69lazy_static! {
72 pub static ref MONEY_TREE_TABLE: String =
73 format!("{}_money_tree", MONEY_CONTRACT_ID.to_string());
74 pub static ref MONEY_SMT_TABLE: String = format!("{}_money_smt", MONEY_CONTRACT_ID.to_string());
75 pub static ref MONEY_KEYS_TABLE: String =
76 format!("{}_money_keys", MONEY_CONTRACT_ID.to_string());
77 pub static ref MONEY_COINS_TABLE: String =
78 format!("{}_money_coins", MONEY_CONTRACT_ID.to_string());
79 pub static ref MONEY_TOKENS_TABLE: String =
80 format!("{}_money_tokens", MONEY_CONTRACT_ID.to_string());
81 pub static ref MONEY_ALIASES_TABLE: String =
82 format!("{}_money_aliases", MONEY_CONTRACT_ID.to_string());
83}
84
85pub const MONEY_TREE_COL_TREE: &str = "tree";
87
88pub const MONEY_SMT_COL_KEY: &str = "smt_key";
90pub const MONEY_SMT_COL_VALUE: &str = "smt_value";
91
92pub const MONEY_KEYS_COL_KEY_ID: &str = "key_id";
94pub const MONEY_KEYS_COL_IS_DEFAULT: &str = "is_default";
95pub const MONEY_KEYS_COL_PUBLIC: &str = "public";
96pub const MONEY_KEYS_COL_SECRET: &str = "secret";
97
98pub const MONEY_COINS_COL_COIN: &str = "coin";
100pub const MONEY_COINS_COL_IS_SPENT: &str = "is_spent";
101pub const MONEY_COINS_COL_VALUE: &str = "value";
102pub const MONEY_COINS_COL_TOKEN_ID: &str = "token_id";
103pub const MONEY_COINS_COL_SPEND_HOOK: &str = "spend_hook";
104pub const MONEY_COINS_COL_USER_DATA: &str = "user_data";
105pub const MONEY_COINS_COL_COIN_BLIND: &str = "coin_blind";
106pub const MONEY_COINS_COL_VALUE_BLIND: &str = "value_blind";
107pub const MONEY_COINS_COL_TOKEN_BLIND: &str = "token_blind";
108pub const MONEY_COINS_COL_SECRET: &str = "secret";
109pub const MONEY_COINS_COL_LEAF_POSITION: &str = "leaf_position";
110pub const MONEY_COINS_COL_MEMO: &str = "memo";
111pub const MONEY_COINS_COL_SPENT_TX_HASH: &str = "spent_tx_hash";
112
113pub const MONEY_TOKENS_COL_TOKEN_ID: &str = "token_id";
115pub const MONEY_TOKENS_COL_MINT_AUTHORITY: &str = "mint_authority";
116pub const MONEY_TOKENS_COL_TOKEN_BLIND: &str = "token_blind";
117pub const MONEY_TOKENS_COL_IS_FROZEN: &str = "is_frozen";
118
119pub const MONEY_ALIASES_COL_ALIAS: &str = "alias";
121pub const MONEY_ALIASES_COL_TOKEN_ID: &str = "token_id";
122
123pub const BALANCE_BASE10_DECIMALS: usize = 8;
124
125impl Drk {
126 pub async fn initialize_money(&self) -> WalletDbResult<()> {
128 let wallet_schema = include_str!("../money.sql");
130 self.wallet.exec_batch_sql(wallet_schema)?;
131
132 if self.get_money_tree().await.is_err() {
138 println!("Initializing Money Merkle tree");
139 let mut tree = MerkleTree::new(1);
140 tree.append(MerkleNode::from(pallas::Base::ZERO));
141 let _ = tree.mark().unwrap();
142 let query =
143 format!("INSERT INTO {} ({}) VALUES (?1);", *MONEY_TREE_TABLE, MONEY_TREE_COL_TREE);
144 self.wallet.exec_sql(&query, rusqlite::params![serialize_async(&tree).await])?;
145 println!("Successfully initialized Merkle tree for the Money contract");
146 }
147
148 self.add_alias("DRK".to_string(), *DARK_TOKEN_ID).await?;
150
151 Ok(())
152 }
153
154 pub async fn money_keygen(&self) -> WalletDbResult<()> {
156 println!("Generating a new keypair");
157
158 let keypair = Keypair::random(&mut OsRng);
160 let is_default = 0;
161
162 let query = format!(
163 "INSERT INTO {} ({}, {}, {}) VALUES (?1, ?2, ?3);",
164 *MONEY_KEYS_TABLE,
165 MONEY_KEYS_COL_IS_DEFAULT,
166 MONEY_KEYS_COL_PUBLIC,
167 MONEY_KEYS_COL_SECRET
168 );
169 self.wallet.exec_sql(
170 &query,
171 rusqlite::params![
172 is_default,
173 serialize_async(&keypair.public).await,
174 serialize_async(&keypair.secret).await
175 ],
176 )?;
177
178 println!("New address:");
179 println!("{}", keypair.public);
180
181 Ok(())
182 }
183
184 pub async fn default_secret(&self) -> Result<SecretKey> {
186 let row = match self.wallet.query_single(
187 &MONEY_KEYS_TABLE,
188 &[MONEY_KEYS_COL_SECRET],
189 convert_named_params! {(MONEY_KEYS_COL_IS_DEFAULT, 1)},
190 ) {
191 Ok(r) => r,
192 Err(e) => {
193 return Err(Error::DatabaseError(format!(
194 "[default_secret] Default secret key retrieval failed: {e:?}"
195 )))
196 }
197 };
198
199 let Value::Blob(ref key_bytes) = row[0] else {
200 return Err(Error::ParseFailed("[default_secret] Key bytes parsing failed"))
201 };
202 let secret_key: SecretKey = deserialize_async(key_bytes).await?;
203
204 Ok(secret_key)
205 }
206
207 pub async fn default_address(&self) -> Result<PublicKey> {
209 let row = match self.wallet.query_single(
210 &MONEY_KEYS_TABLE,
211 &[MONEY_KEYS_COL_PUBLIC],
212 convert_named_params! {(MONEY_KEYS_COL_IS_DEFAULT, 1)},
213 ) {
214 Ok(r) => r,
215 Err(e) => {
216 return Err(Error::DatabaseError(format!(
217 "[default_address] Default address retrieval failed: {e:?}"
218 )))
219 }
220 };
221
222 let Value::Blob(ref key_bytes) = row[0] else {
223 return Err(Error::ParseFailed("[default_address] Key bytes parsing failed"))
224 };
225 let public_key: PublicKey = deserialize_async(key_bytes).await?;
226
227 Ok(public_key)
228 }
229
230 pub fn set_default_address(&self, idx: usize) -> WalletDbResult<()> {
232 let is_default = 0;
234 let query = format!("UPDATE {} SET {} = ?1", *MONEY_KEYS_TABLE, MONEY_KEYS_COL_IS_DEFAULT,);
235 self.wallet.exec_sql(&query, rusqlite::params![is_default])?;
236
237 let is_default = 1;
239 let query = format!(
240 "UPDATE {} SET {} = ?1 WHERE {} = ?2",
241 *MONEY_KEYS_TABLE, MONEY_KEYS_COL_IS_DEFAULT, MONEY_KEYS_COL_KEY_ID,
242 );
243 self.wallet.exec_sql(&query, rusqlite::params![is_default, idx])
244 }
245
246 pub async fn addresses(&self) -> Result<Vec<(u64, PublicKey, SecretKey, u64)>> {
248 let rows = match self.wallet.query_multiple(&MONEY_KEYS_TABLE, &[], &[]) {
249 Ok(r) => r,
250 Err(e) => {
251 return Err(Error::DatabaseError(format!(
252 "[addresses] Addresses retrieval failed: {e:?}"
253 )))
254 }
255 };
256
257 let mut vec = Vec::with_capacity(rows.len());
258 for row in rows {
259 let Value::Integer(key_id) = row[0] else {
260 return Err(Error::ParseFailed("[addresses] Key ID parsing failed"))
261 };
262 let Ok(key_id) = u64::try_from(key_id) else {
263 return Err(Error::ParseFailed("[addresses] Key ID parsing failed"))
264 };
265
266 let Value::Integer(is_default) = row[1] else {
267 return Err(Error::ParseFailed("[addresses] Is default parsing failed"))
268 };
269 let Ok(is_default) = u64::try_from(is_default) else {
270 return Err(Error::ParseFailed("[addresses] Is default parsing failed"))
271 };
272
273 let Value::Blob(ref key_bytes) = row[2] else {
274 return Err(Error::ParseFailed("[addresses] Public key bytes parsing failed"))
275 };
276 let public_key: PublicKey = deserialize_async(key_bytes).await?;
277
278 let Value::Blob(ref key_bytes) = row[3] else {
279 return Err(Error::ParseFailed("[addresses] Secret key bytes parsing failed"))
280 };
281 let secret_key: SecretKey = deserialize_async(key_bytes).await?;
282
283 vec.push((key_id, public_key, secret_key, is_default));
284 }
285
286 Ok(vec)
287 }
288
289 pub async fn get_money_secrets(&self) -> Result<Vec<SecretKey>> {
291 let rows =
292 match self.wallet.query_multiple(&MONEY_KEYS_TABLE, &[MONEY_KEYS_COL_SECRET], &[]) {
293 Ok(r) => r,
294 Err(e) => {
295 return Err(Error::DatabaseError(format!(
296 "[get_money_secrets] Secret keys retrieval failed: {e:?}"
297 )))
298 }
299 };
300
301 let mut secrets = Vec::with_capacity(rows.len());
302
303 for row in rows {
305 let Value::Blob(ref key_bytes) = row[0] else {
306 return Err(Error::ParseFailed(
307 "[get_money_secrets] Secret key bytes parsing failed",
308 ))
309 };
310 let secret_key: SecretKey = deserialize_async(key_bytes).await?;
311 secrets.push(secret_key);
312 }
313
314 Ok(secrets)
315 }
316
317 pub async fn import_money_secrets(&self, secrets: Vec<SecretKey>) -> Result<Vec<PublicKey>> {
321 let existing_secrets = self.get_money_secrets().await?;
322
323 let mut ret = Vec::with_capacity(secrets.len());
324
325 for secret in secrets {
326 if existing_secrets.contains(&secret) {
328 println!("Existing key found: {secret}");
329 continue
330 }
331
332 ret.push(PublicKey::from_secret(secret));
333 let is_default = 0;
334 let public = serialize_async(&PublicKey::from_secret(secret)).await;
335 let secret = serialize_async(&secret).await;
336
337 let query = format!(
338 "INSERT INTO {} ({}, {}, {}) VALUES (?1, ?2, ?3);",
339 *MONEY_KEYS_TABLE,
340 MONEY_KEYS_COL_IS_DEFAULT,
341 MONEY_KEYS_COL_PUBLIC,
342 MONEY_KEYS_COL_SECRET
343 );
344 if let Err(e) =
345 self.wallet.exec_sql(&query, rusqlite::params![is_default, public, secret])
346 {
347 return Err(Error::DatabaseError(format!(
348 "[import_money_secrets] Inserting new address failed: {e:?}"
349 )))
350 }
351 }
352
353 Ok(ret)
354 }
355
356 pub async fn money_balance(&self) -> Result<HashMap<String, u64>> {
358 let mut coins = self.get_coins(false).await?;
359 coins.retain(|x| x.0.note.spend_hook == FuncId::none());
360
361 let mut balmap: HashMap<String, u64> = HashMap::new();
363
364 for coin in coins {
365 let mut value = coin.0.note.value;
366
367 if let Some(prev) = balmap.get(&coin.0.note.token_id.to_string()) {
368 value += prev;
369 }
370
371 balmap.insert(coin.0.note.token_id.to_string(), value);
372 }
373
374 Ok(balmap)
375 }
376
377 pub async fn get_coins(&self, fetch_spent: bool) -> Result<Vec<(OwnCoin, bool, String)>> {
381 let query = if fetch_spent {
382 self.wallet.query_multiple(&MONEY_COINS_TABLE, &[], &[])
383 } else {
384 self.wallet.query_multiple(
385 &MONEY_COINS_TABLE,
386 &[],
387 convert_named_params! {(MONEY_COINS_COL_IS_SPENT, false)},
388 )
389 };
390
391 let rows = match query {
392 Ok(r) => r,
393 Err(e) => {
394 return Err(Error::DatabaseError(format!(
395 "[get_coins] Coins retrieval failed: {e:?}"
396 )))
397 }
398 };
399
400 let mut owncoins = Vec::with_capacity(rows.len());
401 for row in rows {
402 owncoins.push(self.parse_coin_record(&row).await?)
403 }
404
405 Ok(owncoins)
406 }
407
408 pub async fn get_transaction_coins(&self, spent_tx_hash: &String) -> Result<Vec<OwnCoin>> {
410 let query = self.wallet.query_multiple(
411 &MONEY_COINS_TABLE,
412 &[],
413 convert_named_params! {(MONEY_COINS_COL_SPENT_TX_HASH, spent_tx_hash)},
414 );
415
416 let rows = match query {
417 Ok(r) => r,
418 Err(e) => {
419 return Err(Error::DatabaseError(format!(
420 "[get_transaction_coins] Coins retrieval failed: {e:?}"
421 )))
422 }
423 };
424
425 let mut owncoins = Vec::with_capacity(rows.len());
426 for row in rows {
427 owncoins.push(self.parse_coin_record(&row).await?.0)
428 }
429
430 Ok(owncoins)
431 }
432
433 pub async fn get_token_coins(&self, token_id: &TokenId) -> Result<Vec<OwnCoin>> {
435 let query = self.wallet.query_multiple(
436 &MONEY_COINS_TABLE,
437 &[],
438 convert_named_params! {(MONEY_COINS_COL_IS_SPENT, false), (MONEY_COINS_COL_TOKEN_ID, serialize_async(token_id).await), (MONEY_COINS_COL_SPEND_HOOK, serialize_async(&FuncId::none()).await)},
439 );
440
441 let rows = match query {
442 Ok(r) => r,
443 Err(e) => {
444 return Err(Error::DatabaseError(format!(
445 "[get_token_coins] Coins retrieval failed: {e:?}"
446 )))
447 }
448 };
449
450 let mut owncoins = Vec::with_capacity(rows.len());
451 for row in rows {
452 owncoins.push(self.parse_coin_record(&row).await?.0)
453 }
454
455 Ok(owncoins)
456 }
457
458 pub async fn get_contract_token_coins(
460 &self,
461 token_id: &TokenId,
462 spend_hook: &FuncId,
463 user_data: &pallas::Base,
464 ) -> Result<Vec<OwnCoin>> {
465 let query = self.wallet.query_multiple(
466 &MONEY_COINS_TABLE,
467 &[],
468 convert_named_params! {(MONEY_COINS_COL_IS_SPENT, false), (MONEY_COINS_COL_TOKEN_ID, serialize_async(token_id).await), (MONEY_COINS_COL_SPEND_HOOK, serialize_async(spend_hook).await), (MONEY_COINS_COL_USER_DATA, serialize_async(user_data).await)},
469 );
470
471 let rows = match query {
472 Ok(r) => r,
473 Err(e) => {
474 return Err(Error::DatabaseError(format!(
475 "[get_contract_token_coins] Coins retrieval failed: {e:?}"
476 )))
477 }
478 };
479
480 let mut owncoins = Vec::with_capacity(rows.len());
481 for row in rows {
482 owncoins.push(self.parse_coin_record(&row).await?.0)
483 }
484
485 Ok(owncoins)
486 }
487
488 async fn parse_coin_record(&self, row: &[Value]) -> Result<(OwnCoin, bool, String)> {
491 let Value::Blob(ref coin_bytes) = row[0] else {
492 return Err(Error::ParseFailed("[parse_coin_record] Coin bytes parsing failed"))
493 };
494 let coin: Coin = deserialize_async(coin_bytes).await?;
495
496 let Value::Integer(is_spent) = row[1] else {
497 return Err(Error::ParseFailed("[parse_coin_record] Is spent parsing failed"))
498 };
499 let Ok(is_spent) = u64::try_from(is_spent) else {
500 return Err(Error::ParseFailed("[parse_coin_record] Is spent parsing failed"))
501 };
502 let is_spent = is_spent > 0;
503
504 let Value::Blob(ref value_bytes) = row[2] else {
505 return Err(Error::ParseFailed("[parse_coin_record] Value bytes parsing failed"))
506 };
507 let value: u64 = deserialize_async(value_bytes).await?;
508
509 let Value::Blob(ref token_id_bytes) = row[3] else {
510 return Err(Error::ParseFailed("[parse_coin_record] Token ID bytes parsing failed"))
511 };
512 let token_id: TokenId = deserialize_async(token_id_bytes).await?;
513
514 let Value::Blob(ref spend_hook_bytes) = row[4] else {
515 return Err(Error::ParseFailed("[parse_coin_record] Spend hook bytes parsing failed"))
516 };
517 let spend_hook: pallas::Base = deserialize_async(spend_hook_bytes).await?;
518
519 let Value::Blob(ref user_data_bytes) = row[5] else {
520 return Err(Error::ParseFailed("[parse_coin_record] User data bytes parsing failed"))
521 };
522 let user_data: pallas::Base = deserialize_async(user_data_bytes).await?;
523
524 let Value::Blob(ref coin_blind_bytes) = row[6] else {
525 return Err(Error::ParseFailed("[parse_coin_record] Coin blind bytes parsing failed"))
526 };
527 let coin_blind: BaseBlind = deserialize_async(coin_blind_bytes).await?;
528
529 let Value::Blob(ref value_blind_bytes) = row[7] else {
530 return Err(Error::ParseFailed("[parse_coin_record] Value blind bytes parsing failed"))
531 };
532 let value_blind: ScalarBlind = deserialize_async(value_blind_bytes).await?;
533
534 let Value::Blob(ref token_blind_bytes) = row[8] else {
535 return Err(Error::ParseFailed("[parse_coin_record] Token blind bytes parsing failed"))
536 };
537 let token_blind: BaseBlind = deserialize_async(token_blind_bytes).await?;
538
539 let Value::Blob(ref secret_bytes) = row[9] else {
540 return Err(Error::ParseFailed("[parse_coin_record] Secret bytes parsing failed"))
541 };
542 let secret: SecretKey = deserialize_async(secret_bytes).await?;
543
544 let Value::Blob(ref leaf_position_bytes) = row[10] else {
545 return Err(Error::ParseFailed("[parse_coin_record] Leaf position bytes parsing failed"))
546 };
547 let leaf_position: bridgetree::Position = deserialize_async(leaf_position_bytes).await?;
548
549 let Value::Blob(ref memo) = row[11] else {
550 return Err(Error::ParseFailed("[parse_coin_record] Memo parsing failed"))
551 };
552
553 let Value::Text(ref spent_tx_hash) = row[12] else {
554 return Err(Error::ParseFailed(
555 "[parse_coin_record] Spent transaction hash parsing failed",
556 ))
557 };
558
559 let note = MoneyNote {
560 value,
561 token_id,
562 spend_hook: spend_hook.into(),
563 user_data,
564 coin_blind,
565 value_blind,
566 token_blind,
567 memo: memo.clone(),
568 };
569
570 Ok((OwnCoin { coin, note, secret, leaf_position }, is_spent, spent_tx_hash.clone()))
571 }
572
573 pub async fn add_alias(&self, alias: String, token_id: TokenId) -> WalletDbResult<()> {
575 println!("Generating alias {alias} for Token: {token_id}");
576 let query = format!(
577 "INSERT OR REPLACE INTO {} ({}, {}) VALUES (?1, ?2);",
578 *MONEY_ALIASES_TABLE, MONEY_ALIASES_COL_ALIAS, MONEY_ALIASES_COL_TOKEN_ID,
579 );
580 self.wallet.exec_sql(
581 &query,
582 rusqlite::params![serialize_async(&alias).await, serialize_async(&token_id).await],
583 )
584 }
585
586 pub async fn get_aliases(
589 &self,
590 alias_filter: Option<String>,
591 token_id_filter: Option<TokenId>,
592 ) -> Result<HashMap<String, TokenId>> {
593 let rows = match self.wallet.query_multiple(&MONEY_ALIASES_TABLE, &[], &[]) {
594 Ok(r) => r,
595 Err(e) => {
596 return Err(Error::DatabaseError(format!(
597 "[get_aliases] Aliases retrieval failed: {e:?}"
598 )))
599 }
600 };
601
602 let mut map: HashMap<String, TokenId> = HashMap::new();
604 for row in rows {
605 let Value::Blob(ref alias_bytes) = row[0] else {
606 return Err(Error::ParseFailed("[get_aliases] Alias bytes parsing failed"))
607 };
608 let alias: String = deserialize_async(alias_bytes).await?;
609 if alias_filter.is_some() && alias_filter.as_ref().unwrap() != &alias {
610 continue
611 }
612
613 let Value::Blob(ref token_id_bytes) = row[1] else {
614 return Err(Error::ParseFailed("[get_aliases] TokenId bytes parsing failed"))
615 };
616 let token_id: TokenId = deserialize_async(token_id_bytes).await?;
617 if token_id_filter.is_some() && token_id_filter.as_ref().unwrap() != &token_id {
618 continue
619 }
620
621 map.insert(alias, token_id);
622 }
623
624 Ok(map)
625 }
626
627 pub async fn get_aliases_mapped_by_token(&self) -> Result<HashMap<String, String>> {
629 let aliases = self.get_aliases(None, None).await?;
630 let mut map: HashMap<String, String> = HashMap::new();
631 for (alias, token_id) in aliases {
632 let aliases_string = if let Some(prev) = map.get(&token_id.to_string()) {
633 format!("{}, {}", prev, alias)
634 } else {
635 alias
636 };
637
638 map.insert(token_id.to_string(), aliases_string);
639 }
640
641 Ok(map)
642 }
643
644 pub async fn remove_alias(&self, alias: String) -> WalletDbResult<()> {
646 println!("Removing alias: {alias}");
647 let query = format!(
648 "DELETE FROM {} WHERE {} = ?1;",
649 *MONEY_ALIASES_TABLE, MONEY_ALIASES_COL_ALIAS,
650 );
651 self.wallet.exec_sql(&query, rusqlite::params![serialize_async(&alias).await])
652 }
653
654 pub async fn unspend_coin(&self, coin: &Coin) -> WalletDbResult<()> {
656 let is_spend = 0;
657 let query = format!(
658 "UPDATE {} SET {} = ?1, {} = ?2 WHERE {} = ?3;",
659 *MONEY_COINS_TABLE,
660 MONEY_COINS_COL_IS_SPENT,
661 MONEY_COINS_COL_SPENT_TX_HASH,
662 MONEY_COINS_COL_COIN
663 );
664 self.wallet.exec_sql(
665 &query,
666 rusqlite::params![is_spend, "-", serialize_async(&coin.inner()).await],
667 )
668 }
669
670 pub async fn put_money_tree(&self, tree: &MerkleTree) -> WalletDbResult<()> {
672 let query = format!("UPDATE {} SET {} = ?1;", *MONEY_TREE_TABLE, MONEY_TREE_COL_TREE);
673 self.wallet.exec_sql(&query, rusqlite::params![serialize_async(tree).await])
674 }
675
676 pub async fn get_money_tree(&self) -> Result<MerkleTree> {
678 let row = match self.wallet.query_single(&MONEY_TREE_TABLE, &[MONEY_TREE_COL_TREE], &[]) {
679 Ok(r) => r,
680 Err(e) => {
681 return Err(Error::DatabaseError(format!(
682 "[get_money_tree] Tree retrieval failed: {e:?}"
683 )))
684 }
685 };
686
687 let Value::Blob(ref tree_bytes) = row[0] else {
688 return Err(Error::ParseFailed("[get_money_tree] Tree bytes parsing failed"))
689 };
690 let tree = deserialize_async(tree_bytes).await?;
691 Ok(tree)
692 }
693
694 pub async fn get_money_tree_state_query(&self) -> Result<String> {
697 let tree = self.get_money_tree().await?;
699
700 match self.wallet.create_prepared_statement(
702 &format!("UPDATE {} SET {} = ?1;", *MONEY_TREE_TABLE, MONEY_TREE_COL_TREE),
703 rusqlite::params![serialize_async(&tree).await],
704 ) {
705 Ok(q) => Ok(q),
706 Err(e) => Err(Error::DatabaseError(format!(
707 "[get_money_tree_state_query] Creating query for money tree failed: {e:?}"
708 ))),
709 }
710 }
711
712 pub async fn get_nullifiers_smt(&self) -> Result<HashMap<BigUint, pallas::Base>> {
714 let rows = match self.wallet.query_multiple(&MONEY_SMT_TABLE, &[], &[]) {
715 Ok(r) => r,
716 Err(e) => {
717 return Err(Error::DatabaseError(format!(
718 "[get_nullifiers_smt] SMT records retrieval failed: {e:?}"
719 )))
720 }
721 };
722
723 let mut smt = HashMap::new();
724 for row in rows {
725 let Value::Blob(ref key_bytes) = row[0] else {
726 return Err(Error::ParseFailed("[get_nullifiers_smt] Key bytes parsing failed"))
727 };
728 let key = BigUint::from_bytes_le(key_bytes);
729
730 let Value::Blob(ref value_bytes) = row[1] else {
731 return Err(Error::ParseFailed("[get_nullifiers_smt] Value bytes parsing failed"))
732 };
733 let mut repr = [0; 32];
734 repr.copy_from_slice(value_bytes);
735 let Some(value) = pallas::Base::from_repr(repr).into() else {
736 return Err(Error::ParseFailed("[get_nullifiers_smt] Value conversion failed"))
737 };
738
739 smt.insert(key, value);
740 }
741
742 Ok(smt)
743 }
744
745 async fn parse_money_call(
748 &self,
749 call_idx: usize,
750 calls: &[DarkLeaf<ContractCall>],
751 ) -> Result<(Vec<Nullifier>, Vec<Coin>, Vec<AeadEncryptedNote>, Vec<TokenId>)> {
752 let mut nullifiers: Vec<Nullifier> = vec![];
753 let mut coins: Vec<Coin> = vec![];
754 let mut notes: Vec<AeadEncryptedNote> = vec![];
755 let mut freezes: Vec<TokenId> = vec![];
756
757 let call = &calls[call_idx];
758 let data = &call.data.data;
759 match MoneyFunction::try_from(data[0])? {
760 MoneyFunction::FeeV1 => {
761 println!("[parse_money_call] Found Money::FeeV1 call");
762 let params: MoneyFeeParamsV1 = deserialize_async(&data[9..]).await?;
763 nullifiers.push(params.input.nullifier);
764 coins.push(params.output.coin);
765 notes.push(params.output.note);
766 }
767 MoneyFunction::GenesisMintV1 => {
768 println!("[parse_money_call] Found Money::GenesisMintV1 call");
769 let params: MoneyGenesisMintParamsV1 = deserialize_async(&data[1..]).await?;
770 for output in params.outputs {
771 coins.push(output.coin);
772 notes.push(output.note);
773 }
774 }
775 MoneyFunction::PoWRewardV1 => {
776 println!("[parse_money_call] Found Money::PoWRewardV1 call");
777 let params: MoneyPoWRewardParamsV1 = deserialize_async(&data[1..]).await?;
778 coins.push(params.output.coin);
779 notes.push(params.output.note);
780 }
781 MoneyFunction::TransferV1 => {
782 println!("[parse_money_call] Found Money::TransferV1 call");
783 let params: MoneyTransferParamsV1 = deserialize_async(&data[1..]).await?;
784
785 for input in params.inputs {
786 nullifiers.push(input.nullifier);
787 }
788
789 for output in params.outputs {
790 coins.push(output.coin);
791 notes.push(output.note);
792 }
793 }
794 MoneyFunction::OtcSwapV1 => {
795 println!("[parse_money_call] Found Money::OtcSwapV1 call");
796 let params: MoneyTransferParamsV1 = deserialize_async(&data[1..]).await?;
797
798 for input in params.inputs {
799 nullifiers.push(input.nullifier);
800 }
801
802 for output in params.outputs {
803 coins.push(output.coin);
804 notes.push(output.note);
805 }
806 }
807 MoneyFunction::AuthTokenMintV1 => {
808 println!("[parse_money_call] Found Money::AuthTokenMintV1 call");
809 }
811 MoneyFunction::AuthTokenFreezeV1 => {
812 println!("[parse_money_call] Found Money::AuthTokenFreezeV1 call");
813 let params: MoneyAuthTokenFreezeParamsV1 = deserialize_async(&data[1..]).await?;
814 freezes.push(params.token_id);
815 }
816 MoneyFunction::TokenMintV1 => {
817 println!("[parse_money_call] Found Money::TokenMintV1 call");
818 let params: MoneyTokenMintParamsV1 = deserialize_async(&data[1..]).await?;
819 coins.push(params.coin);
820 let child_idx = call.children_indexes[0];
822 let child_call = &calls[child_idx];
823 let params: MoneyAuthTokenMintParamsV1 =
824 deserialize_async(&child_call.data.data[1..]).await?;
825 notes.push(params.enc_note);
826 }
827 }
828
829 Ok((nullifiers, coins, notes, freezes))
830 }
831
832 pub async fn apply_tx_money_data(
836 &self,
837 call_idx: usize,
838 calls: &[DarkLeaf<ContractCall>],
839 tx_hash: &String,
840 ) -> Result<bool> {
841 let (nullifiers, coins, notes, freezes) = self.parse_money_call(call_idx, calls).await?;
842 let secrets = self.get_money_secrets().await?;
843 let dao_notes_secrets = self.get_dao_notes_secrets().await?;
844 let mut tree = self.get_money_tree().await?;
845
846 let mut owncoins = vec![];
847
848 for (coin, note) in coins.iter().zip(notes.iter()) {
849 tree.append(MerkleNode::from(coin.inner()));
851
852 for secret in secrets.iter().chain(dao_notes_secrets.iter()) {
854 if let Ok(note) = note.decrypt::<MoneyNote>(secret) {
855 println!("[apply_tx_money_data] Successfully decrypted a Money Note");
856 println!("[apply_tx_money_data] Witnessing coin in Merkle tree");
857 let leaf_position = tree.mark().unwrap();
858
859 let owncoin =
860 OwnCoin { coin: *coin, note: note.clone(), secret: *secret, leaf_position };
861
862 owncoins.push(owncoin);
863 }
864 }
865 }
866
867 if let Err(e) = self.put_money_tree(&tree).await {
868 return Err(Error::DatabaseError(format!(
869 "[apply_tx_money_data] Put Money tree failed: {e:?}"
870 )))
871 }
872 self.smt_insert(&nullifiers)?;
873 let wallet_spent_coins = self.mark_spent_coins(&nullifiers, tx_hash).await?;
874
875 let query = format!(
877 "INSERT INTO {} ({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12);",
878 *MONEY_COINS_TABLE,
879 MONEY_COINS_COL_COIN,
880 MONEY_COINS_COL_IS_SPENT,
881 MONEY_COINS_COL_VALUE,
882 MONEY_COINS_COL_TOKEN_ID,
883 MONEY_COINS_COL_SPEND_HOOK,
884 MONEY_COINS_COL_USER_DATA,
885 MONEY_COINS_COL_COIN_BLIND,
886 MONEY_COINS_COL_VALUE_BLIND,
887 MONEY_COINS_COL_TOKEN_BLIND,
888 MONEY_COINS_COL_SECRET,
889 MONEY_COINS_COL_LEAF_POSITION,
890 MONEY_COINS_COL_MEMO,
891 );
892
893 let inverse_query =
895 format!("DELETE FROM {} WHERE {} = ?1;", *MONEY_COINS_TABLE, MONEY_COINS_COL_COIN);
896
897 println!("Found {} OwnCoin(s) in transaction", owncoins.len());
898 for owncoin in &owncoins {
899 println!("OwnCoin: {:?}", owncoin.coin);
900 let key = serialize_async(&owncoin.coin).await;
902
903 let inverse =
905 match self.wallet.create_prepared_statement(&inverse_query, rusqlite::params![key])
906 {
907 Ok(q) => q,
908 Err(e) => {
909 return Err(Error::DatabaseError(format!(
910 "[apply_tx_money_data] Creating Money coin insert inverse query failed: {e:?}"
911 )))
912 }
913 };
914
915 let params = rusqlite::params![
917 key,
918 0, serialize_async(&owncoin.note.value).await,
920 serialize_async(&owncoin.note.token_id).await,
921 serialize_async(&owncoin.note.spend_hook).await,
922 serialize_async(&owncoin.note.user_data).await,
923 serialize_async(&owncoin.note.coin_blind).await,
924 serialize_async(&owncoin.note.value_blind).await,
925 serialize_async(&owncoin.note.token_blind).await,
926 serialize_async(&owncoin.secret).await,
927 serialize_async(&owncoin.leaf_position).await,
928 serialize_async(&owncoin.note.memo).await,
929 ];
930
931 if let Err(e) = self.wallet.exec_sql(&query, params) {
932 return Err(Error::DatabaseError(format!(
933 "[apply_tx_money_data] Inserting Money coin failed: {e:?}"
934 )))
935 }
936
937 if let Err(e) = self.wallet.cache_inverse(inverse) {
939 return Err(Error::DatabaseError(format!(
940 "[apply_tx_money_data] Inserting inverse query into cache failed: {e:?}"
941 )))
942 }
943 }
944
945 let query = format!(
947 "UPDATE {} SET {} = 1 WHERE {} = ?1;",
948 *MONEY_TOKENS_TABLE, MONEY_TOKENS_COL_IS_FROZEN, MONEY_TOKENS_COL_TOKEN_ID,
949 );
950
951 let inverse_query = format!(
953 "UPDATE {} SET {} = 0 WHERE {} = ?1;",
954 *MONEY_TOKENS_TABLE, MONEY_TOKENS_COL_IS_FROZEN, MONEY_TOKENS_COL_TOKEN_ID,
955 );
956
957 for token_id in &freezes {
958 let key = serialize_async(token_id).await;
960
961 let inverse =
963 match self.wallet.create_prepared_statement(&inverse_query, rusqlite::params![key])
964 {
965 Ok(q) => q,
966 Err(e) => {
967 return Err(Error::DatabaseError(format!(
968 "[apply_tx_money_data] Creating Money token freeze inverse query failed: {e:?}"
969 )))
970 }
971 };
972
973 if let Err(e) = self.wallet.exec_sql(&query, rusqlite::params![key]) {
975 return Err(Error::DatabaseError(format!(
976 "[apply_tx_money_data] Update Money token freeze failed: {e:?}"
977 )))
978 }
979
980 if let Err(e) = self.wallet.cache_inverse(inverse) {
982 return Err(Error::DatabaseError(format!(
983 "[apply_tx_money_data] Inserting inverse query into cache failed: {e:?}"
984 )))
985 }
986 }
987
988 if self.fun && !owncoins.is_empty() {
989 kaching().await;
990 }
991
992 Ok(wallet_spent_coins || !owncoins.is_empty() || !freezes.is_empty())
993 }
994
995 async fn money_call_nullifiers(&self, call: &DarkLeaf<ContractCall>) -> Result<Vec<Nullifier>> {
997 let mut nullifiers: Vec<Nullifier> = vec![];
998
999 let data = &call.data.data;
1000 match MoneyFunction::try_from(data[0])? {
1001 MoneyFunction::FeeV1 => {
1002 let params: MoneyFeeParamsV1 = deserialize_async(&data[9..]).await?;
1003 nullifiers.push(params.input.nullifier);
1004 }
1005 MoneyFunction::TransferV1 => {
1006 let params: MoneyTransferParamsV1 = deserialize_async(&data[1..]).await?;
1007
1008 for input in params.inputs {
1009 nullifiers.push(input.nullifier);
1010 }
1011 }
1012 MoneyFunction::OtcSwapV1 => {
1013 let params: MoneyTransferParamsV1 = deserialize_async(&data[1..]).await?;
1014
1015 for input in params.inputs {
1016 nullifiers.push(input.nullifier);
1017 }
1018 }
1019 _ => { }
1020 }
1021
1022 Ok(nullifiers)
1023 }
1024
1025 pub async fn mark_tx_spend(&self, tx: &Transaction) -> Result<()> {
1027 let tx_hash = tx.hash().to_string();
1028 println!("[mark_tx_spend] Processing transaction: {tx_hash}");
1029 for (i, call) in tx.calls.iter().enumerate() {
1030 if call.data.contract_id != *MONEY_CONTRACT_ID {
1031 continue
1032 }
1033
1034 println!("[mark_tx_spend] Found Money contract in call {i}");
1035 let nullifiers = self.money_call_nullifiers(call).await?;
1036 self.mark_spent_coins(&nullifiers, &tx_hash).await?;
1037 }
1038
1039 Ok(())
1040 }
1041
1042 pub async fn mark_spent_coin(&self, coin: &Coin, spent_tx_hash: &String) -> WalletDbResult<()> {
1044 let key = serialize_async(&coin.inner()).await;
1046
1047 let query = format!(
1049 "UPDATE {} SET {} = 1, {} = ?1 WHERE {} = ?2;",
1050 *MONEY_COINS_TABLE,
1051 MONEY_COINS_COL_IS_SPENT,
1052 MONEY_COINS_COL_SPENT_TX_HASH,
1053 MONEY_COINS_COL_COIN
1054 );
1055
1056 let inverse = self.wallet.create_prepared_statement(
1058 &format!(
1059 "UPDATE {} SET {} = 0, {} = '-' WHERE {} = ?1;",
1060 *MONEY_COINS_TABLE,
1061 MONEY_COINS_COL_IS_SPENT,
1062 MONEY_COINS_COL_SPENT_TX_HASH,
1063 MONEY_COINS_COL_COIN
1064 ),
1065 rusqlite::params![key],
1066 )?;
1067
1068 self.wallet.exec_sql(&query, rusqlite::params![spent_tx_hash, key])?;
1070
1071 self.wallet.cache_inverse(inverse)
1073 }
1074
1075 pub async fn mark_spent_coins(
1078 &self,
1079 nullifiers: &[Nullifier],
1080 spent_tx_hash: &String,
1081 ) -> Result<bool> {
1082 if nullifiers.is_empty() {
1083 return Ok(false)
1084 }
1085
1086 let mut wallet_spent_coins = false;
1088 for coin in self.get_transaction_coins(spent_tx_hash).await? {
1089 if let Err(e) = self.mark_spent_coin(&coin.coin, spent_tx_hash).await {
1090 return Err(Error::DatabaseError(format!(
1091 "[mark_spent_coins] Marking spent coin failed: {e:?}"
1092 )))
1093 }
1094 wallet_spent_coins = true;
1095 }
1096
1097 for (coin, _, _) in self.get_coins(false).await? {
1099 if !nullifiers.contains(&coin.nullifier()) {
1100 continue
1101 }
1102 if let Err(e) = self.mark_spent_coin(&coin.coin, spent_tx_hash).await {
1103 return Err(Error::DatabaseError(format!(
1104 "[mark_spent_coins] Marking spent coin failed: {e:?}"
1105 )))
1106 }
1107 wallet_spent_coins = true;
1108 }
1109
1110 Ok(wallet_spent_coins)
1111 }
1112
1113 pub fn smt_insert(&self, nullifiers: &[Nullifier]) -> Result<()> {
1115 let store = WalletStorage::new(
1116 &self.wallet,
1117 &MONEY_SMT_TABLE,
1118 MONEY_SMT_COL_KEY,
1119 MONEY_SMT_COL_VALUE,
1120 );
1121 let mut smt = WalletSmt::new(store, PoseidonFp::new(), &EMPTY_NODES_FP);
1122
1123 let leaves: Vec<_> = nullifiers.iter().map(|x| (x.inner(), x.inner())).collect();
1124 smt.insert_batch(leaves)?;
1125
1126 Ok(())
1127 }
1128
1129 pub async fn reset_money_tree(&self) -> WalletDbResult<()> {
1131 println!("Resetting Money Merkle tree");
1132 let mut tree = MerkleTree::new(1);
1133 tree.append(MerkleNode::from(pallas::Base::ZERO));
1134 let _ = tree.mark().unwrap();
1135 self.put_money_tree(&tree).await?;
1136 println!("Successfully reset Money Merkle tree");
1137
1138 Ok(())
1139 }
1140
1141 pub fn reset_money_smt(&self) -> WalletDbResult<()> {
1143 println!("Resetting Money Sparse Merkle tree");
1144 let query = format!("DELETE FROM {};", *MONEY_SMT_TABLE);
1145 self.wallet.exec_sql(&query, &[])?;
1146 println!("Successfully reset Money Sparse Merkle tree");
1147
1148 Ok(())
1149 }
1150
1151 pub fn reset_money_coins(&self) -> WalletDbResult<()> {
1153 println!("Resetting coins");
1154 let query = format!("DELETE FROM {};", *MONEY_COINS_TABLE);
1155 self.wallet.exec_sql(&query, &[])?;
1156 println!("Successfully reset coins");
1157
1158 Ok(())
1159 }
1160
1161 pub async fn get_token(&self, input: String) -> Result<TokenId> {
1164 if input.chars().count() <= 5 {
1166 let aliases = self.get_aliases(Some(input.clone()), None).await?;
1167 if let Some(token_id) = aliases.get(&input) {
1168 return Ok(*token_id)
1169 }
1170 }
1171 Ok(TokenId::from_str(input.as_str())?)
1173 }
1174
1175 pub async fn append_fee_call(
1181 &self,
1182 tx: &Transaction,
1183 money_merkle_tree: &MerkleTree,
1184 fee_pk: &ProvingKey,
1185 fee_zkbin: &ZkBinary,
1186 spent_coins: Option<&[OwnCoin]>,
1187 ) -> Result<(ContractCall, Vec<Proof>, Vec<SecretKey>)> {
1188 let required_fee = compute_fee(&FEE_CALL_GAS) + self.get_tx_fee(tx, false).await?;
1191
1192 let mut available_coins = self.get_token_coins(&DARK_TOKEN_ID).await?;
1195 available_coins.retain(|x| x.note.value > required_fee);
1196 if let Some(spent_coins) = spent_coins {
1197 available_coins.retain(|x| !spent_coins.contains(x));
1198 }
1199 if available_coins.is_empty() {
1200 return Err(Error::Custom("Not enough native tokens to pay for fees".to_string()))
1201 }
1202
1203 let coin = &available_coins[0];
1204 let change_value = coin.note.value - required_fee;
1205
1206 let input = FeeCallInput {
1208 coin: coin.clone(),
1209 merkle_path: money_merkle_tree.witness(coin.leaf_position, 0).unwrap(),
1210 user_data_blind: BaseBlind::random(&mut OsRng),
1211 };
1212
1213 let output = FeeCallOutput {
1214 public_key: PublicKey::from_secret(coin.secret),
1215 value: change_value,
1216 token_id: coin.note.token_id,
1217 blind: BaseBlind::random(&mut OsRng),
1218 spend_hook: FuncId::none(),
1219 user_data: pallas::Base::ZERO,
1220 };
1221
1222 let token_blind = BaseBlind::random(&mut OsRng);
1224 let input_value_blind = ScalarBlind::random(&mut OsRng);
1225 let fee_value_blind = ScalarBlind::random(&mut OsRng);
1226 let output_value_blind = compute_remainder_blind(&[input_value_blind], &[fee_value_blind]);
1227
1228 let signature_secret = SecretKey::random(&mut OsRng);
1230
1231 let (proof, public_inputs) = create_fee_proof(
1233 fee_zkbin,
1234 fee_pk,
1235 &input,
1236 input_value_blind,
1237 &output,
1238 output_value_blind,
1239 output.spend_hook,
1240 output.user_data,
1241 output.blind,
1242 token_blind,
1243 signature_secret,
1244 )?;
1245
1246 let note = MoneyNote {
1248 coin_blind: output.blind,
1249 value: output.value,
1250 token_id: output.token_id,
1251 spend_hook: output.spend_hook,
1252 user_data: output.user_data,
1253 value_blind: output_value_blind,
1254 token_blind,
1255 memo: vec![],
1256 };
1257
1258 let encrypted_note = AeadEncryptedNote::encrypt(¬e, &output.public_key, &mut OsRng)?;
1259
1260 let params = MoneyFeeParamsV1 {
1261 input: Input {
1262 value_commit: public_inputs.input_value_commit,
1263 token_commit: public_inputs.token_commit,
1264 nullifier: public_inputs.nullifier,
1265 merkle_root: public_inputs.merkle_root,
1266 user_data_enc: public_inputs.input_user_data_enc,
1267 signature_public: public_inputs.signature_public,
1268 },
1269 output: Output {
1270 value_commit: public_inputs.output_value_commit,
1271 token_commit: public_inputs.token_commit,
1272 coin: public_inputs.output_coin,
1273 note: encrypted_note,
1274 },
1275 fee_value_blind,
1276 token_blind,
1277 };
1278
1279 let mut data = vec![MoneyFunction::FeeV1 as u8];
1281 required_fee.encode_async(&mut data).await?;
1282 params.encode_async(&mut data).await?;
1283 let call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
1284
1285 Ok((call, vec![proof], vec![signature_secret]))
1286 }
1287
1288 pub async fn attach_fee(&self, tx: &mut Transaction) -> Result<()> {
1290 let mut tx_nullifiers = vec![];
1292 for call in &tx.calls {
1293 if call.data.contract_id != *MONEY_CONTRACT_ID {
1294 continue
1295 }
1296
1297 match MoneyFunction::try_from(call.data.data[0])? {
1298 MoneyFunction::FeeV1 => {
1299 return Err(Error::Custom("Fee call already exists".to_string()))
1300 }
1301 _ => { }
1302 }
1303
1304 let nullifiers = self.money_call_nullifiers(call).await?;
1305 tx_nullifiers.extend_from_slice(&nullifiers);
1306 }
1307
1308 let mut spent_coins = vec![];
1310 let available_coins = self.get_token_coins(&DARK_TOKEN_ID).await?;
1311 for coin in available_coins {
1312 if tx_nullifiers.contains(&coin.nullifier()) {
1313 spent_coins.push(coin);
1314 }
1315 }
1316
1317 let zkas_bins = self.lookup_zkas(&MONEY_CONTRACT_ID).await?;
1321
1322 let Some(fee_zkbin) = zkas_bins.iter().find(|x| x.0 == MONEY_CONTRACT_ZKAS_FEE_NS_V1)
1323 else {
1324 return Err(Error::Custom("Fee circuit not found".to_string()))
1325 };
1326
1327 let fee_zkbin = ZkBinary::decode(&fee_zkbin.1)?;
1328
1329 let fee_circuit = ZkCircuit::new(empty_witnesses(&fee_zkbin)?, &fee_zkbin);
1330
1331 let fee_pk = ProvingKey::build(fee_zkbin.k, &fee_circuit);
1333
1334 let tree = self.get_money_tree().await?;
1337 let (fee_call, fee_proofs, fee_secrets) =
1338 self.append_fee_call(tx, &tree, &fee_pk, &fee_zkbin, Some(&spent_coins)).await?;
1339
1340 tx.calls.push(DarkLeaf { data: fee_call, parent_index: None, children_indexes: vec![] });
1342 tx.proofs.push(fee_proofs);
1343 let sigs = tx.create_sigs(&fee_secrets)?;
1344 tx.signatures.push(sigs);
1345
1346 Ok(())
1347 }
1348}