mod.rs 46 KB

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