/ bin / drk / src / money.rs
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  
  19  use std::{
  20      collections::{BTreeMap, HashMap},
  21      str::FromStr,
  22  };
  23  
  24  use lazy_static::lazy_static;
  25  use rand::rngs::OsRng;
  26  use rusqlite::types::Value;
  27  
  28  use 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  };
  35  use 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  };
  48  use 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  };
  58  use darkfi_serial::{deserialize_async, serialize, serialize_async, AsyncEncodable};
  59  
  60  use 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
  70  pub 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.
  74  lazy_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
  86  pub const MONEY_KEYS_COL_KEY_ID: &str = "key_id";
  87  pub const MONEY_KEYS_COL_IS_DEFAULT: &str = "is_default";
  88  pub const MONEY_KEYS_COL_PUBLIC: &str = "public";
  89  pub const MONEY_KEYS_COL_SECRET: &str = "secret";
  90  
  91  // MONEY_COINS_TABLE
  92  pub const MONEY_COINS_COL_COIN: &str = "coin";
  93  pub const MONEY_COINS_COL_VALUE: &str = "value";
  94  pub const MONEY_COINS_COL_TOKEN_ID: &str = "token_id";
  95  pub const MONEY_COINS_COL_SPEND_HOOK: &str = "spend_hook";
  96  pub const MONEY_COINS_COL_USER_DATA: &str = "user_data";
  97  pub const MONEY_COINS_COL_COIN_BLIND: &str = "coin_blind";
  98  pub const MONEY_COINS_COL_VALUE_BLIND: &str = "value_blind";
  99  pub const MONEY_COINS_COL_TOKEN_BLIND: &str = "token_blind";
 100  pub const MONEY_COINS_COL_SECRET: &str = "secret";
 101  pub const MONEY_COINS_COL_LEAF_POSITION: &str = "leaf_position";
 102  pub const MONEY_COINS_COL_MEMO: &str = "memo";
 103  pub const MONEY_COINS_COL_CREATION_HEIGHT: &str = "creation_height";
 104  pub const MONEY_COINS_COL_IS_SPENT: &str = "is_spent";
 105  pub const MONEY_COINS_COL_SPENT_HEIGHT: &str = "spent_height";
 106  pub const MONEY_COINS_COL_SPENT_TX_HASH: &str = "spent_tx_hash";
 107  
 108  // MONEY_TOKENS_TABLE
 109  pub const MONEY_TOKENS_COL_TOKEN_ID: &str = "token_id";
 110  pub const MONEY_TOKENS_COL_MINT_AUTHORITY: &str = "mint_authority";
 111  pub const MONEY_TOKENS_COL_TOKEN_BLIND: &str = "token_blind";
 112  pub const MONEY_TOKENS_COL_IS_FROZEN: &str = "is_frozen";
 113  pub const MONEY_TOKENS_COL_FREEZE_HEIGHT: &str = "freeze_height";
 114  
 115  // MONEY_ALIASES_TABLE
 116  pub const MONEY_ALIASES_COL_ALIAS: &str = "alias";
 117  pub const MONEY_ALIASES_COL_TOKEN_ID: &str = "token_id";
 118  
 119  pub const BALANCE_BASE10_DECIMALS: usize = 8;
 120  
 121  impl 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  }