drk/
money.rs

1/* This file is part of DarkFi (https://dark.fi)
2 *
3 * Copyright (C) 2020-2025 Dyne.org foundation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18
19use 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
69// Money Merkle tree Sled key
70pub const SLED_MERKLE_TREES_MONEY: &[u8] = b"_money_tree";
71
72// Wallet SQL table constant names. These have to represent the `money.sql`
73// SQL schema. Table names are prefixed with the contract ID to avoid collisions.
74lazy_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
85// MONEY_KEYS_TABLE
86pub 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
91// MONEY_COINS_TABLE
92pub 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
108// MONEY_TOKENS_TABLE
109pub 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
115// MONEY_ALIASES_TABLE
116pub 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    /// Initialize wallet with tables for the Money contract.
123    pub async fn initialize_money(&self, output: &mut Vec<String>) -> WalletDbResult<()> {
124        // Initialize Money wallet schema
125        let wallet_schema = include_str!("../money.sql");
126        self.wallet.exec_batch_sql(wallet_schema)?;
127
128        // Insert DRK alias
129        self.add_alias("DRK".to_string(), *DARK_TOKEN_ID, output).await?;
130
131        Ok(())
132    }
133
134    /// Generate a new keypair and place it into the wallet.
135    pub async fn money_keygen(&self, output: &mut Vec<String>) -> WalletDbResult<()> {
136        output.push(String::from("Generating a new keypair"));
137
138        // TODO: We might want to have hierarchical deterministic key derivation.
139        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    /// Fetch default secret key from the wallet.
165    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    /// Fetch default pubkey from the wallet.
188    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    /// Set provided index address as default in the wallet.
211    pub fn set_default_address(&self, idx: usize) -> WalletDbResult<()> {
212        // First we update previous default record
213        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        // and then we set the new one
218        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    /// Fetch all pukeys from the wallet.
227    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    /// Fetch all secret keys from the wallet.
270    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        // Let's scan through the rows and see if we got anything.
284        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    /// Import given secret keys into the wallet.
298    /// If the key already exists, it will be skipped.
299    /// Returns the respective PublicKey objects for the imported keys.
300    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            // Check if secret already exists
311            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    /// Fetch known unspent balances from the wallet and return them as a hashmap.
341    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        // Fill this map with balances
346        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    /// Fetch all coins and their metadata related to the Money contract from the wallet.
362    /// Optionally also fetch spent ones.
363    /// The boolean in the returned tuple notes if the coin was marked
364    /// as spent, along with the height and tx it was spent in.
365    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    /// Fetch provided token unspend balances from the wallet.
395    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    /// Fetch provided contract specified token unspend balances from the wallet.
424    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    /// Auxiliary function to parse a `MONEY_COINS_TABLE` record.
459    /// The boolean in the returned tuple notes if the coin was marked
460    /// as spent, along with the height and tx it was spent in.
461    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    /// Create an alias record for provided Token ID.
574    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    /// Fetch all aliases from the wallet.
592    /// Optionally filter using alias name and/or token id.
593    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        // Fill this map with aliases
608        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    /// Fetch all aliases from the wallet, mapped by token id.
633    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    /// Remove provided alias record from the wallet database.
650    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    /// Mark a given coin in the wallet as unspent.
664    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    /// Fetch the Money Merkle tree from the cache.
677    /// If it doesn't exists a new Merkle Tree is returned.
678    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    /// Auxiliary function to grab all the nullifiers, coins with their
691    /// notes and freezes from a transaction money call.
692    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                // Handled in TokenMint
751            }
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                // Grab the note from the child auth call
762                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    /// Auxiliary function to handle coins with their notes from a
774    /// transaction money call.
775    /// Returns our found own coins.
776    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        // Keep track of our own coins found in the vec
784        let mut owncoins = vec![];
785
786        // Check if provided coins vec is empty
787        if coins.is_empty() {
788            return owncoins
789        }
790
791        // Handle provided coins vector and grab our own
792        for (coin, note) in coins {
793            // Append the new coin to the Merkle tree.
794            // Every coin has to be added.
795            tree.append(MerkleNode::from(coin.inner()));
796
797            // Attempt to decrypt the note
798            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    /// Auxiliary function to handle own coins from a transaction money
815    /// call.
816    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        // Check if we have any owncoins to process
825        if coins.is_empty() {
826            return Ok(())
827        }
828
829        // This is the SQL query we'll be executing to insert new coins into the wallet
830        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        // Handle our own coins
850        let spent_height: Option<u32> = None;
851        for coin in coins {
852            scan_cache.log(format!("OwnCoin: {:?}", coin.coin));
853            // Grab coin record key
854            let key = coin.coin.to_bytes();
855
856            // Push to our own coins nullifiers cache
857            scan_cache
858                .owncoins_nullifiers
859                .insert(coin.nullifier().to_bytes(), (key, coin.leaf_position));
860
861            // Execute the query
862            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, // <-- is_spent
876                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    /// Auxiliary function to handle freezes from a transaction money
890    /// call.
891    /// Returns a flag indicating if provided freezes refer to our own
892    /// wallet.
893    async fn handle_money_call_freezes(
894        &self,
895        own_tokens: &[TokenId],
896        freezes: &[TokenId],
897        freeze_height: &u32,
898    ) -> Result<bool> {
899        // Check if we have any freezes to process
900        if freezes.is_empty() {
901            return Ok(false)
902        }
903
904        // Find our own tokens that got frozen
905        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        // Check if we need to freeze anything
913        if own_freezes.is_empty() {
914            return Ok(false)
915        }
916
917        // This is the SQL query we'll be executing to update frozen tokens into the wallet
918        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            // Grab token record key
928            let key = serialize_async(token_id).await;
929
930            // Execute the query
931            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    /// Append data related to Money contract transactions into the
944    /// wallet database and update the provided scan cache.
945    /// Returns a flag indicating if provided data refer to our own
946    /// wallet.
947    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        // Parse the call
956        let (nullifiers, coins, freezes) =
957            self.parse_money_call(scan_cache, call_idx, calls).await?;
958
959        // Parse call coins and grab our own
960        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        // Update nullifiers smt
968        self.smt_insert(&mut scan_cache.money_smt, &nullifiers)?;
969
970        // Check if we have any spent coins
971        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        // Handle our own coins
980        self.handle_money_call_owncoins(scan_cache, &owncoins, block_height).await?;
981
982        // Handle freezes
983        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    /// Auxiliary function to  grab all the nullifiers from a transaction money call.
994    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            _ => { /* Do nothing */ }
1018        }
1019
1020        Ok(nullifiers)
1021    }
1022
1023    /// Mark provided transaction input coins as spent.
1024    pub async fn mark_tx_spend(&self, tx: &Transaction, output: &mut Vec<String>) -> Result<()> {
1025        // Create a cache of all our own nullifiers
1026        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    /// Marks all coins in the wallet as spent, if their nullifier is in the given set.
1050    /// Returns a flag indicating if any of the provided nullifiers refer to our own wallet.
1051    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        // Find our owncoins that where spent
1064        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        // Create an SQL `UPDATE` query to mark rows as spent(1)
1075        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        // Mark spent own coins
1085        for (ownoin, leaf_position) in spent_owncoins {
1086            // Execute the query
1087            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            // Remove the coin mark from the Merkle tree
1096            if let Some(ref mut tree) = tree {
1097                tree.remove_mark(*leaf_position);
1098            }
1099        }
1100
1101        Ok(true)
1102    }
1103
1104    /// Inserts given slice to the wallets nullifiers Sparse Merkle Tree.
1105    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    /// Reset the Money Merkle tree in the cache.
1111    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    /// Reset the Money nullifiers Sparse Merkle Tree in the cache.
1123    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    /// Reset the Money coins in the wallet.
1136    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    /// Remove the Money coins in the wallet that were created after
1146    /// provided height.
1147    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    /// Mark the Money coins in the wallet that were spent after
1164    /// provided height as unspent.
1165    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    /// Retrieve token by provided string.
1186    /// Input string represents either an alias or a token id.
1187    pub async fn get_token(&self, input: String) -> Result<TokenId> {
1188        // Check if input is an alias(max 5 characters)
1189        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        // Else parse input
1196        Ok(TokenId::from_str(input.as_str())?)
1197    }
1198
1199    /// Create and append a `Money::Fee` call to a given [`Transaction`].
1200    ///
1201    /// Optionally takes a set of spent coins in order not to reuse them here.
1202    ///
1203    /// Returns the `Fee` call, and all necessary data and parameters related.
1204    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        // First we verify the fee-less transaction to see how much fee it requires for execution
1213        // and verification.
1214        let required_fee = compute_fee(&FEE_CALL_GAS) + self.get_tx_fee(tx, false).await?;
1215
1216        // Knowing the total gas, we can now find an OwnCoin of enough value
1217        // so that we can create a valid Money::Fee call.
1218        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        // Input and output setup
1231        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        // Create blinding factors
1247        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        // Create an ephemeral signing key
1253        let signature_secret = SecretKey::random(&mut OsRng);
1254
1255        // Create the actual fee proof
1256        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        // Encrypted note for the output
1271        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(&note, &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        // Encode the contract call
1304        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    /// Create and attach the fee call to given transaction.
1313    pub async fn attach_fee(&self, tx: &mut Transaction) -> Result<()> {
1314        // Grab spent coins nullifiers of the transactions and check no other fee call exists
1315        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                _ => { /* Do nothing */ }
1326            }
1327
1328            let nullifiers = self.money_call_nullifiers(call).await?;
1329            tx_nullifiers.extend_from_slice(&nullifiers);
1330        }
1331
1332        // Grab all native owncoins to check if any is spent
1333        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        // Now we need to do a lookup for the zkas proof bincodes, and create
1342        // the circuit objects and proving keys so we can build the transaction.
1343        // We also do this through the RPC.
1344        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        // Creating Fee circuits proving keys
1356        let fee_pk = ProvingKey::build(fee_zkbin.k, &fee_circuit);
1357
1358        // We first have to execute the fee-less tx to gather its used gas, and then we feed
1359        // it into the fee-creating function.
1360        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        // Append the fee call to the transaction
1365        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}