rpc_wallet.rs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2022 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_money_contract::client::{
  22. Coin, Note, OwnCoin, MONEY_COINS_COL_COIN, MONEY_COINS_COL_COIN_BLIND,
  23. MONEY_COINS_COL_IS_SPENT, MONEY_COINS_COL_LEAF_POSITION, MONEY_COINS_COL_MEMO,
  24. MONEY_COINS_COL_NULLIFIER, MONEY_COINS_COL_SECRET, MONEY_COINS_COL_SERIAL,
  25. MONEY_COINS_COL_TOKEN_BLIND, MONEY_COINS_COL_TOKEN_ID, MONEY_COINS_COL_VALUE,
  26. MONEY_COINS_COL_VALUE_BLIND, MONEY_COINS_TABLE, MONEY_INFO_COL_LAST_SCANNED_SLOT,
  27. MONEY_INFO_TABLE, MONEY_KEYS_COL_IS_DEFAULT, MONEY_KEYS_COL_PUBLIC, MONEY_KEYS_COL_SECRET,
  28. MONEY_KEYS_TABLE, MONEY_TREE_COL_TREE, MONEY_TREE_TABLE,
  29. };
  30. use darkfi_sdk::{
  31. crypto::{
  32. constants::MERKLE_DEPTH, Keypair, MerkleNode, Nullifier, PublicKey, SecretKey, TokenId,
  33. },
  34. incrementalmerkletree,
  35. incrementalmerkletree::bridgetree::BridgeTree,
  36. pasta::pallas,
  37. };
  38. use darkfi_serial::{deserialize, serialize};
  39. use prettytable::{format, row, Table};
  40. use rand::rngs::OsRng;
  41. use serde_json::json;
  42. use super::Drk;
  43. impl Drk {
  44. /// Initialize wallet with tables for the Money contract.
  45. /// This should be performed initially before doing other operations.
  46. pub async fn wallet_initialize(&self) -> Result<()> {
  47. let wallet_schema = include_str!("../../../src/contract/money/wallet.sql");
  48. // We perform a request to darkfid with the schema to initialize
  49. // the necessary tables in the wallet.
  50. let req = JsonRequest::new("wallet.exec_sql", json!([wallet_schema]));
  51. let rep = self.rpc_client.request(req).await?;
  52. if rep == true {
  53. println!("Successfully initialized wallet schema for Money Contract");
  54. } else {
  55. println!("Got unexpected reply from darkfid: {}", rep);
  56. }
  57. // Check if we have to initialize the Merkle tree.
  58. // We check if we find a row in the tree table, and if not, we create
  59. // a new tree and push it into the table.
  60. let mut tree_needs_init = false;
  61. let query = format!("SELECT * FROM {}", MONEY_TREE_TABLE);
  62. let params = json!([query, QueryType::Blob as u8, MONEY_TREE_COL_TREE]);
  63. let req = JsonRequest::new("wallet.query_row_single", params);
  64. // For now, on success, we don't care what's returned, but maybe in
  65. // the future we should actually check it?
  66. // TODO: The RPC needs a better variant for errors so detailed inspection
  67. // can be done with error codes and all that.
  68. if let Err(_) = self.rpc_client.request(req).await {
  69. tree_needs_init = true;
  70. }
  71. if tree_needs_init {
  72. println!("Initializing Merkle tree");
  73. let tree = BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(100);
  74. self.put_tree(&tree).await?;
  75. println!("Successfully initialized Merkle tree");
  76. }
  77. if let Err(_) = self.wallet_last_scanned_slot().await {
  78. let query = format!(
  79. "INSERT INTO {} ({}) VALUES (?1);",
  80. MONEY_INFO_TABLE, MONEY_INFO_COL_LAST_SCANNED_SLOT
  81. );
  82. let params = json!([query, QueryType::Integer as u8, 0]);
  83. let req = JsonRequest::new("wallet.exec_sql", params);
  84. let _ = self.rpc_client.request(req).await?;
  85. }
  86. Ok(())
  87. }
  88. /// Generate a new wallet keypair and put it in the according wallet table.
  89. pub async fn wallet_keygen(&self) -> Result<()> {
  90. println!("Generating a new keypair");
  91. // TODO: We might want to have hierarchical deterministic key derivation.
  92. let keypair = Keypair::random(&mut OsRng);
  93. let public = serialize(&keypair.public);
  94. let secret = serialize(&keypair.secret);
  95. let is_default = 0;
  96. let query = format!(
  97. "INSERT INTO {} ({}, {}, {}) VALUES (?1, ?2, ?3)",
  98. MONEY_KEYS_TABLE,
  99. MONEY_KEYS_COL_IS_DEFAULT,
  100. MONEY_KEYS_COL_PUBLIC,
  101. MONEY_KEYS_COL_SECRET,
  102. );
  103. let params = json!([
  104. query,
  105. QueryType::Integer as u8,
  106. is_default,
  107. QueryType::Blob as u8,
  108. public,
  109. QueryType::Blob as u8,
  110. secret,
  111. ]);
  112. let req = JsonRequest::new("wallet.exec_sql", params);
  113. let rep = self.rpc_client.request(req).await?;
  114. if rep == true {
  115. println!("Successfully added new keypair to wallet");
  116. } else {
  117. println!("Got unexpected reply from darkfid: {}", rep);
  118. }
  119. println!("New address: {}", keypair.public);
  120. Ok(())
  121. }
  122. /// Fetch all coins and their metadata from the wallet, optionally also spent ones.
  123. /// The boolean in the return tuple marks if the coin is marked as spent.
  124. pub async fn wallet_coins(&self, fetch_spent: bool) -> Result<Vec<(OwnCoin, bool)>> {
  125. eprintln!("Fetching OwnCoins from wallet");
  126. let query = if fetch_spent {
  127. format!("SELECT * FROM {}", MONEY_COINS_TABLE)
  128. } else {
  129. format!(
  130. "SELECT * FROM {} WHERE {} = {}",
  131. MONEY_COINS_TABLE, MONEY_COINS_COL_IS_SPENT, false,
  132. )
  133. };
  134. let params = json!([
  135. query,
  136. QueryType::Blob as u8,
  137. MONEY_COINS_COL_COIN,
  138. QueryType::Integer as u8,
  139. MONEY_COINS_COL_IS_SPENT,
  140. QueryType::Blob as u8,
  141. MONEY_COINS_COL_SERIAL,
  142. QueryType::Blob as u8,
  143. MONEY_COINS_COL_VALUE,
  144. QueryType::Blob as u8,
  145. MONEY_COINS_COL_TOKEN_ID,
  146. QueryType::Blob as u8,
  147. MONEY_COINS_COL_COIN_BLIND,
  148. QueryType::Blob as u8,
  149. MONEY_COINS_COL_VALUE_BLIND,
  150. QueryType::Blob as u8,
  151. MONEY_COINS_COL_TOKEN_BLIND,
  152. QueryType::Blob as u8,
  153. MONEY_COINS_COL_SECRET,
  154. QueryType::Blob as u8,
  155. MONEY_COINS_COL_NULLIFIER,
  156. QueryType::Blob as u8,
  157. MONEY_COINS_COL_LEAF_POSITION,
  158. QueryType::Blob as u8,
  159. MONEY_COINS_COL_MEMO,
  160. ]);
  161. let req = JsonRequest::new("wallet.query_row_multi", params);
  162. let rep = self.rpc_client.request(req).await?;
  163. // The returned thing should be an array of found rows.
  164. let Some(rows) = rep.as_array() else {
  165. return Err(anyhow!("Unexpected response from darkfid: {}", rep))
  166. };
  167. let mut owncoins = vec![];
  168. for row in rows {
  169. let Some(row) = row.as_array() else {
  170. return Err(anyhow!("Unexpected response from darkfid: {}", rep))
  171. };
  172. let coin_bytes: Vec<u8> = serde_json::from_value(row[0].clone())?;
  173. let coin: Coin = deserialize(&coin_bytes)?;
  174. let is_spent: u64 = serde_json::from_value(row[1].clone())?;
  175. let is_spent = if is_spent > 0 { true } else { false };
  176. let serial_bytes: Vec<u8> = serde_json::from_value(row[2].clone())?;
  177. let serial: pallas::Base = deserialize(&serial_bytes)?;
  178. let value_bytes: Vec<u8> = serde_json::from_value(row[3].clone())?;
  179. let value: u64 = deserialize(&value_bytes)?;
  180. let token_id_bytes: Vec<u8> = serde_json::from_value(row[4].clone())?;
  181. let token_id: TokenId = deserialize(&token_id_bytes)?;
  182. let coin_blind_bytes: Vec<u8> = serde_json::from_value(row[5].clone())?;
  183. let coin_blind: pallas::Base = deserialize(&coin_blind_bytes)?;
  184. let value_blind_bytes: Vec<u8> = serde_json::from_value(row[6].clone())?;
  185. let value_blind: pallas::Scalar = deserialize(&value_blind_bytes)?;
  186. let token_blind_bytes: Vec<u8> = serde_json::from_value(row[7].clone())?;
  187. let token_blind: pallas::Scalar = deserialize(&token_blind_bytes)?;
  188. let secret_bytes: Vec<u8> = serde_json::from_value(row[8].clone())?;
  189. let secret: SecretKey = deserialize(&secret_bytes)?;
  190. let nullifier_bytes: Vec<u8> = serde_json::from_value(row[9].clone())?;
  191. let nullifier: Nullifier = deserialize(&nullifier_bytes)?;
  192. let leaf_position_bytes: Vec<u8> = serde_json::from_value(row[10].clone())?;
  193. let leaf_position: incrementalmerkletree::Position = deserialize(&leaf_position_bytes)?;
  194. let memo: Vec<u8> = serde_json::from_value(row[11].clone())?;
  195. let note = Note { serial, value, token_id, coin_blind, value_blind, token_blind, memo };
  196. let owncoin = OwnCoin { coin, note, secret, nullifier, leaf_position };
  197. owncoins.push((owncoin, is_spent))
  198. }
  199. Ok(owncoins)
  200. }
  201. /// Fetch known balances from the wallet and try to print them as a table.
  202. pub async fn wallet_balance(&self) -> Result<()> {
  203. // This represents "false"
  204. let is_spent = 0;
  205. let query = format!(
  206. "SELECT {}, {} FROM {} WHERE {} = {}",
  207. MONEY_COINS_COL_VALUE,
  208. MONEY_COINS_COL_TOKEN_ID,
  209. MONEY_COINS_TABLE,
  210. MONEY_COINS_COL_IS_SPENT,
  211. is_spent,
  212. );
  213. let params = json!([
  214. query,
  215. QueryType::Blob as u8,
  216. MONEY_COINS_COL_VALUE,
  217. QueryType::Blob as u8,
  218. MONEY_COINS_COL_TOKEN_ID,
  219. ]);
  220. let req = JsonRequest::new("wallet.query_row_multi", params);
  221. let rep = self.rpc_client.request(req).await?;
  222. // The returned thing should be an array of found rows.
  223. let Some(rows) = rep.as_array() else {
  224. return Err(anyhow!("Unexpected response from darkfid: {}", rep))
  225. };
  226. // Fill this map with balances, and in the end we'll print it as a table.
  227. let mut balmap: HashMap<String, u64> = HashMap::new();
  228. // Let's scan through the rows and see if we got anything.
  229. for row in rows {
  230. let Some(row) = row.as_array() else {
  231. return Err(anyhow!("Unexpected response from darkfid: {}", rep))
  232. };
  233. if row.len() != 2 {
  234. eprintln!("Error: Got invalid array, row should contain two elements.");
  235. eprintln!("Actual contents:\n:{:#?}", row);
  236. return Err(anyhow!("Unexpected response from darkfid: {}", rep))
  237. }
  238. let value_bytes: Vec<u8> = serde_json::from_value(row[0].clone())?;
  239. let mut value: u64 = deserialize(&value_bytes)?;
  240. let token_bytes: Vec<u8> = serde_json::from_value(row[1].clone())?;
  241. let token_id: TokenId = deserialize(&token_bytes)?;
  242. let token_id = format!("{}", token_id);
  243. if let Some(prev) = balmap.get(&token_id) {
  244. value += prev;
  245. }
  246. balmap.insert(token_id, value);
  247. }
  248. // Create a prettytable with the new data.
  249. let mut table = Table::new();
  250. table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
  251. table.set_titles(row!["Token ID", "Balance"]);
  252. for (token_id, balance) in balmap.iter() {
  253. // FIXME: Don't hardcode to 8 decimals
  254. table.add_row(row![token_id, encode_base10(*balance, 8)]);
  255. }
  256. if table.is_empty() {
  257. println!("No unspent balances found");
  258. } else {
  259. println!("{}", table);
  260. }
  261. Ok(())
  262. }
  263. /// Fetch pubkeys from the wallet and print the requested index.
  264. pub async fn wallet_address(&self, _idx: u64) -> Result<PublicKey> {
  265. let query = format!("SELECT {} FROM {};", MONEY_KEYS_COL_PUBLIC, MONEY_KEYS_TABLE);
  266. let params = json!([query, QueryType::Blob as u8, MONEY_KEYS_COL_PUBLIC]);
  267. let req = JsonRequest::new("wallet.query_row_single", params);
  268. let rep = self.rpc_client.request(req).await?;
  269. let Some(arr) = rep.as_array() else {
  270. return Err(anyhow!("Unexpected response from darkfid: {}", rep));
  271. };
  272. if arr.len() != 1 {
  273. return Err(anyhow!("Unexpected response from darkfid: {}", rep))
  274. }
  275. let key_bytes: Vec<u8> = serde_json::from_value(arr[0].clone())?;
  276. let public_key: PublicKey = deserialize(&key_bytes)?;
  277. Ok(public_key)
  278. }
  279. /// Fetch secret keys from the wallet and return them if found.
  280. pub async fn wallet_secrets(&self) -> Result<Vec<SecretKey>> {
  281. let query = format!("SELECT {} FROM {};", MONEY_KEYS_COL_SECRET, MONEY_KEYS_TABLE);
  282. let params = json!([query, QueryType::Blob as u8, MONEY_KEYS_COL_SECRET]);
  283. let req = JsonRequest::new("wallet.query_row_multi", params);
  284. let rep = self.rpc_client.request(req).await?;
  285. // The returned thing should be an array of found rows.
  286. let Some(rows) = rep.as_array() else {
  287. return Err(anyhow!("Unexpected response from darkfid: {}", rep))
  288. };
  289. let mut secrets = vec![];
  290. // Let's scan through the rows and see if we got anything.
  291. for row in rows {
  292. let secret_bytes: Vec<u8> = serde_json::from_value(row[0].clone())?;
  293. let secret: SecretKey = deserialize(&secret_bytes)?;
  294. secrets.push(secret);
  295. }
  296. Ok(secrets)
  297. }
  298. /// Get the Merkle tree from the wallet
  299. pub async fn wallet_tree(&self) -> Result<BridgeTree<MerkleNode, MERKLE_DEPTH>> {
  300. let query = format!("SELECT * FROM {}", MONEY_TREE_TABLE);
  301. let params = json!([query, QueryType::Blob as u8, MONEY_TREE_COL_TREE]);
  302. let req = JsonRequest::new("wallet.query_row_single", params);
  303. let rep = self.rpc_client.request(req).await?;
  304. let tree_bytes: Vec<u8> = serde_json::from_value(rep[0].clone())?;
  305. let tree = deserialize(&tree_bytes)?;
  306. Ok(tree)
  307. }
  308. /// Get the last scanned slot from the wallet
  309. pub async fn wallet_last_scanned_slot(&self) -> Result<u64> {
  310. let query =
  311. format!("SELECT {} FROM {};", MONEY_INFO_COL_LAST_SCANNED_SLOT, MONEY_INFO_TABLE);
  312. let params = json!([query, QueryType::Integer as u8, MONEY_INFO_COL_LAST_SCANNED_SLOT]);
  313. let req = JsonRequest::new("wallet.query_row_single", params);
  314. let rep = self.rpc_client.request(req).await?;
  315. Ok(serde_json::from_value(rep[0].clone())?)
  316. }
  317. /// Mark a coin in the wallet as spent
  318. pub async fn mark_spent_coin(&self, coin: &Coin) -> Result<()> {
  319. let query = format!(
  320. "UPDATE {} SET {} = ?1 WHERE {} = ?2;",
  321. MONEY_COINS_TABLE, MONEY_COINS_COL_IS_SPENT, MONEY_COINS_COL_COIN
  322. );
  323. let params = json!([
  324. query,
  325. QueryType::Integer as u8,
  326. 1,
  327. QueryType::Blob as u8,
  328. serialize(&coin.inner())
  329. ]);
  330. let req = JsonRequest::new("wallet.exec_sql", params);
  331. let _ = self.rpc_client.request(req).await?;
  332. Ok(())
  333. }
  334. /// Mark a given coin in the wallet as unspent
  335. pub async fn unspend_coin(&self, coin: &Coin) -> Result<()> {
  336. let query = format!(
  337. "UPDATE {} SET {} = ?1 WHERE {} = ?2;",
  338. MONEY_COINS_TABLE, MONEY_COINS_COL_IS_SPENT, MONEY_COINS_COL_COIN
  339. );
  340. let params = json!([
  341. query,
  342. QueryType::Integer as u8,
  343. 0,
  344. QueryType::Blob as u8,
  345. serialize(&coin.inner())
  346. ]);
  347. let req = JsonRequest::new("wallet.exec_sql", params);
  348. let _ = self.rpc_client.request(req).await?;
  349. Ok(())
  350. }
  351. /// Replace the Merkle tree in the wallet
  352. pub async fn put_tree(&self, tree: &BridgeTree<MerkleNode, MERKLE_DEPTH>) -> Result<()> {
  353. let query = format!(
  354. "DELETE FROM {}; INSERT INTO {} ({}) VALUES (?1);",
  355. MONEY_TREE_TABLE, MONEY_TREE_TABLE, MONEY_TREE_COL_TREE
  356. );
  357. let params = json!([query, QueryType::Blob as u8, serialize(tree)]);
  358. let req = JsonRequest::new("wallet.exec_sql", params);
  359. let _ = self.rpc_client.request(req).await?;
  360. Ok(())
  361. }
  362. }