rpc_wallet.rs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2023 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::collections::HashMap;
  19. use anyhow::{anyhow, Result};
  20. use darkfi::{rpc::jsonrpc::JsonRequest, util::parse::encode_base10, wallet::walletdb::QueryType};
  21. use darkfi_dao_contract::dao_client::{
  22. DAO_TREES_COL_DAOS_TREE, DAO_TREES_COL_PROPOSALS_TREE, DAO_TREES_TABLE,
  23. };
  24. use darkfi_money_contract::client::{
  25. Coin, Note, OwnCoin, MONEY_COINS_COL_COIN, MONEY_COINS_COL_COIN_BLIND,
  26. MONEY_COINS_COL_IS_SPENT, MONEY_COINS_COL_LEAF_POSITION, MONEY_COINS_COL_MEMO,
  27. MONEY_COINS_COL_NULLIFIER, MONEY_COINS_COL_SECRET, MONEY_COINS_COL_SERIAL,
  28. MONEY_COINS_COL_SPEND_HOOK, MONEY_COINS_COL_TOKEN_BLIND, MONEY_COINS_COL_TOKEN_ID,
  29. MONEY_COINS_COL_USER_DATA, MONEY_COINS_COL_VALUE, MONEY_COINS_COL_VALUE_BLIND,
  30. MONEY_COINS_TABLE, MONEY_INFO_COL_LAST_SCANNED_SLOT, MONEY_INFO_TABLE,
  31. MONEY_KEYS_COL_IS_DEFAULT, MONEY_KEYS_COL_PUBLIC, MONEY_KEYS_COL_SECRET, MONEY_KEYS_TABLE,
  32. MONEY_TREE_COL_TREE, MONEY_TREE_TABLE,
  33. };
  34. use darkfi_sdk::{
  35. crypto::{
  36. constants::MERKLE_DEPTH, Keypair, MerkleNode, MerkleTree, Nullifier, PublicKey, SecretKey,
  37. TokenId,
  38. },
  39. incrementalmerkletree,
  40. incrementalmerkletree::bridgetree::BridgeTree,
  41. pasta::pallas,
  42. };
  43. use darkfi_serial::{deserialize, serialize};
  44. use prettytable::{format, row, Table};
  45. use rand::rngs::OsRng;
  46. use serde_json::json;
  47. use super::Drk;
  48. impl Drk {
  49. /// Initialize wallet with tables for the Money Contract.
  50. async fn wallet_initialize_money(&self) -> Result<()> {
  51. let wallet_schema = include_str!("../../../src/contract/money/wallet.sql");
  52. // We perform a request to darkfid with the schema to initialize
  53. // the necessary tables in the wallet.
  54. let req = JsonRequest::new("wallet.exec_sql", json!([wallet_schema]));
  55. let rep = self.rpc_client.request(req).await?;
  56. if rep == true {
  57. println!("Successfully initialized wallet schema for the Money Contract");
  58. } else {
  59. println!("Got unxpected reply from darkfid: {}", rep);
  60. }
  61. // Check if we have to initialize the Merkle tree.
  62. // We check if we find a row in the tree table, and if not, we create
  63. // a new tree and push it into the table.
  64. let mut tree_needs_init = false;
  65. let query = format!("SELECT * FROM {}", MONEY_TREE_TABLE);
  66. let params = json!([query, QueryType::Blob as u8, MONEY_TREE_COL_TREE]);
  67. let req = JsonRequest::new("wallet.query_row_single", params);
  68. // For now, on success, we don't care what's returned, but maybe in
  69. // the future we should actually check it?
  70. // TODO: The RPC needs a better variant for errors so detailed inspection
  71. // can be done with error codes and all that.
  72. if (self.rpc_client.request(req).await).is_err() {
  73. tree_needs_init = true;
  74. }
  75. if tree_needs_init {
  76. println!("Initializing Merkle tree");
  77. let tree = MerkleTree::new(100);
  78. self.put_money_tree(&tree).await?;
  79. println!("Successfully initialized Merkle tree for Money Contract");
  80. }
  81. if (self.wallet_last_scanned_slot().await).is_err() {
  82. let query = format!(
  83. "INSERT INTO {} ({}) VALUES (?1);",
  84. MONEY_INFO_TABLE, MONEY_INFO_COL_LAST_SCANNED_SLOT
  85. );
  86. let params = json!([query, QueryType::Integer as u8, 0]);
  87. let req = JsonRequest::new("wallet.exec_sql", params);
  88. let _ = self.rpc_client.request(req).await?;
  89. }
  90. Ok(())
  91. }
  92. /// Initialize wallet with tables for the DAO Contract.
  93. async fn wallet_initialize_dao(&self) -> Result<()> {
  94. let wallet_schema = include_str!("../../../src/contract/dao/wallet.sql");
  95. // We perform a request to darkfid with the schema to initialize
  96. // the necessary tables in the wallet.
  97. let req = JsonRequest::new("wallet.exec_sql", json!([wallet_schema]));
  98. let rep = self.rpc_client.request(req).await?;
  99. if rep == true {
  100. println!("Successfully initialized wallet schema for the DAO Contract");
  101. } else {
  102. println!("Got unxpected reply from darkfid: {}", rep);
  103. }
  104. // Check if we have to initialize the Merkle trees. We check if one exists,
  105. // but we actually have to create two.
  106. let mut tree_needs_init = false;
  107. let query = format!("SELECT {} FROM {}", DAO_TREES_COL_DAOS_TREE, DAO_TREES_TABLE);
  108. let params = json!([query, QueryType::Blob as u8, DAO_TREES_COL_DAOS_TREE]);
  109. let req = JsonRequest::new("wallet.query_row_single", params);
  110. // For now, on success, we don't care what's returned, but maybe in
  111. // the future we should actually check it?
  112. // TODO: The RPC needs a better variant for errors so detailed inspection
  113. // can be done with error codes and all that.
  114. if (self.rpc_client.request(req).await).is_err() {
  115. tree_needs_init = true;
  116. }
  117. if tree_needs_init {
  118. println!("Initializing DAO Merkle trees");
  119. let daos_tree = MerkleTree::new(100);
  120. let proposals_tree = MerkleTree::new(100);
  121. self.put_dao_trees(&daos_tree, &proposals_tree).await?;
  122. println!("Successfully initialized Merkle trees for DAO Contract");
  123. }
  124. Ok(())
  125. }
  126. /// Main orchestration for wallet initialization. Internally, it initializes
  127. /// the wallet structure for the Money contract and the DAO contract.
  128. /// This should be performed initially before doing other operations.
  129. pub async fn wallet_initialize(&self) -> Result<()> {
  130. self.wallet_initialize_money().await?;
  131. self.wallet_initialize_dao().await?;
  132. Ok(())
  133. }
  134. /// Generate a new wallet keypair and put it in the according wallet table.
  135. pub async fn wallet_keygen(&self) -> Result<()> {
  136. println!("Generating a new keypair");
  137. // TODO: We might want to have hierarchical deterministic key derivation.
  138. let keypair = Keypair::random(&mut OsRng);
  139. let public = serialize(&keypair.public);
  140. let secret = serialize(&keypair.secret);
  141. let is_default = 0;
  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. let params = json!([
  150. query,
  151. QueryType::Integer as u8,
  152. is_default,
  153. QueryType::Blob as u8,
  154. public,
  155. QueryType::Blob as u8,
  156. secret,
  157. ]);
  158. let req = JsonRequest::new("wallet.exec_sql", params);
  159. let rep = self.rpc_client.request(req).await?;
  160. if rep == true {
  161. println!("Successfully added new keypair to wallet");
  162. } else {
  163. println!("Got unexpected reply from darkfid: {}", rep);
  164. }
  165. println!("New address: {}", keypair.public);
  166. Ok(())
  167. }
  168. /// Fetch all coins and their metadata from the wallet, optionally also spent ones.
  169. /// The boolean in the return tuple marks if the coin is marked as spent.
  170. pub async fn wallet_coins(&self, fetch_spent: bool) -> Result<Vec<(OwnCoin, bool)>> {
  171. eprintln!("Fetching OwnCoins from wallet");
  172. let query = if fetch_spent {
  173. format!("SELECT * FROM {}", MONEY_COINS_TABLE)
  174. } else {
  175. format!(
  176. "SELECT * FROM {} WHERE {} = {}",
  177. MONEY_COINS_TABLE, MONEY_COINS_COL_IS_SPENT, false,
  178. )
  179. };
  180. let params = json!([
  181. query,
  182. QueryType::Blob as u8,
  183. MONEY_COINS_COL_COIN,
  184. QueryType::Integer as u8,
  185. MONEY_COINS_COL_IS_SPENT,
  186. QueryType::Blob as u8,
  187. MONEY_COINS_COL_SERIAL,
  188. QueryType::Blob as u8,
  189. MONEY_COINS_COL_VALUE,
  190. QueryType::Blob as u8,
  191. MONEY_COINS_COL_TOKEN_ID,
  192. QueryType::Blob as u8,
  193. MONEY_COINS_COL_SPEND_HOOK,
  194. QueryType::Blob as u8,
  195. MONEY_COINS_COL_USER_DATA,
  196. QueryType::Blob as u8,
  197. MONEY_COINS_COL_COIN_BLIND,
  198. QueryType::Blob as u8,
  199. MONEY_COINS_COL_VALUE_BLIND,
  200. QueryType::Blob as u8,
  201. MONEY_COINS_COL_TOKEN_BLIND,
  202. QueryType::Blob as u8,
  203. MONEY_COINS_COL_SECRET,
  204. QueryType::Blob as u8,
  205. MONEY_COINS_COL_NULLIFIER,
  206. QueryType::Blob as u8,
  207. MONEY_COINS_COL_LEAF_POSITION,
  208. QueryType::Blob as u8,
  209. MONEY_COINS_COL_MEMO,
  210. ]);
  211. let req = JsonRequest::new("wallet.query_row_multi", params);
  212. let rep = self.rpc_client.request(req).await?;
  213. // The returned thing should be an array of found rows.
  214. let Some(rows) = rep.as_array() else {
  215. return Err(anyhow!("Unexpected response from darkfid: {}", rep))
  216. };
  217. let mut owncoins = vec![];
  218. for row in rows {
  219. let Some(row) = row.as_array() else {
  220. return Err(anyhow!("Unexpected response from darkfid: {}", rep))
  221. };
  222. let coin_bytes: Vec<u8> = serde_json::from_value(row[0].clone())?;
  223. let coin: Coin = deserialize(&coin_bytes)?;
  224. let is_spent: u64 = serde_json::from_value(row[1].clone())?;
  225. let is_spent = is_spent > 0;
  226. let serial_bytes: Vec<u8> = serde_json::from_value(row[2].clone())?;
  227. let serial: pallas::Base = deserialize(&serial_bytes)?;
  228. let value_bytes: Vec<u8> = serde_json::from_value(row[3].clone())?;
  229. let value: u64 = deserialize(&value_bytes)?;
  230. let token_id_bytes: Vec<u8> = serde_json::from_value(row[4].clone())?;
  231. let token_id: TokenId = deserialize(&token_id_bytes)?;
  232. let spend_hook_bytes: Vec<u8> = serde_json::from_value(row[5].clone())?;
  233. let spend_hook: pallas::Base = deserialize(&spend_hook_bytes)?;
  234. let user_data_bytes: Vec<u8> = serde_json::from_value(row[6].clone())?;
  235. let user_data: pallas::Base = deserialize(&user_data_bytes)?;
  236. let coin_blind_bytes: Vec<u8> = serde_json::from_value(row[7].clone())?;
  237. let coin_blind: pallas::Base = deserialize(&coin_blind_bytes)?;
  238. let value_blind_bytes: Vec<u8> = serde_json::from_value(row[8].clone())?;
  239. let value_blind: pallas::Scalar = deserialize(&value_blind_bytes)?;
  240. let token_blind_bytes: Vec<u8> = serde_json::from_value(row[9].clone())?;
  241. let token_blind: pallas::Scalar = deserialize(&token_blind_bytes)?;
  242. let secret_bytes: Vec<u8> = serde_json::from_value(row[10].clone())?;
  243. let secret: SecretKey = deserialize(&secret_bytes)?;
  244. let nullifier_bytes: Vec<u8> = serde_json::from_value(row[11].clone())?;
  245. let nullifier: Nullifier = deserialize(&nullifier_bytes)?;
  246. let leaf_position_bytes: Vec<u8> = serde_json::from_value(row[12].clone())?;
  247. let leaf_position: incrementalmerkletree::Position = deserialize(&leaf_position_bytes)?;
  248. let memo: Vec<u8> = serde_json::from_value(row[13].clone())?;
  249. let note = Note {
  250. serial,
  251. value,
  252. token_id,
  253. spend_hook,
  254. user_data,
  255. coin_blind,
  256. value_blind,
  257. token_blind,
  258. memo,
  259. };
  260. let owncoin = OwnCoin { coin, note, secret, nullifier, leaf_position };
  261. owncoins.push((owncoin, is_spent))
  262. }
  263. Ok(owncoins)
  264. }
  265. /// Fetch known balances from the wallet and try to print them as a table.
  266. pub async fn wallet_balance(&self) -> Result<()> {
  267. // This represents "false"
  268. let is_spent = 0;
  269. let query = format!(
  270. "SELECT {}, {} FROM {} WHERE {} = {}",
  271. MONEY_COINS_COL_VALUE,
  272. MONEY_COINS_COL_TOKEN_ID,
  273. MONEY_COINS_TABLE,
  274. MONEY_COINS_COL_IS_SPENT,
  275. is_spent,
  276. );
  277. let params = json!([
  278. query,
  279. QueryType::Blob as u8,
  280. MONEY_COINS_COL_VALUE,
  281. QueryType::Blob as u8,
  282. MONEY_COINS_COL_TOKEN_ID,
  283. ]);
  284. let req = JsonRequest::new("wallet.query_row_multi", params);
  285. let rep = self.rpc_client.request(req).await?;
  286. // The returned thing should be an array of found rows.
  287. let Some(rows) = rep.as_array() else {
  288. return Err(anyhow!("Unexpected response from darkfid: {}", rep))
  289. };
  290. // Fill this map with balances, and in the end we'll print it as a table.
  291. let mut balmap: HashMap<String, u64> = HashMap::new();
  292. // Let's scan through the rows and see if we got anything.
  293. for row in rows {
  294. let Some(row) = row.as_array() else {
  295. return Err(anyhow!("Unexpected response from darkfid: {}", rep))
  296. };
  297. if row.len() != 2 {
  298. eprintln!("Error: Got invalid array, row should contain two elements.");
  299. eprintln!("Actual contents:\n:{:#?}", row);
  300. return Err(anyhow!("Unexpected response from darkfid: {}", rep))
  301. }
  302. let value_bytes: Vec<u8> = serde_json::from_value(row[0].clone())?;
  303. let mut value: u64 = deserialize(&value_bytes)?;
  304. let token_bytes: Vec<u8> = serde_json::from_value(row[1].clone())?;
  305. let token_id: TokenId = deserialize(&token_bytes)?;
  306. let token_id = format!("{}", token_id);
  307. if let Some(prev) = balmap.get(&token_id) {
  308. value += prev;
  309. }
  310. balmap.insert(token_id, value);
  311. }
  312. // Create a prettytable with the new data.
  313. let mut table = Table::new();
  314. table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
  315. table.set_titles(row!["Token ID", "Balance"]);
  316. for (token_id, balance) in balmap.iter() {
  317. // FIXME: Don't hardcode to 8 decimals
  318. table.add_row(row![token_id, encode_base10(*balance, 8)]);
  319. }
  320. if table.is_empty() {
  321. println!("No unspent balances found");
  322. } else {
  323. println!("{}", table);
  324. }
  325. Ok(())
  326. }
  327. /// Fetch pubkeys from the wallet and print the requested index.
  328. pub async fn wallet_address(&self, _idx: u64) -> Result<PublicKey> {
  329. let query = format!("SELECT {} FROM {};", MONEY_KEYS_COL_PUBLIC, MONEY_KEYS_TABLE);
  330. let params = json!([query, QueryType::Blob as u8, MONEY_KEYS_COL_PUBLIC]);
  331. let req = JsonRequest::new("wallet.query_row_single", params);
  332. let rep = self.rpc_client.request(req).await?;
  333. let Some(arr) = rep.as_array() else {
  334. return Err(anyhow!("Unexpected response from darkfid: {}", rep));
  335. };
  336. if arr.len() != 1 {
  337. return Err(anyhow!("Unexpected response from darkfid: {}", rep))
  338. }
  339. let key_bytes: Vec<u8> = serde_json::from_value(arr[0].clone())?;
  340. let public_key: PublicKey = deserialize(&key_bytes)?;
  341. Ok(public_key)
  342. }
  343. /// Fetch secret keys from the wallet and return them if found.
  344. pub async fn wallet_secrets(&self) -> Result<Vec<SecretKey>> {
  345. let query = format!("SELECT {} FROM {};", MONEY_KEYS_COL_SECRET, MONEY_KEYS_TABLE);
  346. let params = json!([query, QueryType::Blob as u8, MONEY_KEYS_COL_SECRET]);
  347. let req = JsonRequest::new("wallet.query_row_multi", params);
  348. let rep = self.rpc_client.request(req).await?;
  349. // The returned thing should be an array of found rows.
  350. let Some(rows) = rep.as_array() else {
  351. return Err(anyhow!("Unexpected response from darkfid: {}", rep))
  352. };
  353. let mut secrets = vec![];
  354. // Let's scan through the rows and see if we got anything.
  355. for row in rows {
  356. let secret_bytes: Vec<u8> = serde_json::from_value(row[0].clone())?;
  357. let secret: SecretKey = deserialize(&secret_bytes)?;
  358. secrets.push(secret);
  359. }
  360. Ok(secrets)
  361. }
  362. /// Import given secret keys into the wallet. The query uses INSERT, so if the key already
  363. /// exists, it will simply be skipped.
  364. pub async fn wallet_import_secrets(&self, secrets: Vec<SecretKey>) -> Result<Vec<PublicKey>> {
  365. let mut ret = vec![];
  366. for secret in secrets {
  367. ret.push(PublicKey::from_secret(secret));
  368. let is_default = 0;
  369. let public = serialize(&PublicKey::from_secret(secret));
  370. let secret = serialize(&secret);
  371. let query = format!(
  372. "INSERT INTO {} ({}, {}, {}) VALUES (?1, ?2, ?3)",
  373. MONEY_KEYS_TABLE,
  374. MONEY_KEYS_COL_IS_DEFAULT,
  375. MONEY_KEYS_COL_PUBLIC,
  376. MONEY_KEYS_COL_SECRET,
  377. );
  378. let params = json!([
  379. query,
  380. QueryType::Integer as u8,
  381. is_default,
  382. QueryType::Blob as u8,
  383. public,
  384. QueryType::Blob as u8,
  385. secret,
  386. ]);
  387. let req = JsonRequest::new("wallet.exec_sql", params);
  388. let rep = self.rpc_client.request(req).await?;
  389. if rep != true {
  390. // Something weird happened?
  391. eprintln!("Got unexpected reply from darkfid: {}", rep);
  392. }
  393. }
  394. Ok(ret)
  395. }
  396. /// Get the Merkle tree from the wallet
  397. pub async fn wallet_tree(&self) -> Result<BridgeTree<MerkleNode, MERKLE_DEPTH>> {
  398. let query = format!("SELECT * FROM {}", MONEY_TREE_TABLE);
  399. let params = json!([query, QueryType::Blob as u8, MONEY_TREE_COL_TREE]);
  400. let req = JsonRequest::new("wallet.query_row_single", params);
  401. let rep = self.rpc_client.request(req).await?;
  402. let tree_bytes: Vec<u8> = serde_json::from_value(rep[0].clone())?;
  403. let tree = deserialize(&tree_bytes)?;
  404. Ok(tree)
  405. }
  406. /// Get the last scanned slot from the wallet
  407. pub async fn wallet_last_scanned_slot(&self) -> Result<u64> {
  408. let query =
  409. format!("SELECT {} FROM {};", MONEY_INFO_COL_LAST_SCANNED_SLOT, MONEY_INFO_TABLE);
  410. let params = json!([query, QueryType::Integer as u8, MONEY_INFO_COL_LAST_SCANNED_SLOT]);
  411. let req = JsonRequest::new("wallet.query_row_single", params);
  412. let rep = self.rpc_client.request(req).await?;
  413. Ok(serde_json::from_value(rep[0].clone())?)
  414. }
  415. /// Mark a coin in the wallet as spent
  416. pub async fn mark_spent_coin(&self, coin: &Coin) -> Result<()> {
  417. let query = format!(
  418. "UPDATE {} SET {} = ?1 WHERE {} = ?2;",
  419. MONEY_COINS_TABLE, MONEY_COINS_COL_IS_SPENT, MONEY_COINS_COL_COIN
  420. );
  421. let params = json!([
  422. query,
  423. QueryType::Integer as u8,
  424. 1,
  425. QueryType::Blob as u8,
  426. serialize(&coin.inner())
  427. ]);
  428. let req = JsonRequest::new("wallet.exec_sql", params);
  429. let _ = self.rpc_client.request(req).await?;
  430. Ok(())
  431. }
  432. /// Marks all coins in the wallet as spent, if their nullifier is
  433. /// in the provided set
  434. pub async fn mark_spent_coins(&self, nullifiers: Vec<Nullifier>) -> Result<()> {
  435. if nullifiers.is_empty() {
  436. return Ok(())
  437. }
  438. for (coin, _) in self.wallet_coins(false).await? {
  439. if nullifiers.contains(&coin.nullifier) {
  440. self.mark_spent_coin(&coin.coin).await?;
  441. }
  442. }
  443. Ok(())
  444. }
  445. /// Mark a given coin in the wallet as unspent
  446. pub async fn unspend_coin(&self, coin: &Coin) -> Result<()> {
  447. let query = format!(
  448. "UPDATE {} SET {} = ?1 WHERE {} = ?2;",
  449. MONEY_COINS_TABLE, MONEY_COINS_COL_IS_SPENT, MONEY_COINS_COL_COIN
  450. );
  451. let params = json!([
  452. query,
  453. QueryType::Integer as u8,
  454. 0,
  455. QueryType::Blob as u8,
  456. serialize(&coin.inner())
  457. ]);
  458. let req = JsonRequest::new("wallet.exec_sql", params);
  459. let _ = self.rpc_client.request(req).await?;
  460. Ok(())
  461. }
  462. /// Replace the Money Merkle tree in the wallet
  463. pub async fn put_money_tree(&self, tree: &BridgeTree<MerkleNode, MERKLE_DEPTH>) -> Result<()> {
  464. let query = format!(
  465. "DELETE FROM {}; INSERT INTO {} ({}) VALUES (?1);",
  466. MONEY_TREE_TABLE, MONEY_TREE_TABLE, MONEY_TREE_COL_TREE
  467. );
  468. let params = json!([query, QueryType::Blob as u8, serialize(tree)]);
  469. let req = JsonRequest::new("wallet.exec_sql", params);
  470. let _ = self.rpc_client.request(req).await?;
  471. Ok(())
  472. }
  473. /// Reset the Money Contract Merkle tree and coins in the wallet
  474. pub async fn reset_money_tree(&self) -> Result<()> {
  475. println!("Resetting Money Merkle tree");
  476. let tree = BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(100);
  477. self.put_money_tree(&tree).await?;
  478. println!("Successfully reset Money Merkle tree");
  479. println!("Resetting coins");
  480. let query = format!("DELETE FROM {};", MONEY_COINS_TABLE);
  481. let params = json!([query]);
  482. let req = JsonRequest::new("wallet.exec_sql", params);
  483. let _ = self.rpc_client.request(req).await?;
  484. println!("Successfully reset coins");
  485. Ok(())
  486. }
  487. /// Replace the DAO Merkle trees in the wallet
  488. pub async fn put_dao_trees(
  489. &self,
  490. daos_tree: &BridgeTree<MerkleNode, MERKLE_DEPTH>,
  491. proposals_tree: &BridgeTree<MerkleNode, MERKLE_DEPTH>,
  492. ) -> Result<()> {
  493. let query = format!(
  494. "DELETE FROM {}; INSERT INTO {} ({}) VALUES (?1);",
  495. DAO_TREES_TABLE, DAO_TREES_TABLE, DAO_TREES_COL_DAOS_TREE
  496. );
  497. let params = json!([query, QueryType::Blob as u8, serialize(daos_tree)]);
  498. let req = JsonRequest::new("wallet.exec_sql", params);
  499. let _ = self.rpc_client.request(req).await?;
  500. let query = format!(
  501. "DELETE FROM {}; INSERT INTO {} ({}) VALUES (?1);",
  502. DAO_TREES_TABLE, DAO_TREES_TABLE, DAO_TREES_COL_PROPOSALS_TREE
  503. );
  504. let params = json!([query, QueryType::Blob as u8, serialize(proposals_tree)]);
  505. let req = JsonRequest::new("wallet.exec_sql", params);
  506. let _ = self.rpc_client.request(req).await?;
  507. Ok(())
  508. }
  509. /// Reset the DAO Contract Merkle trees in the wallet
  510. pub async fn reset_dao_trees(&self) -> Result<()> {
  511. println!("Resetting DAO Merkle trees");
  512. let tree0 = MerkleTree::new(100);
  513. let tree1 = MerkleTree::new(100);
  514. self.put_dao_trees(&tree0, &tree1).await?;
  515. println!("Successfully reset DAO Merkle trees");
  516. Ok(())
  517. }
  518. }