mod.rs 36 KB

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