mod.rs 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504
  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. .map(|w| serde_json::to_string(&w).unwrap()),
  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 id in keys_without_u32 {
  763. let id = column_as_string!(id.first().unwrap());
  764. if let Ok(id) = Id::from_str(&id) {
  765. query(
  766. r#"
  767. UPDATE
  768. key
  769. SET keyset_u32 = :u32_keyset
  770. WHERE id = :keyset_id
  771. "#,
  772. )?
  773. .bind("u32_keyset", u32::from(id))
  774. .bind("keyset_id", id.to_string())
  775. .execute(conn)
  776. .await?;
  777. }
  778. }
  779. // Also update keysets where keyset_u32 is null
  780. let keysets_without_u32: Vec<Vec<Column>> = query(
  781. r#"
  782. SELECT
  783. id
  784. FROM keyset
  785. WHERE keyset_u32 IS NULL
  786. "#,
  787. )?
  788. .fetch_all(conn)
  789. .await?;
  790. for id in keysets_without_u32 {
  791. let id = column_as_string!(id.first().unwrap());
  792. if let Ok(id) = Id::from_str(&id) {
  793. query(
  794. r#"
  795. UPDATE
  796. keyset
  797. SET keyset_u32 = :u32_keyset
  798. WHERE id = :keyset_id
  799. "#,
  800. )?
  801. .bind("u32_keyset", u32::from(id))
  802. .bind("keyset_id", id.to_string())
  803. .execute(conn)
  804. .await?;
  805. }
  806. }
  807. Ok(())
  808. }
  809. }
  810. #[async_trait]
  811. impl<RM> WalletDatabase for SQLWalletDatabase<RM>
  812. where
  813. RM: DatabasePool + 'static,
  814. {
  815. type Err = database::Error;
  816. async fn begin_db_transaction(
  817. &self,
  818. ) -> Result<Box<dyn WalletDatabaseTransaction<Self::Err> + Send + Sync>, Self::Err> {
  819. Ok(Box::new(SQLWalletTransaction {
  820. inner: ConnectionWithTransaction::new(
  821. self.pool.get().map_err(|e| Error::Database(Box::new(e)))?,
  822. )
  823. .await?,
  824. }))
  825. }
  826. #[instrument(skip(self))]
  827. async fn get_melt_quotes(&self) -> Result<Vec<wallet::MeltQuote>, Self::Err> {
  828. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  829. Ok(query(
  830. r#"
  831. SELECT
  832. id,
  833. unit,
  834. amount,
  835. request,
  836. fee_reserve,
  837. state,
  838. expiry,
  839. payment_preimage,
  840. payment_method
  841. FROM
  842. melt_quote
  843. "#,
  844. )?
  845. .fetch_all(&*conn)
  846. .await?
  847. .into_iter()
  848. .map(sql_row_to_melt_quote)
  849. .collect::<Result<_, _>>()?)
  850. }
  851. #[instrument(skip(self))]
  852. async fn get_mint(&self, mint_url: MintUrl) -> Result<Option<MintInfo>, Self::Err> {
  853. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  854. Ok(query(
  855. r#"
  856. SELECT
  857. name,
  858. pubkey,
  859. version,
  860. description,
  861. description_long,
  862. contact,
  863. nuts,
  864. icon_url,
  865. motd,
  866. urls,
  867. mint_time,
  868. tos_url
  869. FROM
  870. mint
  871. WHERE mint_url = :mint_url
  872. "#,
  873. )?
  874. .bind("mint_url", mint_url.to_string())
  875. .fetch_one(&*conn)
  876. .await?
  877. .map(sql_row_to_mint_info)
  878. .transpose()?)
  879. }
  880. #[instrument(skip(self))]
  881. async fn get_mints(&self) -> Result<HashMap<MintUrl, Option<MintInfo>>, Self::Err> {
  882. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  883. Ok(query(
  884. r#"
  885. SELECT
  886. name,
  887. pubkey,
  888. version,
  889. description,
  890. description_long,
  891. contact,
  892. nuts,
  893. icon_url,
  894. motd,
  895. urls,
  896. mint_time,
  897. tos_url,
  898. mint_url
  899. FROM
  900. mint
  901. "#,
  902. )?
  903. .fetch_all(&*conn)
  904. .await?
  905. .into_iter()
  906. .map(|mut row| {
  907. let url = column_as_string!(
  908. row.pop().ok_or(ConversionError::MissingColumn(0, 1))?,
  909. MintUrl::from_str
  910. );
  911. Ok((url, sql_row_to_mint_info(row).ok()))
  912. })
  913. .collect::<Result<HashMap<_, _>, Error>>()?)
  914. }
  915. #[instrument(skip(self))]
  916. async fn get_mint_keysets(
  917. &self,
  918. mint_url: MintUrl,
  919. ) -> Result<Option<Vec<KeySetInfo>>, Self::Err> {
  920. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  921. let keysets = query(
  922. r#"
  923. SELECT
  924. id,
  925. unit,
  926. active,
  927. input_fee_ppk,
  928. final_expiry
  929. FROM
  930. keyset
  931. WHERE mint_url = :mint_url
  932. "#,
  933. )?
  934. .bind("mint_url", mint_url.to_string())
  935. .fetch_all(&*conn)
  936. .await?
  937. .into_iter()
  938. .map(sql_row_to_keyset)
  939. .collect::<Result<Vec<_>, Error>>()?;
  940. match keysets.is_empty() {
  941. false => Ok(Some(keysets)),
  942. true => Ok(None),
  943. }
  944. }
  945. #[instrument(skip(self), fields(keyset_id = %keyset_id))]
  946. async fn get_keyset_by_id(&self, keyset_id: &Id) -> Result<Option<KeySetInfo>, Self::Err> {
  947. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  948. get_keyset_by_id_inner(&*conn, keyset_id, false).await
  949. }
  950. #[instrument(skip(self))]
  951. async fn get_mint_quote(&self, quote_id: &str) -> Result<Option<MintQuote>, Self::Err> {
  952. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  953. get_mint_quote_inner(&*conn, quote_id, false).await
  954. }
  955. #[instrument(skip(self))]
  956. async fn get_mint_quotes(&self) -> Result<Vec<MintQuote>, Self::Err> {
  957. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  958. Ok(query(
  959. r#"
  960. SELECT
  961. id,
  962. mint_url,
  963. amount,
  964. unit,
  965. request,
  966. state,
  967. expiry,
  968. secret_key,
  969. payment_method,
  970. amount_issued,
  971. amount_paid
  972. FROM
  973. mint_quote
  974. "#,
  975. )?
  976. .fetch_all(&*conn)
  977. .await?
  978. .into_iter()
  979. .map(sql_row_to_mint_quote)
  980. .collect::<Result<_, _>>()?)
  981. }
  982. #[instrument(skip(self))]
  983. async fn get_melt_quote(&self, quote_id: &str) -> Result<Option<wallet::MeltQuote>, Self::Err> {
  984. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  985. get_melt_quote_inner(&*conn, quote_id, false).await
  986. }
  987. #[instrument(skip(self), fields(keyset_id = %keyset_id))]
  988. async fn get_keys(&self, keyset_id: &Id) -> Result<Option<Keys>, Self::Err> {
  989. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  990. get_keys_inner(&*conn, keyset_id).await
  991. }
  992. #[instrument(skip(self, state, spending_conditions))]
  993. async fn get_proofs(
  994. &self,
  995. mint_url: Option<MintUrl>,
  996. unit: Option<CurrencyUnit>,
  997. state: Option<Vec<State>>,
  998. spending_conditions: Option<Vec<SpendingConditions>>,
  999. ) -> Result<Vec<ProofInfo>, Self::Err> {
  1000. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1001. get_proofs_inner(&*conn, mint_url, unit, state, spending_conditions, false).await
  1002. }
  1003. #[instrument(skip(self, ys))]
  1004. async fn get_proofs_by_ys(&self, ys: Vec<PublicKey>) -> Result<Vec<ProofInfo>, Self::Err> {
  1005. if ys.is_empty() {
  1006. return Ok(Vec::new());
  1007. }
  1008. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1009. Ok(query(
  1010. r#"
  1011. SELECT
  1012. amount,
  1013. unit,
  1014. keyset_id,
  1015. secret,
  1016. c,
  1017. witness,
  1018. dleq_e,
  1019. dleq_s,
  1020. dleq_r,
  1021. y,
  1022. mint_url,
  1023. state,
  1024. spending_condition
  1025. FROM proof
  1026. WHERE y IN (:ys)
  1027. "#,
  1028. )?
  1029. .bind_vec("ys", ys.iter().map(|y| y.to_bytes().to_vec()).collect())
  1030. .fetch_all(&*conn)
  1031. .await?
  1032. .into_iter()
  1033. .filter_map(|row| sql_row_to_proof_info(row).ok())
  1034. .collect::<Vec<_>>())
  1035. }
  1036. async fn get_balance(
  1037. &self,
  1038. mint_url: Option<MintUrl>,
  1039. unit: Option<CurrencyUnit>,
  1040. states: Option<Vec<State>>,
  1041. ) -> Result<u64, Self::Err> {
  1042. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1043. let mut query_str = "SELECT COALESCE(SUM(amount), 0) as total FROM proof".to_string();
  1044. let mut where_clauses = Vec::new();
  1045. let states = states
  1046. .unwrap_or_default()
  1047. .into_iter()
  1048. .map(|x| x.to_string())
  1049. .collect::<Vec<_>>();
  1050. if mint_url.is_some() {
  1051. where_clauses.push("mint_url = :mint_url");
  1052. }
  1053. if unit.is_some() {
  1054. where_clauses.push("unit = :unit");
  1055. }
  1056. if !states.is_empty() {
  1057. where_clauses.push("state IN (:states)");
  1058. }
  1059. if !where_clauses.is_empty() {
  1060. query_str.push_str(" WHERE ");
  1061. query_str.push_str(&where_clauses.join(" AND "));
  1062. }
  1063. let mut q = query(&query_str)?;
  1064. if let Some(ref mint_url) = mint_url {
  1065. q = q.bind("mint_url", mint_url.to_string());
  1066. }
  1067. if let Some(ref unit) = unit {
  1068. q = q.bind("unit", unit.to_string());
  1069. }
  1070. if !states.is_empty() {
  1071. q = q.bind_vec("states", states);
  1072. }
  1073. let balance = q
  1074. .pluck(&*conn)
  1075. .await?
  1076. .map(|n| {
  1077. // SQLite SUM returns INTEGER which we need to convert to u64
  1078. match n {
  1079. crate::stmt::Column::Integer(i) => Ok(i as u64),
  1080. crate::stmt::Column::Real(f) => Ok(f as u64),
  1081. _ => Err(Error::Database(Box::new(std::io::Error::new(
  1082. std::io::ErrorKind::InvalidData,
  1083. "Invalid balance type",
  1084. )))),
  1085. }
  1086. })
  1087. .transpose()?
  1088. .unwrap_or(0);
  1089. Ok(balance)
  1090. }
  1091. #[instrument(skip(self))]
  1092. async fn get_transaction(
  1093. &self,
  1094. transaction_id: TransactionId,
  1095. ) -> Result<Option<Transaction>, Self::Err> {
  1096. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1097. Ok(query(
  1098. r#"
  1099. SELECT
  1100. mint_url,
  1101. direction,
  1102. unit,
  1103. amount,
  1104. fee,
  1105. ys,
  1106. timestamp,
  1107. memo,
  1108. metadata,
  1109. quote_id,
  1110. payment_request,
  1111. payment_proof
  1112. FROM
  1113. transactions
  1114. WHERE
  1115. id = :id
  1116. "#,
  1117. )?
  1118. .bind("id", transaction_id.as_slice().to_vec())
  1119. .fetch_one(&*conn)
  1120. .await?
  1121. .map(sql_row_to_transaction)
  1122. .transpose()?)
  1123. }
  1124. #[instrument(skip(self))]
  1125. async fn list_transactions(
  1126. &self,
  1127. mint_url: Option<MintUrl>,
  1128. direction: Option<TransactionDirection>,
  1129. unit: Option<CurrencyUnit>,
  1130. ) -> Result<Vec<Transaction>, Self::Err> {
  1131. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1132. Ok(query(
  1133. r#"
  1134. SELECT
  1135. mint_url,
  1136. direction,
  1137. unit,
  1138. amount,
  1139. fee,
  1140. ys,
  1141. timestamp,
  1142. memo,
  1143. metadata,
  1144. quote_id,
  1145. payment_request,
  1146. payment_proof
  1147. FROM
  1148. transactions
  1149. "#,
  1150. )?
  1151. .fetch_all(&*conn)
  1152. .await?
  1153. .into_iter()
  1154. .filter_map(|row| {
  1155. // TODO: Avoid a table scan by passing the heavy lifting of checking to the DB engine
  1156. let transaction = sql_row_to_transaction(row).ok()?;
  1157. if transaction.matches_conditions(&mint_url, &direction, &unit) {
  1158. Some(transaction)
  1159. } else {
  1160. None
  1161. }
  1162. })
  1163. .collect::<Vec<_>>())
  1164. }
  1165. }
  1166. fn sql_row_to_mint_info(row: Vec<Column>) -> Result<MintInfo, Error> {
  1167. unpack_into!(
  1168. let (
  1169. name,
  1170. pubkey,
  1171. version,
  1172. description,
  1173. description_long,
  1174. contact,
  1175. nuts,
  1176. icon_url,
  1177. motd,
  1178. urls,
  1179. mint_time,
  1180. tos_url
  1181. ) = row
  1182. );
  1183. Ok(MintInfo {
  1184. name: column_as_nullable_string!(&name),
  1185. pubkey: column_as_nullable_string!(&pubkey, |v| serde_json::from_str(v).ok(), |v| {
  1186. serde_json::from_slice(v).ok()
  1187. }),
  1188. version: column_as_nullable_string!(&version).and_then(|v| serde_json::from_str(&v).ok()),
  1189. description: column_as_nullable_string!(description),
  1190. description_long: column_as_nullable_string!(description_long),
  1191. contact: column_as_nullable_string!(contact, |v| serde_json::from_str(&v).ok()),
  1192. nuts: column_as_nullable_string!(nuts, |v| serde_json::from_str(&v).ok())
  1193. .unwrap_or_default(),
  1194. urls: column_as_nullable_string!(urls, |v| serde_json::from_str(&v).ok()),
  1195. icon_url: column_as_nullable_string!(icon_url),
  1196. motd: column_as_nullable_string!(motd),
  1197. time: column_as_nullable_number!(mint_time).map(|t| t),
  1198. tos_url: column_as_nullable_string!(tos_url),
  1199. })
  1200. }
  1201. #[instrument(skip_all)]
  1202. fn sql_row_to_keyset(row: Vec<Column>) -> Result<KeySetInfo, Error> {
  1203. unpack_into!(
  1204. let (
  1205. id,
  1206. unit,
  1207. active,
  1208. input_fee_ppk,
  1209. final_expiry
  1210. ) = row
  1211. );
  1212. Ok(KeySetInfo {
  1213. id: column_as_string!(id, Id::from_str, Id::from_bytes),
  1214. unit: column_as_string!(unit, CurrencyUnit::from_str),
  1215. active: matches!(active, Column::Integer(1)),
  1216. input_fee_ppk: column_as_nullable_number!(input_fee_ppk).unwrap_or_default(),
  1217. final_expiry: column_as_nullable_number!(final_expiry),
  1218. })
  1219. }
  1220. fn sql_row_to_mint_quote(row: Vec<Column>) -> Result<MintQuote, Error> {
  1221. unpack_into!(
  1222. let (
  1223. id,
  1224. mint_url,
  1225. amount,
  1226. unit,
  1227. request,
  1228. state,
  1229. expiry,
  1230. secret_key,
  1231. row_method,
  1232. row_amount_minted,
  1233. row_amount_paid
  1234. ) = row
  1235. );
  1236. let amount: Option<i64> = column_as_nullable_number!(amount);
  1237. let amount_paid: u64 = column_as_number!(row_amount_paid);
  1238. let amount_minted: u64 = column_as_number!(row_amount_minted);
  1239. let payment_method =
  1240. PaymentMethod::from_str(&column_as_string!(row_method)).map_err(Error::from)?;
  1241. Ok(MintQuote {
  1242. id: column_as_string!(id),
  1243. mint_url: column_as_string!(mint_url, MintUrl::from_str),
  1244. amount: amount.and_then(Amount::from_i64),
  1245. unit: column_as_string!(unit, CurrencyUnit::from_str),
  1246. request: column_as_string!(request),
  1247. state: column_as_string!(state, MintQuoteState::from_str),
  1248. expiry: column_as_number!(expiry),
  1249. secret_key: column_as_nullable_string!(secret_key)
  1250. .map(|v| SecretKey::from_str(&v))
  1251. .transpose()?,
  1252. payment_method,
  1253. amount_issued: amount_minted.into(),
  1254. amount_paid: amount_paid.into(),
  1255. })
  1256. }
  1257. fn sql_row_to_melt_quote(row: Vec<Column>) -> Result<wallet::MeltQuote, Error> {
  1258. unpack_into!(
  1259. let (
  1260. id,
  1261. unit,
  1262. amount,
  1263. request,
  1264. fee_reserve,
  1265. state,
  1266. expiry,
  1267. payment_preimage,
  1268. row_method
  1269. ) = row
  1270. );
  1271. let amount: u64 = column_as_number!(amount);
  1272. let fee_reserve: u64 = column_as_number!(fee_reserve);
  1273. let payment_method =
  1274. PaymentMethod::from_str(&column_as_string!(row_method)).map_err(Error::from)?;
  1275. Ok(wallet::MeltQuote {
  1276. id: column_as_string!(id),
  1277. amount: Amount::from(amount),
  1278. unit: column_as_string!(unit, CurrencyUnit::from_str),
  1279. request: column_as_string!(request),
  1280. fee_reserve: Amount::from(fee_reserve),
  1281. state: column_as_string!(state, MeltQuoteState::from_str),
  1282. expiry: column_as_number!(expiry),
  1283. payment_preimage: column_as_nullable_string!(payment_preimage),
  1284. payment_method,
  1285. })
  1286. }
  1287. fn sql_row_to_proof_info(row: Vec<Column>) -> Result<ProofInfo, Error> {
  1288. unpack_into!(
  1289. let (
  1290. amount,
  1291. unit,
  1292. keyset_id,
  1293. secret,
  1294. c,
  1295. witness,
  1296. dleq_e,
  1297. dleq_s,
  1298. dleq_r,
  1299. y,
  1300. mint_url,
  1301. state,
  1302. spending_condition
  1303. ) = row
  1304. );
  1305. let dleq = match (
  1306. column_as_nullable_binary!(dleq_e),
  1307. column_as_nullable_binary!(dleq_s),
  1308. column_as_nullable_binary!(dleq_r),
  1309. ) {
  1310. (Some(e), Some(s), Some(r)) => {
  1311. let e_key = SecretKey::from_slice(&e)?;
  1312. let s_key = SecretKey::from_slice(&s)?;
  1313. let r_key = SecretKey::from_slice(&r)?;
  1314. Some(ProofDleq::new(e_key, s_key, r_key))
  1315. }
  1316. _ => None,
  1317. };
  1318. let amount: u64 = column_as_number!(amount);
  1319. let proof = Proof {
  1320. amount: Amount::from(amount),
  1321. keyset_id: column_as_string!(keyset_id, Id::from_str),
  1322. secret: column_as_string!(secret, Secret::from_str),
  1323. witness: column_as_nullable_string!(witness, |v| { serde_json::from_str(&v).ok() }, |v| {
  1324. serde_json::from_slice(&v).ok()
  1325. }),
  1326. c: column_as_string!(c, PublicKey::from_str, PublicKey::from_slice),
  1327. dleq,
  1328. };
  1329. Ok(ProofInfo {
  1330. proof,
  1331. y: column_as_string!(y, PublicKey::from_str, PublicKey::from_slice),
  1332. mint_url: column_as_string!(mint_url, MintUrl::from_str),
  1333. state: column_as_string!(state, State::from_str),
  1334. spending_condition: column_as_nullable_string!(
  1335. spending_condition,
  1336. |r| { serde_json::from_str(&r).ok() },
  1337. |r| { serde_json::from_slice(&r).ok() }
  1338. ),
  1339. unit: column_as_string!(unit, CurrencyUnit::from_str),
  1340. })
  1341. }
  1342. fn sql_row_to_transaction(row: Vec<Column>) -> Result<Transaction, Error> {
  1343. unpack_into!(
  1344. let (
  1345. mint_url,
  1346. direction,
  1347. unit,
  1348. amount,
  1349. fee,
  1350. ys,
  1351. timestamp,
  1352. memo,
  1353. metadata,
  1354. quote_id,
  1355. payment_request,
  1356. payment_proof
  1357. ) = row
  1358. );
  1359. let amount: u64 = column_as_number!(amount);
  1360. let fee: u64 = column_as_number!(fee);
  1361. Ok(Transaction {
  1362. mint_url: column_as_string!(mint_url, MintUrl::from_str),
  1363. direction: column_as_string!(direction, TransactionDirection::from_str),
  1364. unit: column_as_string!(unit, CurrencyUnit::from_str),
  1365. amount: Amount::from(amount),
  1366. fee: Amount::from(fee),
  1367. ys: column_as_binary!(ys)
  1368. .chunks(33)
  1369. .map(PublicKey::from_slice)
  1370. .collect::<Result<Vec<_>, _>>()?,
  1371. timestamp: column_as_number!(timestamp),
  1372. memo: column_as_nullable_string!(memo),
  1373. metadata: column_as_nullable_string!(metadata, |v| serde_json::from_str(&v).ok(), |v| {
  1374. serde_json::from_slice(&v).ok()
  1375. })
  1376. .unwrap_or_default(),
  1377. quote_id: column_as_nullable_string!(quote_id),
  1378. payment_request: column_as_nullable_string!(payment_request),
  1379. payment_proof: column_as_nullable_string!(payment_proof),
  1380. })
  1381. }