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(¬e, &output.public_key, &mut OsRng)?; 1283 1284 let params = MoneyFeeParamsV1 { 1285 input: Input { 1286 value_commit: public_inputs.input_value_commit, 1287 token_commit: public_inputs.token_commit, 1288 nullifier: public_inputs.nullifier, 1289 merkle_root: public_inputs.merkle_root, 1290 user_data_enc: public_inputs.input_user_data_enc, 1291 signature_public: public_inputs.signature_public, 1292 }, 1293 output: Output { 1294 value_commit: public_inputs.output_value_commit, 1295 token_commit: public_inputs.token_commit, 1296 coin: public_inputs.output_coin, 1297 note: encrypted_note, 1298 }, 1299 fee_value_blind, 1300 token_blind, 1301 }; 1302 1303 // 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 }