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