walletdb.rs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  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::{path::Path, str::FromStr, time::Duration};
  19. use async_std::{fs::create_dir_all, sync::Arc};
  20. use log::{debug, error, info, LevelFilter};
  21. use sqlx::{
  22. sqlite::{SqliteConnectOptions, SqliteJournalMode},
  23. ConnectOptions, SqlitePool,
  24. };
  25. use crate::{util::path::expand_path, Error, Result};
  26. pub type WalletPtr = Arc<WalletDb>;
  27. /// Helper function to initialize `WalletPtr`
  28. pub async fn init_wallet(wallet_path: &str, wallet_pass: &str) -> Result<WalletPtr> {
  29. let expanded = expand_path(wallet_path)?;
  30. let wallet_path = format!("sqlite://{}", expanded.to_str().unwrap());
  31. let wallet = WalletDb::new(&wallet_path, wallet_pass).await?;
  32. Ok(wallet)
  33. }
  34. /// Types we want to allow to query from the SQL wallet
  35. #[repr(u8)]
  36. pub enum QueryType {
  37. /// Integer gets decoded into u64
  38. Integer = 0x00,
  39. /// Blob gets decoded into Vec<u8>
  40. Blob = 0x01,
  41. /// OptionInteger gets decoded into Option<u64>
  42. OptionInteger = 0x02,
  43. /// OptionBlob gets decoded into Option<Vec<u8>>
  44. OptionBlob = 0x03,
  45. /// Last type, increment this when you add new types.
  46. Last = 0x04,
  47. }
  48. impl From<u8> for QueryType {
  49. fn from(x: u8) -> Self {
  50. match x {
  51. 0x00 => Self::Integer,
  52. 0x01 => Self::Blob,
  53. 0x02 => Self::OptionInteger,
  54. 0x03 => Self::OptionBlob,
  55. _ => unimplemented!(),
  56. }
  57. }
  58. }
  59. /// Structure representing base wallet operations.
  60. /// Additional operations can be implemented by trait extensions.
  61. pub struct WalletDb {
  62. pub conn: SqlitePool,
  63. }
  64. impl WalletDb {
  65. pub async fn new(path: &str, password: &str) -> Result<WalletPtr> {
  66. if password.trim().is_empty() {
  67. error!(target: "wallet::walletdb", "Wallet password is empty. You must set a password to use the wallet.");
  68. return Err(Error::WalletEmptyPassword)
  69. }
  70. if path != "sqlite::memory:" {
  71. let p = Path::new(path.strip_prefix("sqlite://").unwrap());
  72. if let Some(dirname) = p.parent() {
  73. info!(target: "wallet::walletdb", "Creating path to wallet database: {}", dirname.display());
  74. create_dir_all(&dirname).await?;
  75. }
  76. }
  77. let mut connect_opts = SqliteConnectOptions::from_str(path)?
  78. //.pragma("key", password.to_string())
  79. .create_if_missing(true)
  80. .journal_mode(SqliteJournalMode::Off);
  81. connect_opts.log_statements(LevelFilter::Trace);
  82. connect_opts.log_slow_statements(LevelFilter::Trace, Duration::from_micros(10));
  83. let conn = SqlitePool::connect_with(connect_opts).await?;
  84. info!(target: "wallet::walletdb", "Opened wallet Sqlite connection at path {}", path);
  85. Ok(Arc::new(WalletDb { conn }))
  86. }
  87. /// This function executes a given SQL query, but isn't able to return anything.
  88. /// Therefore it's best to use it for initializing a table or similar things.
  89. pub async fn exec_sql(&self, query: &str) -> Result<()> {
  90. info!(target: "wallet::walletdb", "walletdb: Executing SQL query");
  91. debug!(target: "wallet::walletdb", "\n{}", query);
  92. let mut conn = self.conn.acquire().await?;
  93. sqlx::query(query).execute(&mut conn).await?;
  94. Ok(())
  95. }
  96. }