money.rs 53 KB

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