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