walletdb.rs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  1. use std::path::Path;
  2. use async_std::sync::{Arc, Mutex};
  3. use log::{debug, error, info};
  4. use pasta_curves::arithmetic::Field;
  5. use rand::rngs::OsRng;
  6. use rusqlite::{named_params, params, Connection};
  7. use super::WalletApi;
  8. use crate::{
  9. client::ClientFailed,
  10. crypto::{coin::Coin, note::Note, nullifier::Nullifier, OwnCoin, OwnCoins},
  11. serial,
  12. types::*,
  13. Error, Result,
  14. };
  15. pub type WalletPtr = Arc<WalletDb>;
  16. #[derive(Debug, Clone)]
  17. pub struct Keypair {
  18. pub public: DrkPublicKey,
  19. pub private: DrkSecretKey,
  20. }
  21. #[derive(Debug, Clone)]
  22. pub struct Balance {
  23. pub token_id: DrkTokenId,
  24. pub value: u64,
  25. pub nullifier: Nullifier,
  26. }
  27. #[derive(Debug, Clone)]
  28. pub struct Balances {
  29. pub list: Vec<Balance>,
  30. }
  31. impl Balances {
  32. pub fn add(&mut self, balance: &Balance) {
  33. if let Some(mut saved_balance) =
  34. self.list.iter_mut().find(|b| b.token_id == balance.token_id)
  35. {
  36. saved_balance.value += balance.value;
  37. } else {
  38. self.list.push(balance.clone());
  39. }
  40. }
  41. }
  42. pub struct WalletDb {
  43. pub conn: Mutex<Connection>,
  44. }
  45. impl WalletApi for WalletDb {}
  46. impl WalletDb {
  47. pub fn new(path: &Path, password: String) -> Result<WalletPtr> {
  48. debug!(target: "WALLETDB", "new() Constructor called");
  49. if password.trim().is_empty() {
  50. error!(target: "WALLETDB", "Password is empty. You must set a password to use the wallet.");
  51. return Err(Error::from(ClientFailed::EmptyPassword))
  52. }
  53. let conn = Connection::open(path)?;
  54. conn.pragma_update(None, "key", &password)?;
  55. info!(target: "WALLETDB", "Opened connection at path: {:?}", path);
  56. Ok(Arc::new(Self { conn: Mutex::new(conn) }))
  57. }
  58. pub async fn init_db(&self) -> Result<()> {
  59. debug!(target: "WALLETDB", "Initialize...");
  60. let contents = include_str!("../../sql/schema.sql");
  61. let conn = self.conn.lock().await;
  62. Ok(conn.execute_batch(contents)?)
  63. }
  64. pub async fn key_gen(&self) -> Result<()> {
  65. debug!(target: "WALLETDB", "Attempting to generate keys...");
  66. let conn = self.conn.lock().await;
  67. let mut stmt = conn.prepare("SELECT * FROM keys WHERE key_id > ?")?;
  68. let key_check = stmt.exists(params!["0"])?;
  69. if !key_check {
  70. let secret = DrkSecretKey::random(&mut OsRng);
  71. let public = derive_public_key(secret);
  72. self.put_keypair(&public, &secret).await?;
  73. return Ok(())
  74. }
  75. error!(target: "WALLETDB", "Keys already exist.");
  76. Err(Error::from(ClientFailed::KeyExists))
  77. }
  78. pub async fn put_keypair(
  79. &self,
  80. key_public: &DrkPublicKey,
  81. key_private: &DrkSecretKey,
  82. ) -> Result<()> {
  83. debug!(target: "WALLETDB", "put_keypair()");
  84. let key_public = serial::serialize(key_public);
  85. let key_private = serial::serialize(key_private);
  86. let conn = self.conn.lock().await;
  87. conn.execute(
  88. "INSERT INTO keys(key_public, key_private) VALUES (?1, ?2)",
  89. params![key_public, key_private],
  90. )?;
  91. Ok(())
  92. }
  93. pub async fn get_keypairs(&self) -> Result<Vec<Keypair>> {
  94. debug!(target: "WALLETDB", "Returning keypairs...");
  95. let conn = self.conn.lock().await;
  96. let mut stmt = conn.prepare("SELECT * FROM keys")?;
  97. // this just gets the first key. maybe we should randomize this
  98. let key_iter = stmt.query_map([], |row| Ok((row.get(1)?, row.get(2)?)))?;
  99. let mut keypairs = Vec::new();
  100. for key in key_iter {
  101. let key = key?;
  102. let public = key.0;
  103. let private = key.1;
  104. let public: DrkPublicKey = self.get_value_deserialized(public)?;
  105. let private: DrkSecretKey = self.get_value_deserialized(private)?;
  106. keypairs.push(Keypair { public, private });
  107. }
  108. Ok(keypairs)
  109. }
  110. pub async fn get_own_coins(&self) -> Result<OwnCoins> {
  111. debug!(target: "WALLETDB", "Get own coins");
  112. let is_spent = 0;
  113. let conn = self.conn.lock().await;
  114. let mut coins = conn.prepare("SELECT * FROM coins WHERE is_spent = :is_spent ;")?;
  115. let rows = coins.query_map(&[(":is_spent", &is_spent)], |row| {
  116. Ok((
  117. row.get(0)?,
  118. row.get(1)?,
  119. row.get(2)?,
  120. row.get(3)?,
  121. row.get(4)?,
  122. row.get(5)?,
  123. // TODO: row.get(6)?,
  124. row.get(7)?,
  125. row.get(9)?,
  126. ))
  127. })?;
  128. let mut own_coins = Vec::new();
  129. for row in rows {
  130. let row = row?;
  131. let coin = self.get_value_deserialized(row.0)?;
  132. // note
  133. let serial = self.get_value_deserialized(row.1)?;
  134. let coin_blind = self.get_value_deserialized(row.2)?;
  135. let value_blind = self.get_value_deserialized(row.3)?;
  136. let value: u64 = row.4;
  137. let token_id = self.get_value_deserialized(row.5)?;
  138. let note = Note { serial, value, token_id, coin_blind, value_blind };
  139. // TODO:
  140. // let witness = self.get_value_deserialized(row.6)?;
  141. // let secret: DrkSecretKey = self.get_value_deserialized(row.7)?;
  142. // let nullifier: Nullifier = self.get_value_deserialized(row.8)?;
  143. let secret: DrkSecretKey = self.get_value_deserialized(row.6)?;
  144. let nullifier: Nullifier = self.get_value_deserialized(row.7)?;
  145. let oc = OwnCoin {
  146. coin,
  147. note,
  148. secret,
  149. // TODO: witness,
  150. nullifier,
  151. };
  152. own_coins.push(oc)
  153. }
  154. Ok(own_coins)
  155. }
  156. pub async fn put_own_coins(&self, own_coin: OwnCoin) -> Result<()> {
  157. debug!(target: "WALLETDB", "Put own coins");
  158. let coin = self.get_value_serialized(&own_coin.coin.to_bytes())?;
  159. let serial = self.get_value_serialized(&own_coin.note.serial)?;
  160. let coin_blind = self.get_value_serialized(&own_coin.note.coin_blind)?;
  161. let value_blind = self.get_value_serialized(&own_coin.note.value_blind)?;
  162. let value: u64 = own_coin.note.value;
  163. let token_id = self.get_value_serialized(&own_coin.note.token_id)?;
  164. // TODO: let witness = self.get_value_serialized(&own_coin.witness)?;
  165. let secret = self.get_value_serialized(&own_coin.secret)?;
  166. let is_spent = 0;
  167. let nullifier = self.get_value_serialized(&own_coin.nullifier)?;
  168. let conn = self.conn.lock().await;
  169. conn.execute(
  170. "INSERT OR REPLACE INTO coins
  171. (coin, serial, value, token_id, coin_blind,
  172. valcom_blind, witness, secret, is_spent, nullifier)
  173. VALUES
  174. (:coin, :serial, :value, :token_id, :coin_blind,
  175. :valcom_blind, :witness, :secret, :is_spent, :nullifier);",
  176. named_params! {
  177. ":coin": coin,
  178. ":serial": serial,
  179. ":value": value,
  180. ":token_id": token_id,
  181. ":coin_blind": coin_blind,
  182. ":valcom_blind": value_blind,
  183. // TODO: ":witness": witness,
  184. ":secret": secret,
  185. ":is_spent": is_spent,
  186. ":nullifier": nullifier,
  187. },
  188. )?;
  189. Ok(())
  190. }
  191. pub async fn remove_own_coins(&self) -> Result<()> {
  192. debug!(target: "WALLETDB", "Remove own coins");
  193. let conn = self.conn.lock().await;
  194. let _rows = conn.execute("DROP TABLE coins;", [])?;
  195. Ok(())
  196. }
  197. pub async fn confirm_spend_coin(&self, coin: &Coin) -> Result<()> {
  198. debug!(target: "WALLETDB", "Confirm spend coin");
  199. let is_spent = 1;
  200. let coin = self.get_value_serialized(coin)?;
  201. let conn = self.conn.lock().await;
  202. conn.execute(
  203. "UPDATE coins
  204. SET is_spent = ?1
  205. WHERE coin = ?2 ;",
  206. params![is_spent, coin],
  207. )?;
  208. Ok(())
  209. }
  210. /* TODO:
  211. pub fn get_witnesses(&self) -> Result<HashMap<Vec<u8>, IncrementalWitness<MerkleNode>>> {
  212. let conn = Connection::open(&self.path)?;
  213. conn.pragma_update(None, "key", &self.password)?;
  214. let is_spent = 0;
  215. let mut witnesses =
  216. conn.prepare("SELECT coin, witness FROM coins WHERE is_spent = :is_spent;")?;
  217. let rows = witnesses.query_map(&[(":is_spent", &is_spent)], |row| {
  218. Ok((row.get(0)?, row.get(1)?))
  219. })?;
  220. let mut witnesses = HashMap::new();
  221. for i in rows {
  222. let i = i?;
  223. let coin: Vec<u8> = i.0;
  224. let witness: IncrementalWitness<MerkleNode> = self.get_value_deserialized(i.1)?;
  225. witnesses.insert(coin, witness);
  226. }
  227. Ok(witnesses)
  228. }
  229. pub fn update_witnesses(
  230. &self,
  231. witnesses: HashMap<Vec<u8>, IncrementalWitness<MerkleNode>>,
  232. ) -> Result<()> {
  233. debug!(target: "WALLETDB", "Updating witness");
  234. let conn = Connection::open(&self.path)?;
  235. conn.pragma_update(None, "key", &self.password)?;
  236. for (coin, witness) in witnesses.iter() {
  237. let witness = self.get_value_serialized(witness)?;
  238. let is_spent = 0;
  239. conn.execute(
  240. "UPDATE coins SET witness = ?1 WHERE coin = ?2 AND is_spent = ?3",
  241. params![witness, coin, is_spent],
  242. )?;
  243. }
  244. Ok(())
  245. }
  246. */
  247. pub async fn get_balances(&self) -> Result<Balances> {
  248. debug!(target: "WALLETDB", "Get token and balances...");
  249. let is_spent = 0;
  250. let conn = self.conn.lock().await;
  251. let mut stmt = conn.prepare(
  252. "SELECT value, token_id, nullifier FROM coins WHERE is_spent = :is_spent ;",
  253. )?;
  254. let rows = stmt.query_map(&[(":is_spent", &is_spent)], |row| {
  255. Ok((row.get(0)?, row.get(1)?, row.get(2)?))
  256. })?;
  257. let mut balances = Balances { list: Vec::new() };
  258. for row in rows {
  259. let row = row?;
  260. let value: u64 = row.0;
  261. let token_id: DrkTokenId = self.get_value_deserialized(row.1)?;
  262. let nullifier: Nullifier = self.get_value_deserialized(row.2)?;
  263. balances.add(&Balance { token_id, value, nullifier });
  264. }
  265. Ok(balances)
  266. }
  267. pub async fn get_token_id(&self) -> Result<Vec<DrkTokenId>> {
  268. debug!(target: "WALLETDB", "Get token ID...");
  269. let is_spent = 0;
  270. let conn = self.conn.lock().await;
  271. let mut stmt = conn.prepare("SELECT token_id FROM coins WHERE is_spent = :is_spent ;")?;
  272. let rows = stmt.query_map(&[(":is_spent", &is_spent)], |row| row.get(0))?;
  273. let mut token_ids = Vec::new();
  274. for row in rows {
  275. let row = row?;
  276. let token_id = self.get_value_deserialized(row).unwrap();
  277. token_ids.push(token_id);
  278. }
  279. Ok(token_ids)
  280. }
  281. pub async fn token_id_exists(&self, token_id: &DrkTokenId) -> Result<bool> {
  282. debug!(target: "WALLETDB", "Check tokenID exists");
  283. let is_spent = 0;
  284. let id = self.get_value_serialized(token_id)?;
  285. let conn = self.conn.lock().await;
  286. let mut stmt = conn.prepare("SELECT * FROM coins WHERE token_id = ? AND is_spent = ? ;")?;
  287. let id_check = stmt.exists(params![id, is_spent])?;
  288. Ok(id_check)
  289. }
  290. pub async fn test_wallet(&self) -> Result<()> {
  291. let conn = self.conn.lock().await;
  292. let mut stmt = conn.prepare("SELECT * FROM keys")?;
  293. let _rows = stmt.query([])?;
  294. Ok(())
  295. }
  296. }
  297. #[cfg(test)]
  298. mod tests {
  299. // TODO: Clean up, there's a lot of duplicated code here.
  300. use super::*;
  301. use crate::{
  302. crypto::{
  303. coin::Coin,
  304. types::{derive_public_key, CoinBlind, NullifierSerial, ValueCommitBlind},
  305. OwnCoin,
  306. },
  307. util::join_config_path,
  308. };
  309. use ff::PrimeField;
  310. pub fn init_db(path: &Path, password: String) -> Result<()> {
  311. if !password.trim().is_empty() {
  312. let contents = include_str!("../../sql/schema.sql");
  313. let conn = Connection::open(path)?;
  314. debug!(target: "WALLETDB", "OPENED CONNECTION AT PATH {:?}", path);
  315. conn.pragma_update(None, "key", &password)?;
  316. conn.execute_batch(contents)?;
  317. } else {
  318. debug!(
  319. target: "WALLETDB", "Password is empty. You must set a password to use the wallet."
  320. );
  321. return Err(Error::from(ClientFailed::EmptyPassword))
  322. }
  323. Ok(())
  324. }
  325. #[test]
  326. pub fn test_get_token_id() -> Result<()> {
  327. let walletdb_path = join_config_path(&PathBuf::from("test_wallet.db"))?;
  328. let password: String = "darkfi".into();
  329. let wallet = WalletDb::new(&walletdb_path, password.clone())?;
  330. init_db(&walletdb_path, password)?;
  331. let secret = DrkSecretKey::random(&mut OsRng);
  332. let public = secret.derive_public_key();
  333. wallet.put_keypair(&public, &secret)?;
  334. let token_id = DrkTokenId::random(&mut OsRng);
  335. let note = Note {
  336. serial: NullifierSerial::random(&mut OsRng),
  337. value: 110,
  338. token_id,
  339. coin_blind: CoinBlind::random(&mut OsRng),
  340. valcom_blind: ValueCommitBlind::random(&mut OsRng),
  341. };
  342. let coin = Coin::new(bls12_381::Scalar::random(&mut OsRng).to_repr());
  343. let mut tree = crate::crypto::merkle::CommitmentTree::empty();
  344. tree.append(MerkleNode::from_coin(&coin))?;
  345. let witness = IncrementalWitness::from_tree(&tree);
  346. let nullifier = Nullifier::new(coin.repr);
  347. let own_coin = OwnCoin { coin, note, secret, witness, nullifier };
  348. wallet.put_own_coins(own_coin.clone())?;
  349. wallet.put_own_coins(own_coin.clone())?;
  350. wallet.put_own_coins(own_coin.clone())?;
  351. wallet.put_own_coins(own_coin)?;
  352. let id = wallet.get_token_id()?;
  353. assert_eq!(id.len(), 1);
  354. for i in id {
  355. assert_eq!(i, token_id);
  356. assert!(wallet.token_id_exists(&i)?);
  357. }
  358. std::fs::remove_file(walletdb_path)?;
  359. Ok(())
  360. }
  361. #[test]
  362. pub fn test_get_balances() -> Result<()> {
  363. let walletdb_path = join_config_path(&PathBuf::from("test2_wallet.db"))?;
  364. let password: String = "darkfi".into();
  365. let wallet = WalletDb::new(&walletdb_path, password.clone())?;
  366. init_db(&walletdb_path, password)?;
  367. let secret = DrkSecretKey::random(&mut OsRng);
  368. let public = secret.derive_public_key();
  369. wallet.put_keypair(&public, &secret)?;
  370. let token_id = DrkTokenId::random(&mut OsRng);
  371. let note = Note {
  372. serial: NullifierSerial::random(&mut OsRng),
  373. value: 110,
  374. token_id,
  375. coin_blind: CoinBlind::random(&mut OsRng),
  376. valcom_blind: ValueCommitBlind::random(&mut OsRng),
  377. };
  378. let coin = Coin::new(bls12_381::Scalar::random(&mut OsRng).to_repr());
  379. let mut tree = crate::crypto::merkle::CommitmentTree::empty();
  380. tree.append(MerkleNode::from_coin(&coin))?;
  381. let witness = IncrementalWitness::from_tree(&tree);
  382. let nullifier = Nullifier::new(coin.repr);
  383. let own_coin = OwnCoin { coin, note, secret, witness, nullifier };
  384. wallet.put_own_coins(own_coin.clone())?;
  385. wallet.put_own_coins(own_coin.clone())?;
  386. wallet.put_own_coins(own_coin.clone())?;
  387. wallet.put_own_coins(own_coin)?;
  388. let balances = wallet.get_balances()?;
  389. assert_eq!(balances.list.len(), 1);
  390. assert_eq!(balances.list[0].value, 110);
  391. assert_eq!(balances.list[0].token_id, token_id);
  392. std::fs::remove_file(walletdb_path)?;
  393. Ok(())
  394. }
  395. #[test]
  396. pub fn test_save_and_load_keypair() -> Result<()> {
  397. let walletdb_path = join_config_path(&PathBuf::from("test3_wallet.db"))?;
  398. let password: String = "darkfi".into();
  399. let wallet = WalletDb::new(&walletdb_path, password.clone())?;
  400. init_db(&walletdb_path, password)?;
  401. let secret = DrkSecretKey::random(&mut OsRng);
  402. let public = secret.derive_public_key();
  403. wallet.put_keypair(&public, &secret)?;
  404. let keypair = wallet.get_keypairs()?[0].clone();
  405. assert_eq!(public, keypair.public);
  406. assert_eq!(secret, keypair.private);
  407. std::fs::remove_file(walletdb_path)?;
  408. Ok(())
  409. }
  410. #[test]
  411. pub fn test_put_and_get_own_coins() -> Result<()> {
  412. let walletdb_path = join_config_path(&PathBuf::from("test4_wallet.db"))?;
  413. let password: String = "darkfi".into();
  414. let wallet = WalletDb::new(&walletdb_path, password.clone())?;
  415. init_db(&walletdb_path, password)?;
  416. let secret = DrkSecretKey::random(&mut OsRng);
  417. let public = secret.derive_public_key();
  418. wallet.put_keypair(&public, &secret)?;
  419. let note = Note {
  420. serial: NullifierSerial::random(&mut OsRng),
  421. value: 110,
  422. token_id: DrkTokenId::random(&mut OsRng),
  423. coin_blind: CoinBlind::random(&mut OsRng),
  424. valcom_blind: ValueCommitBlind::random(&mut OsRng),
  425. };
  426. let coin = Coin::new(bls12_381::Scalar::random(&mut OsRng).to_repr());
  427. let mut tree = crate::crypto::merkle::CommitmentTree::empty();
  428. tree.append(MerkleNode::from_coin(&coin))?;
  429. let witness = IncrementalWitness::from_tree(&tree);
  430. let coin_ser = crate::serial::serialize(&coin.repr);
  431. assert_eq!(coin, crate::serial::deserialize(&coin_ser)?);
  432. let nullifier = Nullifier::new(coin.repr);
  433. let own_coin = OwnCoin {
  434. coin,
  435. note: note.clone(),
  436. secret,
  437. witness: witness.clone(),
  438. nullifier: nullifier.clone(),
  439. };
  440. wallet.put_own_coins(own_coin)?;
  441. let own_coin = wallet.get_own_coins()?[0].clone();
  442. assert_eq!(&own_coin.note.valcom_blind, &note.valcom_blind);
  443. assert_eq!(&own_coin.note.coin_blind, &note.coin_blind);
  444. assert_eq!(own_coin.secret, secret);
  445. assert_eq!(own_coin.witness.root(), witness.root());
  446. assert_eq!(own_coin.witness.path(), witness.path());
  447. assert_eq!(own_coin.nullifier, nullifier);
  448. wallet.confirm_spend_coin(&own_coin.coin)?;
  449. let own_coins = wallet.get_own_coins()?;
  450. assert_eq!(own_coins.len(), 0);
  451. wallet.put_own_coins(own_coin)?;
  452. let own_coins = wallet.get_own_coins()?;
  453. assert_eq!(own_coins.len(), 1);
  454. wallet.remove_own_coins()?;
  455. std::fs::remove_file(walletdb_path)?;
  456. Ok(())
  457. }
  458. #[test]
  459. pub fn test_get_witnesses_and_update_them() -> Result<()> {
  460. let walletdb_path = join_config_path(&PathBuf::from("test5_wallet.db"))?;
  461. let password: String = "darkfi".into();
  462. let wallet = WalletDb::new(&walletdb_path, password.clone())?;
  463. init_db(&walletdb_path, password)?;
  464. let secret = DrkSecretKey::random(&mut OsRng);
  465. let public = secret.derive_public_key();
  466. wallet.put_keypair(&public, &secret)?;
  467. let mut tree = crate::crypto::merkle::CommitmentTree::empty();
  468. let note = Note {
  469. serial: NullifierSerial::random(&mut OsRng),
  470. value: 110,
  471. token_id: DrkTokenId::random(&mut OsRng),
  472. coin_blind: CoinBlind::random(&mut OsRng),
  473. valcom_blind: ValueCommitBlind::random(&mut OsRng),
  474. };
  475. let coin = Coin::new(bls12_381::Scalar::random(&mut OsRng).to_repr());
  476. let node = MerkleNode::from_coin(&coin);
  477. tree.append(node)?;
  478. tree.append(node)?;
  479. tree.append(node)?;
  480. tree.append(node)?;
  481. let witness = IncrementalWitness::from_tree(&tree);
  482. // for testing
  483. let nullifier = Nullifier::new(coin.repr);
  484. let own_coin = OwnCoin { coin, note, secret, witness, nullifier };
  485. wallet.put_own_coins(own_coin.clone())?;
  486. wallet.put_own_coins(own_coin.clone())?;
  487. wallet.put_own_coins(own_coin.clone())?;
  488. wallet.put_own_coins(own_coin)?;
  489. let coin2 = Coin::new(bls12_381::Scalar::random(&mut OsRng).to_repr());
  490. let node2 = MerkleNode::from_coin(&coin2);
  491. tree.append(node2)?;
  492. let mut updated_witnesses = wallet.get_witnesses()?;
  493. updated_witnesses.iter_mut().for_each(|(_, witness)| {
  494. witness.append(node2).expect("Append to witness");
  495. });
  496. wallet.update_witnesses(updated_witnesses)?;
  497. for (_, witness) in wallet.get_witnesses()?.iter() {
  498. assert_eq!(tree.root(), witness.root());
  499. }
  500. std::fs::remove_file(walletdb_path)?;
  501. Ok(())
  502. }
  503. }