mod.rs 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341
  1. //! SQLite Wallet Database
  2. use std::collections::HashMap;
  3. use std::fmt::Debug;
  4. use std::str::FromStr;
  5. use std::sync::Arc;
  6. use async_trait::async_trait;
  7. use cdk_common::common::ProofInfo;
  8. use cdk_common::database::{ConversionError, Error, WalletDatabase};
  9. use cdk_common::mint_url::MintUrl;
  10. use cdk_common::nuts::{MeltQuoteState, MintQuoteState};
  11. use cdk_common::secret::Secret;
  12. use cdk_common::wallet::{self, MintQuote, Transaction, TransactionDirection, TransactionId};
  13. use cdk_common::{
  14. database, Amount, CurrencyUnit, Id, KeySet, KeySetInfo, Keys, MintInfo, PaymentMethod, Proof,
  15. ProofDleq, PublicKey, SecretKey, SpendingConditions, State,
  16. };
  17. use tracing::instrument;
  18. use crate::common::migrate;
  19. use crate::database::{ConnectionWithTransaction, DatabaseExecutor};
  20. use crate::pool::{DatabasePool, Pool, PooledResource};
  21. use crate::stmt::{query, Column};
  22. use crate::{
  23. column_as_binary, column_as_nullable_binary, column_as_nullable_number,
  24. column_as_nullable_string, column_as_number, column_as_string, unpack_into,
  25. };
  26. #[rustfmt::skip]
  27. mod migrations {
  28. include!(concat!(env!("OUT_DIR"), "/migrations_wallet.rs"));
  29. }
  30. /// Wallet SQLite Database
  31. #[derive(Debug, Clone)]
  32. pub struct SQLWalletDatabase<RM>
  33. where
  34. RM: DatabasePool + 'static,
  35. {
  36. pool: Arc<Pool<RM>>,
  37. }
  38. impl<RM> SQLWalletDatabase<RM>
  39. where
  40. RM: DatabasePool + 'static,
  41. {
  42. /// Creates a new instance
  43. pub async fn new<X>(db: X) -> Result<Self, Error>
  44. where
  45. X: Into<RM::Config>,
  46. {
  47. let pool = Pool::new(db.into());
  48. Self::migrate(pool.get().map_err(|e| Error::Database(Box::new(e)))?).await?;
  49. Ok(Self { pool })
  50. }
  51. /// Migrate [`WalletSqliteDatabase`]
  52. async fn migrate(conn: PooledResource<RM>) -> Result<(), Error> {
  53. let tx = ConnectionWithTransaction::new(conn).await?;
  54. migrate(&tx, RM::Connection::name(), migrations::MIGRATIONS).await?;
  55. // Update any existing keys with missing keyset_u32 values
  56. Self::add_keyset_u32(&tx).await?;
  57. tx.commit().await?;
  58. Ok(())
  59. }
  60. async fn add_keyset_u32<T>(conn: &T) -> Result<(), Error>
  61. where
  62. T: DatabaseExecutor,
  63. {
  64. // First get the keysets where keyset_u32 on key is null
  65. let keys_without_u32: Vec<Vec<Column>> = query(
  66. r#"
  67. SELECT
  68. id
  69. FROM key
  70. WHERE keyset_u32 IS NULL
  71. "#,
  72. )?
  73. .fetch_all(conn)
  74. .await?;
  75. for id in keys_without_u32 {
  76. let id = column_as_string!(id.first().unwrap());
  77. if let Ok(id) = Id::from_str(&id) {
  78. query(
  79. r#"
  80. UPDATE
  81. key
  82. SET keyset_u32 = :u32_keyset
  83. WHERE id = :keyset_id
  84. "#,
  85. )?
  86. .bind("u32_keyset", u32::from(id))
  87. .bind("keyset_id", id.to_string())
  88. .execute(conn)
  89. .await?;
  90. }
  91. }
  92. // Also update keysets where keyset_u32 is null
  93. let keysets_without_u32: Vec<Vec<Column>> = query(
  94. r#"
  95. SELECT
  96. id
  97. FROM keyset
  98. WHERE keyset_u32 IS NULL
  99. "#,
  100. )?
  101. .fetch_all(conn)
  102. .await?;
  103. for id in keysets_without_u32 {
  104. let id = column_as_string!(id.first().unwrap());
  105. if let Ok(id) = Id::from_str(&id) {
  106. query(
  107. r#"
  108. UPDATE
  109. keyset
  110. SET keyset_u32 = :u32_keyset
  111. WHERE id = :keyset_id
  112. "#,
  113. )?
  114. .bind("u32_keyset", u32::from(id))
  115. .bind("keyset_id", id.to_string())
  116. .execute(conn)
  117. .await?;
  118. }
  119. }
  120. Ok(())
  121. }
  122. }
  123. #[async_trait]
  124. impl<RM> WalletDatabase for SQLWalletDatabase<RM>
  125. where
  126. RM: DatabasePool + 'static,
  127. {
  128. type Err = database::Error;
  129. #[instrument(skip(self))]
  130. async fn get_melt_quotes(&self) -> Result<Vec<wallet::MeltQuote>, Self::Err> {
  131. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  132. Ok(query(
  133. r#"
  134. SELECT
  135. id,
  136. unit,
  137. amount,
  138. request,
  139. fee_reserve,
  140. state,
  141. expiry,
  142. payment_preimage,
  143. payment_method
  144. FROM
  145. melt_quote
  146. "#,
  147. )?
  148. .fetch_all(&*conn)
  149. .await?
  150. .into_iter()
  151. .map(sql_row_to_melt_quote)
  152. .collect::<Result<_, _>>()?)
  153. }
  154. #[instrument(skip(self, mint_info))]
  155. async fn add_mint(
  156. &self,
  157. mint_url: MintUrl,
  158. mint_info: Option<MintInfo>,
  159. ) -> Result<(), Self::Err> {
  160. let (
  161. name,
  162. pubkey,
  163. version,
  164. description,
  165. description_long,
  166. contact,
  167. nuts,
  168. icon_url,
  169. urls,
  170. motd,
  171. time,
  172. tos_url,
  173. ) = match mint_info {
  174. Some(mint_info) => {
  175. let MintInfo {
  176. name,
  177. pubkey,
  178. version,
  179. description,
  180. description_long,
  181. contact,
  182. nuts,
  183. icon_url,
  184. urls,
  185. motd,
  186. time,
  187. tos_url,
  188. } = mint_info;
  189. (
  190. name,
  191. pubkey.map(|p| p.to_bytes().to_vec()),
  192. version.map(|v| serde_json::to_string(&v).ok()),
  193. description,
  194. description_long,
  195. contact.map(|c| serde_json::to_string(&c).ok()),
  196. serde_json::to_string(&nuts).ok(),
  197. icon_url,
  198. urls.map(|c| serde_json::to_string(&c).ok()),
  199. motd,
  200. time,
  201. tos_url,
  202. )
  203. }
  204. None => (
  205. None, None, None, None, None, None, None, None, None, None, None, None,
  206. ),
  207. };
  208. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  209. query(
  210. r#"
  211. INSERT INTO mint
  212. (
  213. mint_url, name, pubkey, version, description, description_long,
  214. contact, nuts, icon_url, urls, motd, mint_time, tos_url
  215. )
  216. VALUES
  217. (
  218. :mint_url, :name, :pubkey, :version, :description, :description_long,
  219. :contact, :nuts, :icon_url, :urls, :motd, :mint_time, :tos_url
  220. )
  221. ON CONFLICT(mint_url) DO UPDATE SET
  222. name = excluded.name,
  223. pubkey = excluded.pubkey,
  224. version = excluded.version,
  225. description = excluded.description,
  226. description_long = excluded.description_long,
  227. contact = excluded.contact,
  228. nuts = excluded.nuts,
  229. icon_url = excluded.icon_url,
  230. urls = excluded.urls,
  231. motd = excluded.motd,
  232. mint_time = excluded.mint_time,
  233. tos_url = excluded.tos_url
  234. ;
  235. "#,
  236. )?
  237. .bind("mint_url", mint_url.to_string())
  238. .bind("name", name)
  239. .bind("pubkey", pubkey)
  240. .bind("version", version)
  241. .bind("description", description)
  242. .bind("description_long", description_long)
  243. .bind("contact", contact)
  244. .bind("nuts", nuts)
  245. .bind("icon_url", icon_url)
  246. .bind("urls", urls)
  247. .bind("motd", motd)
  248. .bind("mint_time", time.map(|v| v as i64))
  249. .bind("tos_url", tos_url)
  250. .execute(&*conn)
  251. .await?;
  252. Ok(())
  253. }
  254. #[instrument(skip(self))]
  255. async fn remove_mint(&self, mint_url: MintUrl) -> Result<(), Self::Err> {
  256. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  257. query(r#"DELETE FROM mint WHERE mint_url=:mint_url"#)?
  258. .bind("mint_url", mint_url.to_string())
  259. .execute(&*conn)
  260. .await?;
  261. Ok(())
  262. }
  263. #[instrument(skip(self))]
  264. async fn get_mint(&self, mint_url: MintUrl) -> Result<Option<MintInfo>, Self::Err> {
  265. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  266. Ok(query(
  267. r#"
  268. SELECT
  269. name,
  270. pubkey,
  271. version,
  272. description,
  273. description_long,
  274. contact,
  275. nuts,
  276. icon_url,
  277. motd,
  278. urls,
  279. mint_time,
  280. tos_url
  281. FROM
  282. mint
  283. WHERE mint_url = :mint_url
  284. "#,
  285. )?
  286. .bind("mint_url", mint_url.to_string())
  287. .fetch_one(&*conn)
  288. .await?
  289. .map(sql_row_to_mint_info)
  290. .transpose()?)
  291. }
  292. #[instrument(skip(self))]
  293. async fn get_mints(&self) -> Result<HashMap<MintUrl, Option<MintInfo>>, Self::Err> {
  294. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  295. Ok(query(
  296. r#"
  297. SELECT
  298. name,
  299. pubkey,
  300. version,
  301. description,
  302. description_long,
  303. contact,
  304. nuts,
  305. icon_url,
  306. motd,
  307. urls,
  308. mint_time,
  309. tos_url,
  310. mint_url
  311. FROM
  312. mint
  313. "#,
  314. )?
  315. .fetch_all(&*conn)
  316. .await?
  317. .into_iter()
  318. .map(|mut row| {
  319. let url = column_as_string!(
  320. row.pop().ok_or(ConversionError::MissingColumn(0, 1))?,
  321. MintUrl::from_str
  322. );
  323. Ok((url, sql_row_to_mint_info(row).ok()))
  324. })
  325. .collect::<Result<HashMap<_, _>, Error>>()?)
  326. }
  327. #[instrument(skip(self))]
  328. async fn update_mint_url(
  329. &self,
  330. old_mint_url: MintUrl,
  331. new_mint_url: MintUrl,
  332. ) -> Result<(), Self::Err> {
  333. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  334. let tables = ["mint_quote", "proof"];
  335. for table in &tables {
  336. query(&format!(
  337. r#"
  338. UPDATE {table}
  339. SET mint_url = :new_mint_url
  340. WHERE mint_url = :old_mint_url
  341. "#
  342. ))?
  343. .bind("new_mint_url", new_mint_url.to_string())
  344. .bind("old_mint_url", old_mint_url.to_string())
  345. .execute(&*conn)
  346. .await?;
  347. }
  348. Ok(())
  349. }
  350. #[instrument(skip(self, keysets))]
  351. async fn add_mint_keysets(
  352. &self,
  353. mint_url: MintUrl,
  354. keysets: Vec<KeySetInfo>,
  355. ) -> Result<(), Self::Err> {
  356. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  357. for keyset in keysets {
  358. query(
  359. r#"
  360. INSERT INTO keyset
  361. (mint_url, id, unit, active, input_fee_ppk, final_expiry, keyset_u32)
  362. VALUES
  363. (:mint_url, :id, :unit, :active, :input_fee_ppk, :final_expiry, :keyset_u32)
  364. ON CONFLICT(id) DO UPDATE SET
  365. active = excluded.active,
  366. input_fee_ppk = excluded.input_fee_ppk
  367. "#,
  368. )?
  369. .bind("mint_url", mint_url.to_string())
  370. .bind("id", keyset.id.to_string())
  371. .bind("unit", keyset.unit.to_string())
  372. .bind("active", keyset.active)
  373. .bind("input_fee_ppk", keyset.input_fee_ppk as i64)
  374. .bind("final_expiry", keyset.final_expiry.map(|v| v as i64))
  375. .bind("keyset_u32", u32::from(keyset.id))
  376. .execute(&*conn)
  377. .await?;
  378. }
  379. Ok(())
  380. }
  381. #[instrument(skip(self))]
  382. async fn get_mint_keysets(
  383. &self,
  384. mint_url: MintUrl,
  385. ) -> Result<Option<Vec<KeySetInfo>>, Self::Err> {
  386. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  387. let keysets = query(
  388. r#"
  389. SELECT
  390. id,
  391. unit,
  392. active,
  393. input_fee_ppk,
  394. final_expiry
  395. FROM
  396. keyset
  397. WHERE mint_url = :mint_url
  398. "#,
  399. )?
  400. .bind("mint_url", mint_url.to_string())
  401. .fetch_all(&*conn)
  402. .await?
  403. .into_iter()
  404. .map(sql_row_to_keyset)
  405. .collect::<Result<Vec<_>, Error>>()?;
  406. match keysets.is_empty() {
  407. false => Ok(Some(keysets)),
  408. true => Ok(None),
  409. }
  410. }
  411. #[instrument(skip(self), fields(keyset_id = %keyset_id))]
  412. async fn get_keyset_by_id(&self, keyset_id: &Id) -> Result<Option<KeySetInfo>, Self::Err> {
  413. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  414. Ok(query(
  415. r#"
  416. SELECT
  417. id,
  418. unit,
  419. active,
  420. input_fee_ppk,
  421. final_expiry
  422. FROM
  423. keyset
  424. WHERE id = :id
  425. "#,
  426. )?
  427. .bind("id", keyset_id.to_string())
  428. .fetch_one(&*conn)
  429. .await?
  430. .map(sql_row_to_keyset)
  431. .transpose()?)
  432. }
  433. #[instrument(skip_all)]
  434. async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), Self::Err> {
  435. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  436. query(
  437. r#"
  438. INSERT INTO mint_quote
  439. (id, mint_url, amount, unit, request, state, expiry, secret_key, payment_method, amount_issued, amount_paid)
  440. VALUES
  441. (:id, :mint_url, :amount, :unit, :request, :state, :expiry, :secret_key, :payment_method, :amount_issued, :amount_paid)
  442. ON CONFLICT(id) DO UPDATE SET
  443. mint_url = excluded.mint_url,
  444. amount = excluded.amount,
  445. unit = excluded.unit,
  446. request = excluded.request,
  447. state = excluded.state,
  448. expiry = excluded.expiry,
  449. secret_key = excluded.secret_key,
  450. payment_method = excluded.payment_method,
  451. amount_issued = excluded.amount_issued,
  452. amount_paid = excluded.amount_paid
  453. ;
  454. "#,
  455. )?
  456. .bind("id", quote.id.to_string())
  457. .bind("mint_url", quote.mint_url.to_string())
  458. .bind("amount", quote.amount.map(|a| a.to_i64()))
  459. .bind("unit", quote.unit.to_string())
  460. .bind("request", quote.request)
  461. .bind("state", quote.state.to_string())
  462. .bind("expiry", quote.expiry as i64)
  463. .bind("secret_key", quote.secret_key.map(|p| p.to_string()))
  464. .bind("payment_method", quote.payment_method.to_string())
  465. .bind("amount_issued", quote.amount_issued.to_i64())
  466. .bind("amount_paid", quote.amount_paid.to_i64())
  467. .execute(&*conn).await?;
  468. Ok(())
  469. }
  470. #[instrument(skip(self))]
  471. async fn get_mint_quote(&self, quote_id: &str) -> Result<Option<MintQuote>, Self::Err> {
  472. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  473. Ok(query(
  474. r#"
  475. SELECT
  476. id,
  477. mint_url,
  478. amount,
  479. unit,
  480. request,
  481. state,
  482. expiry,
  483. secret_key,
  484. payment_method,
  485. amount_issued,
  486. amount_paid
  487. FROM
  488. mint_quote
  489. WHERE
  490. id = :id
  491. "#,
  492. )?
  493. .bind("id", quote_id.to_string())
  494. .fetch_one(&*conn)
  495. .await?
  496. .map(sql_row_to_mint_quote)
  497. .transpose()?)
  498. }
  499. #[instrument(skip(self))]
  500. async fn get_mint_quotes(&self) -> Result<Vec<MintQuote>, Self::Err> {
  501. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  502. Ok(query(
  503. r#"
  504. SELECT
  505. id,
  506. mint_url,
  507. amount,
  508. unit,
  509. request,
  510. state,
  511. expiry,
  512. secret_key
  513. payment_method,
  514. amount_issued,
  515. amount_paid
  516. FROM
  517. mint_quote
  518. "#,
  519. )?
  520. .fetch_all(&*conn)
  521. .await?
  522. .into_iter()
  523. .map(sql_row_to_mint_quote)
  524. .collect::<Result<_, _>>()?)
  525. }
  526. #[instrument(skip(self))]
  527. async fn remove_mint_quote(&self, quote_id: &str) -> Result<(), Self::Err> {
  528. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  529. query(r#"DELETE FROM mint_quote WHERE id=:id"#)?
  530. .bind("id", quote_id.to_string())
  531. .execute(&*conn)
  532. .await?;
  533. Ok(())
  534. }
  535. #[instrument(skip_all)]
  536. async fn add_melt_quote(&self, quote: wallet::MeltQuote) -> Result<(), Self::Err> {
  537. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  538. query(
  539. r#"
  540. INSERT INTO melt_quote
  541. (id, unit, amount, request, fee_reserve, state, expiry, payment_method)
  542. VALUES
  543. (:id, :unit, :amount, :request, :fee_reserve, :state, :expiry, :payment_method)
  544. ON CONFLICT(id) DO UPDATE SET
  545. unit = excluded.unit,
  546. amount = excluded.amount,
  547. request = excluded.request,
  548. fee_reserve = excluded.fee_reserve,
  549. state = excluded.state,
  550. expiry = excluded.expiry,
  551. payment_method = excluded.payment_method
  552. ;
  553. "#,
  554. )?
  555. .bind("id", quote.id.to_string())
  556. .bind("unit", quote.unit.to_string())
  557. .bind("amount", u64::from(quote.amount) as i64)
  558. .bind("request", quote.request)
  559. .bind("fee_reserve", u64::from(quote.fee_reserve) as i64)
  560. .bind("state", quote.state.to_string())
  561. .bind("expiry", quote.expiry as i64)
  562. .bind("payment_method", quote.payment_method.to_string())
  563. .execute(&*conn)
  564. .await?;
  565. Ok(())
  566. }
  567. #[instrument(skip(self))]
  568. async fn get_melt_quote(&self, quote_id: &str) -> Result<Option<wallet::MeltQuote>, Self::Err> {
  569. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  570. Ok(query(
  571. r#"
  572. SELECT
  573. id,
  574. unit,
  575. amount,
  576. request,
  577. fee_reserve,
  578. state,
  579. expiry,
  580. payment_preimage,
  581. payment_method
  582. FROM
  583. melt_quote
  584. WHERE
  585. id=:id
  586. "#,
  587. )?
  588. .bind("id", quote_id.to_owned())
  589. .fetch_one(&*conn)
  590. .await?
  591. .map(sql_row_to_melt_quote)
  592. .transpose()?)
  593. }
  594. #[instrument(skip(self))]
  595. async fn remove_melt_quote(&self, quote_id: &str) -> Result<(), Self::Err> {
  596. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  597. query(r#"DELETE FROM melt_quote WHERE id=:id"#)?
  598. .bind("id", quote_id.to_owned())
  599. .execute(&*conn)
  600. .await?;
  601. Ok(())
  602. }
  603. #[instrument(skip_all)]
  604. async fn add_keys(&self, keyset: KeySet) -> Result<(), Self::Err> {
  605. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  606. // Recompute ID for verification
  607. keyset.verify_id()?;
  608. query(
  609. r#"
  610. INSERT INTO key
  611. (id, keys, keyset_u32)
  612. VALUES
  613. (:id, :keys, :keyset_u32)
  614. "#,
  615. )?
  616. .bind("id", keyset.id.to_string())
  617. .bind(
  618. "keys",
  619. serde_json::to_string(&keyset.keys).map_err(Error::from)?,
  620. )
  621. .bind("keyset_u32", u32::from(keyset.id))
  622. .execute(&*conn)
  623. .await?;
  624. Ok(())
  625. }
  626. #[instrument(skip(self), fields(keyset_id = %keyset_id))]
  627. async fn get_keys(&self, keyset_id: &Id) -> Result<Option<Keys>, Self::Err> {
  628. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  629. Ok(query(
  630. r#"
  631. SELECT
  632. keys
  633. FROM key
  634. WHERE id = :id
  635. "#,
  636. )?
  637. .bind("id", keyset_id.to_string())
  638. .pluck(&*conn)
  639. .await?
  640. .map(|keys| {
  641. let keys = column_as_string!(keys);
  642. serde_json::from_str(&keys).map_err(Error::from)
  643. })
  644. .transpose()?)
  645. }
  646. #[instrument(skip(self))]
  647. async fn remove_keys(&self, id: &Id) -> Result<(), Self::Err> {
  648. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  649. query(r#"DELETE FROM key WHERE id = :id"#)?
  650. .bind("id", id.to_string())
  651. .pluck(&*conn)
  652. .await?;
  653. Ok(())
  654. }
  655. async fn update_proofs(
  656. &self,
  657. added: Vec<ProofInfo>,
  658. removed_ys: Vec<PublicKey>,
  659. ) -> Result<(), Self::Err> {
  660. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  661. let tx = ConnectionWithTransaction::new(conn).await?;
  662. // TODO: Use a transaction for all these operations
  663. for proof in added {
  664. query(
  665. r#"
  666. INSERT INTO proof
  667. (y, mint_url, state, spending_condition, unit, amount, keyset_id, secret, c, witness, dleq_e, dleq_s, dleq_r)
  668. VALUES
  669. (:y, :mint_url, :state, :spending_condition, :unit, :amount, :keyset_id, :secret, :c, :witness, :dleq_e, :dleq_s, :dleq_r)
  670. ON CONFLICT(y) DO UPDATE SET
  671. mint_url = excluded.mint_url,
  672. state = excluded.state,
  673. spending_condition = excluded.spending_condition,
  674. unit = excluded.unit,
  675. amount = excluded.amount,
  676. keyset_id = excluded.keyset_id,
  677. secret = excluded.secret,
  678. c = excluded.c,
  679. witness = excluded.witness,
  680. dleq_e = excluded.dleq_e,
  681. dleq_s = excluded.dleq_s,
  682. dleq_r = excluded.dleq_r
  683. ;
  684. "#,
  685. )?
  686. .bind("y", proof.y.to_bytes().to_vec())
  687. .bind("mint_url", proof.mint_url.to_string())
  688. .bind("state",proof.state.to_string())
  689. .bind(
  690. "spending_condition",
  691. proof
  692. .spending_condition
  693. .map(|s| serde_json::to_string(&s).ok()),
  694. )
  695. .bind("unit", proof.unit.to_string())
  696. .bind("amount", u64::from(proof.proof.amount) as i64)
  697. .bind("keyset_id", proof.proof.keyset_id.to_string())
  698. .bind("secret", proof.proof.secret.to_string())
  699. .bind("c", proof.proof.c.to_bytes().to_vec())
  700. .bind(
  701. "witness",
  702. proof
  703. .proof
  704. .witness
  705. .map(|w| serde_json::to_string(&w).unwrap()),
  706. )
  707. .bind(
  708. "dleq_e",
  709. proof.proof.dleq.as_ref().map(|dleq| dleq.e.to_secret_bytes().to_vec()),
  710. )
  711. .bind(
  712. "dleq_s",
  713. proof.proof.dleq.as_ref().map(|dleq| dleq.s.to_secret_bytes().to_vec()),
  714. )
  715. .bind(
  716. "dleq_r",
  717. proof.proof.dleq.as_ref().map(|dleq| dleq.r.to_secret_bytes().to_vec()),
  718. )
  719. .execute(&tx).await?;
  720. }
  721. if !removed_ys.is_empty() {
  722. query(r#"DELETE FROM proof WHERE y IN (:ys)"#)?
  723. .bind_vec(
  724. "ys",
  725. removed_ys.iter().map(|y| y.to_bytes().to_vec()).collect(),
  726. )
  727. .execute(&tx)
  728. .await?;
  729. }
  730. tx.commit().await?;
  731. Ok(())
  732. }
  733. #[instrument(skip(self, state, spending_conditions))]
  734. async fn get_proofs(
  735. &self,
  736. mint_url: Option<MintUrl>,
  737. unit: Option<CurrencyUnit>,
  738. state: Option<Vec<State>>,
  739. spending_conditions: Option<Vec<SpendingConditions>>,
  740. ) -> Result<Vec<ProofInfo>, Self::Err> {
  741. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  742. Ok(query(
  743. r#"
  744. SELECT
  745. amount,
  746. unit,
  747. keyset_id,
  748. secret,
  749. c,
  750. witness,
  751. dleq_e,
  752. dleq_s,
  753. dleq_r,
  754. y,
  755. mint_url,
  756. state,
  757. spending_condition
  758. FROM proof
  759. "#,
  760. )?
  761. .fetch_all(&*conn)
  762. .await?
  763. .into_iter()
  764. .filter_map(|row| {
  765. let row = sql_row_to_proof_info(row).ok()?;
  766. if row.matches_conditions(&mint_url, &unit, &state, &spending_conditions) {
  767. Some(row)
  768. } else {
  769. None
  770. }
  771. })
  772. .collect::<Vec<_>>())
  773. }
  774. async fn get_balance(
  775. &self,
  776. mint_url: Option<MintUrl>,
  777. unit: Option<CurrencyUnit>,
  778. states: Option<Vec<State>>,
  779. ) -> Result<u64, Self::Err> {
  780. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  781. let mut query_str = "SELECT COALESCE(SUM(amount), 0) as total FROM proof".to_string();
  782. let mut where_clauses = Vec::new();
  783. let states = states
  784. .unwrap_or_default()
  785. .into_iter()
  786. .map(|x| x.to_string())
  787. .collect::<Vec<_>>();
  788. if mint_url.is_some() {
  789. where_clauses.push("mint_url = :mint_url");
  790. }
  791. if unit.is_some() {
  792. where_clauses.push("unit = :unit");
  793. }
  794. if !states.is_empty() {
  795. where_clauses.push("state IN (:states)");
  796. }
  797. if !where_clauses.is_empty() {
  798. query_str.push_str(" WHERE ");
  799. query_str.push_str(&where_clauses.join(" AND "));
  800. }
  801. let mut q = query(&query_str)?;
  802. if let Some(ref mint_url) = mint_url {
  803. q = q.bind("mint_url", mint_url.to_string());
  804. }
  805. if let Some(ref unit) = unit {
  806. q = q.bind("unit", unit.to_string());
  807. }
  808. if !states.is_empty() {
  809. q = q.bind_vec("states", states);
  810. }
  811. let balance = q
  812. .pluck(&*conn)
  813. .await?
  814. .map(|n| {
  815. // SQLite SUM returns INTEGER which we need to convert to u64
  816. match n {
  817. crate::stmt::Column::Integer(i) => Ok(i as u64),
  818. crate::stmt::Column::Real(f) => Ok(f as u64),
  819. _ => Err(Error::Database(Box::new(std::io::Error::new(
  820. std::io::ErrorKind::InvalidData,
  821. "Invalid balance type",
  822. )))),
  823. }
  824. })
  825. .transpose()?
  826. .unwrap_or(0);
  827. Ok(balance)
  828. }
  829. async fn update_proofs_state(&self, ys: Vec<PublicKey>, state: State) -> Result<(), Self::Err> {
  830. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  831. query("UPDATE proof SET state = :state WHERE y IN (:ys)")?
  832. .bind_vec("ys", ys.iter().map(|y| y.to_bytes().to_vec()).collect())
  833. .bind("state", state.to_string())
  834. .execute(&*conn)
  835. .await?;
  836. Ok(())
  837. }
  838. #[instrument(skip(self), fields(keyset_id = %keyset_id))]
  839. async fn increment_keyset_counter(&self, keyset_id: &Id, count: u32) -> Result<u32, Self::Err> {
  840. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  841. let tx = ConnectionWithTransaction::new(conn).await?;
  842. // Lock the row and get current counter from keyset_counter table
  843. let current_counter = query(
  844. r#"
  845. SELECT counter
  846. FROM keyset_counter
  847. WHERE keyset_id=:keyset_id
  848. FOR UPDATE
  849. "#,
  850. )?
  851. .bind("keyset_id", keyset_id.to_string())
  852. .pluck(&tx)
  853. .await?
  854. .map(|n| Ok::<_, Error>(column_as_number!(n)))
  855. .transpose()?
  856. .unwrap_or(0);
  857. let new_counter = current_counter + count;
  858. // Upsert the new counter value
  859. query(
  860. r#"
  861. INSERT INTO keyset_counter (keyset_id, counter)
  862. VALUES (:keyset_id, :new_counter)
  863. ON CONFLICT(keyset_id) DO UPDATE SET
  864. counter = excluded.counter
  865. "#,
  866. )?
  867. .bind("keyset_id", keyset_id.to_string())
  868. .bind("new_counter", new_counter)
  869. .execute(&tx)
  870. .await?;
  871. tx.commit().await?;
  872. Ok(new_counter)
  873. }
  874. #[instrument(skip(self))]
  875. async fn add_transaction(&self, transaction: Transaction) -> Result<(), Self::Err> {
  876. let mint_url = transaction.mint_url.to_string();
  877. let direction = transaction.direction.to_string();
  878. let unit = transaction.unit.to_string();
  879. let amount = u64::from(transaction.amount) as i64;
  880. let fee = u64::from(transaction.fee) as i64;
  881. let ys = transaction
  882. .ys
  883. .iter()
  884. .flat_map(|y| y.to_bytes().to_vec())
  885. .collect::<Vec<_>>();
  886. let id = transaction.id();
  887. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  888. query(
  889. r#"
  890. INSERT INTO transactions
  891. (id, mint_url, direction, unit, amount, fee, ys, timestamp, memo, metadata, quote_id, payment_request, payment_proof)
  892. VALUES
  893. (:id, :mint_url, :direction, :unit, :amount, :fee, :ys, :timestamp, :memo, :metadata, :quote_id, :payment_request, :payment_proof)
  894. ON CONFLICT(id) DO UPDATE SET
  895. mint_url = excluded.mint_url,
  896. direction = excluded.direction,
  897. unit = excluded.unit,
  898. amount = excluded.amount,
  899. fee = excluded.fee,
  900. timestamp = excluded.timestamp,
  901. memo = excluded.memo,
  902. metadata = excluded.metadata,
  903. quote_id = excluded.quote_id,
  904. payment_request = excluded.payment_request,
  905. payment_proof = excluded.payment_proof
  906. ;
  907. "#,
  908. )?
  909. .bind("id", id.as_slice().to_vec())
  910. .bind("mint_url", mint_url)
  911. .bind("direction", direction)
  912. .bind("unit", unit)
  913. .bind("amount", amount)
  914. .bind("fee", fee)
  915. .bind("ys", ys)
  916. .bind("timestamp", transaction.timestamp as i64)
  917. .bind("memo", transaction.memo)
  918. .bind(
  919. "metadata",
  920. serde_json::to_string(&transaction.metadata).map_err(Error::from)?,
  921. )
  922. .bind("quote_id", transaction.quote_id)
  923. .bind("payment_request", transaction.payment_request)
  924. .bind("payment_proof", transaction.payment_proof)
  925. .execute(&*conn)
  926. .await?;
  927. Ok(())
  928. }
  929. #[instrument(skip(self))]
  930. async fn get_transaction(
  931. &self,
  932. transaction_id: TransactionId,
  933. ) -> Result<Option<Transaction>, Self::Err> {
  934. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  935. Ok(query(
  936. r#"
  937. SELECT
  938. mint_url,
  939. direction,
  940. unit,
  941. amount,
  942. fee,
  943. ys,
  944. timestamp,
  945. memo,
  946. metadata,
  947. quote_id,
  948. payment_request,
  949. payment_proof
  950. FROM
  951. transactions
  952. WHERE
  953. id = :id
  954. "#,
  955. )?
  956. .bind("id", transaction_id.as_slice().to_vec())
  957. .fetch_one(&*conn)
  958. .await?
  959. .map(sql_row_to_transaction)
  960. .transpose()?)
  961. }
  962. #[instrument(skip(self))]
  963. async fn list_transactions(
  964. &self,
  965. mint_url: Option<MintUrl>,
  966. direction: Option<TransactionDirection>,
  967. unit: Option<CurrencyUnit>,
  968. ) -> Result<Vec<Transaction>, Self::Err> {
  969. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  970. Ok(query(
  971. r#"
  972. SELECT
  973. mint_url,
  974. direction,
  975. unit,
  976. amount,
  977. fee,
  978. ys,
  979. timestamp,
  980. memo,
  981. metadata,
  982. quote_id,
  983. payment_request,
  984. payment_proof
  985. FROM
  986. transactions
  987. "#,
  988. )?
  989. .fetch_all(&*conn)
  990. .await?
  991. .into_iter()
  992. .filter_map(|row| {
  993. // TODO: Avoid a table scan by passing the heavy lifting of checking to the DB engine
  994. let transaction = sql_row_to_transaction(row).ok()?;
  995. if transaction.matches_conditions(&mint_url, &direction, &unit) {
  996. Some(transaction)
  997. } else {
  998. None
  999. }
  1000. })
  1001. .collect::<Vec<_>>())
  1002. }
  1003. #[instrument(skip(self))]
  1004. async fn remove_transaction(&self, transaction_id: TransactionId) -> Result<(), Self::Err> {
  1005. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1006. query(r#"DELETE FROM transactions WHERE id=:id"#)?
  1007. .bind("id", transaction_id.as_slice().to_vec())
  1008. .execute(&*conn)
  1009. .await?;
  1010. Ok(())
  1011. }
  1012. }
  1013. fn sql_row_to_mint_info(row: Vec<Column>) -> Result<MintInfo, Error> {
  1014. unpack_into!(
  1015. let (
  1016. name,
  1017. pubkey,
  1018. version,
  1019. description,
  1020. description_long,
  1021. contact,
  1022. nuts,
  1023. icon_url,
  1024. motd,
  1025. urls,
  1026. mint_time,
  1027. tos_url
  1028. ) = row
  1029. );
  1030. Ok(MintInfo {
  1031. name: column_as_nullable_string!(&name),
  1032. pubkey: column_as_nullable_string!(&pubkey, |v| serde_json::from_str(v).ok(), |v| {
  1033. serde_json::from_slice(v).ok()
  1034. }),
  1035. version: column_as_nullable_string!(&version).and_then(|v| serde_json::from_str(&v).ok()),
  1036. description: column_as_nullable_string!(description),
  1037. description_long: column_as_nullable_string!(description_long),
  1038. contact: column_as_nullable_string!(contact, |v| serde_json::from_str(&v).ok()),
  1039. nuts: column_as_nullable_string!(nuts, |v| serde_json::from_str(&v).ok())
  1040. .unwrap_or_default(),
  1041. urls: column_as_nullable_string!(urls, |v| serde_json::from_str(&v).ok()),
  1042. icon_url: column_as_nullable_string!(icon_url),
  1043. motd: column_as_nullable_string!(motd),
  1044. time: column_as_nullable_number!(mint_time).map(|t| t),
  1045. tos_url: column_as_nullable_string!(tos_url),
  1046. })
  1047. }
  1048. #[instrument(skip_all)]
  1049. fn sql_row_to_keyset(row: Vec<Column>) -> Result<KeySetInfo, Error> {
  1050. unpack_into!(
  1051. let (
  1052. id,
  1053. unit,
  1054. active,
  1055. input_fee_ppk,
  1056. final_expiry
  1057. ) = row
  1058. );
  1059. Ok(KeySetInfo {
  1060. id: column_as_string!(id, Id::from_str, Id::from_bytes),
  1061. unit: column_as_string!(unit, CurrencyUnit::from_str),
  1062. active: matches!(active, Column::Integer(1)),
  1063. input_fee_ppk: column_as_nullable_number!(input_fee_ppk).unwrap_or_default(),
  1064. final_expiry: column_as_nullable_number!(final_expiry),
  1065. })
  1066. }
  1067. fn sql_row_to_mint_quote(row: Vec<Column>) -> Result<MintQuote, Error> {
  1068. unpack_into!(
  1069. let (
  1070. id,
  1071. mint_url,
  1072. amount,
  1073. unit,
  1074. request,
  1075. state,
  1076. expiry,
  1077. secret_key,
  1078. row_method,
  1079. row_amount_minted,
  1080. row_amount_paid
  1081. ) = row
  1082. );
  1083. let amount: Option<i64> = column_as_nullable_number!(amount);
  1084. let amount_paid: u64 = column_as_number!(row_amount_paid);
  1085. let amount_minted: u64 = column_as_number!(row_amount_minted);
  1086. let payment_method =
  1087. PaymentMethod::from_str(&column_as_string!(row_method)).map_err(Error::from)?;
  1088. Ok(MintQuote {
  1089. id: column_as_string!(id),
  1090. mint_url: column_as_string!(mint_url, MintUrl::from_str),
  1091. amount: amount.and_then(Amount::from_i64),
  1092. unit: column_as_string!(unit, CurrencyUnit::from_str),
  1093. request: column_as_string!(request),
  1094. state: column_as_string!(state, MintQuoteState::from_str),
  1095. expiry: column_as_number!(expiry),
  1096. secret_key: column_as_nullable_string!(secret_key)
  1097. .map(|v| SecretKey::from_str(&v))
  1098. .transpose()?,
  1099. payment_method,
  1100. amount_issued: amount_minted.into(),
  1101. amount_paid: amount_paid.into(),
  1102. })
  1103. }
  1104. fn sql_row_to_melt_quote(row: Vec<Column>) -> Result<wallet::MeltQuote, Error> {
  1105. unpack_into!(
  1106. let (
  1107. id,
  1108. unit,
  1109. amount,
  1110. request,
  1111. fee_reserve,
  1112. state,
  1113. expiry,
  1114. payment_preimage,
  1115. row_method
  1116. ) = row
  1117. );
  1118. let amount: u64 = column_as_number!(amount);
  1119. let fee_reserve: u64 = column_as_number!(fee_reserve);
  1120. let payment_method =
  1121. PaymentMethod::from_str(&column_as_string!(row_method)).map_err(Error::from)?;
  1122. Ok(wallet::MeltQuote {
  1123. id: column_as_string!(id),
  1124. amount: Amount::from(amount),
  1125. unit: column_as_string!(unit, CurrencyUnit::from_str),
  1126. request: column_as_string!(request),
  1127. fee_reserve: Amount::from(fee_reserve),
  1128. state: column_as_string!(state, MeltQuoteState::from_str),
  1129. expiry: column_as_number!(expiry),
  1130. payment_preimage: column_as_nullable_string!(payment_preimage),
  1131. payment_method,
  1132. })
  1133. }
  1134. fn sql_row_to_proof_info(row: Vec<Column>) -> Result<ProofInfo, Error> {
  1135. unpack_into!(
  1136. let (
  1137. amount,
  1138. unit,
  1139. keyset_id,
  1140. secret,
  1141. c,
  1142. witness,
  1143. dleq_e,
  1144. dleq_s,
  1145. dleq_r,
  1146. y,
  1147. mint_url,
  1148. state,
  1149. spending_condition
  1150. ) = row
  1151. );
  1152. let dleq = match (
  1153. column_as_nullable_binary!(dleq_e),
  1154. column_as_nullable_binary!(dleq_s),
  1155. column_as_nullable_binary!(dleq_r),
  1156. ) {
  1157. (Some(e), Some(s), Some(r)) => {
  1158. let e_key = SecretKey::from_slice(&e)?;
  1159. let s_key = SecretKey::from_slice(&s)?;
  1160. let r_key = SecretKey::from_slice(&r)?;
  1161. Some(ProofDleq::new(e_key, s_key, r_key))
  1162. }
  1163. _ => None,
  1164. };
  1165. let amount: u64 = column_as_number!(amount);
  1166. let proof = Proof {
  1167. amount: Amount::from(amount),
  1168. keyset_id: column_as_string!(keyset_id, Id::from_str),
  1169. secret: column_as_string!(secret, Secret::from_str),
  1170. witness: column_as_nullable_string!(witness, |v| { serde_json::from_str(&v).ok() }, |v| {
  1171. serde_json::from_slice(&v).ok()
  1172. }),
  1173. c: column_as_string!(c, PublicKey::from_str, PublicKey::from_slice),
  1174. dleq,
  1175. };
  1176. Ok(ProofInfo {
  1177. proof,
  1178. y: column_as_string!(y, PublicKey::from_str, PublicKey::from_slice),
  1179. mint_url: column_as_string!(mint_url, MintUrl::from_str),
  1180. state: column_as_string!(state, State::from_str),
  1181. spending_condition: column_as_nullable_string!(
  1182. spending_condition,
  1183. |r| { serde_json::from_str(&r).ok() },
  1184. |r| { serde_json::from_slice(&r).ok() }
  1185. ),
  1186. unit: column_as_string!(unit, CurrencyUnit::from_str),
  1187. })
  1188. }
  1189. fn sql_row_to_transaction(row: Vec<Column>) -> Result<Transaction, Error> {
  1190. unpack_into!(
  1191. let (
  1192. mint_url,
  1193. direction,
  1194. unit,
  1195. amount,
  1196. fee,
  1197. ys,
  1198. timestamp,
  1199. memo,
  1200. metadata,
  1201. quote_id,
  1202. payment_request,
  1203. payment_proof
  1204. ) = row
  1205. );
  1206. let amount: u64 = column_as_number!(amount);
  1207. let fee: u64 = column_as_number!(fee);
  1208. Ok(Transaction {
  1209. mint_url: column_as_string!(mint_url, MintUrl::from_str),
  1210. direction: column_as_string!(direction, TransactionDirection::from_str),
  1211. unit: column_as_string!(unit, CurrencyUnit::from_str),
  1212. amount: Amount::from(amount),
  1213. fee: Amount::from(fee),
  1214. ys: column_as_binary!(ys)
  1215. .chunks(33)
  1216. .map(PublicKey::from_slice)
  1217. .collect::<Result<Vec<_>, _>>()?,
  1218. timestamp: column_as_number!(timestamp),
  1219. memo: column_as_nullable_string!(memo),
  1220. metadata: column_as_nullable_string!(metadata, |v| serde_json::from_str(&v).ok(), |v| {
  1221. serde_json::from_slice(&v).ok()
  1222. })
  1223. .unwrap_or_default(),
  1224. quote_id: column_as_nullable_string!(quote_id),
  1225. payment_request: column_as_nullable_string!(payment_request),
  1226. payment_proof: column_as_nullable_string!(payment_proof),
  1227. })
  1228. }