mod.rs 47 KB

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