mod.rs 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825
  1. //! SQL database implementation of the Mint
  2. //!
  3. //! This is a generic SQL implementation for the mint storage layer. Any database can be plugged in
  4. //! as long as standard ANSI SQL is used, as Postgres and SQLite would understand it.
  5. //!
  6. //! This implementation also has a rudimentary but standard migration and versioning system.
  7. //!
  8. //! The trait expects an asynchronous interaction, but it also provides tools to spawn blocking
  9. //! clients in a pool and expose them to an asynchronous environment, making them compatible with
  10. //! Mint.
  11. use std::collections::HashMap;
  12. use std::fmt::Debug;
  13. use std::str::FromStr;
  14. use std::sync::Arc;
  15. use async_trait::async_trait;
  16. use bitcoin::bip32::DerivationPath;
  17. use cdk_common::common::QuoteTTL;
  18. use cdk_common::database::{
  19. self, ConversionError, Error, MintDatabase, MintDbWriterFinalizer, MintKeyDatabaseTransaction,
  20. MintKeysDatabase, MintProofsDatabase, MintQuotesDatabase, MintQuotesTransaction,
  21. MintSignatureTransaction, MintSignaturesDatabase,
  22. };
  23. use cdk_common::mint::{
  24. self, IncomingPayment, Issuance, MeltPaymentRequest, MeltQuote, MintKeySetInfo, MintQuote,
  25. };
  26. use cdk_common::nut00::ProofsMethods;
  27. use cdk_common::payment::PaymentIdentifier;
  28. use cdk_common::secret::Secret;
  29. use cdk_common::state::check_state_transition;
  30. use cdk_common::util::unix_time;
  31. use cdk_common::{
  32. Amount, BlindSignature, BlindSignatureDleq, CurrencyUnit, Id, MeltQuoteState, MintInfo,
  33. PaymentMethod, Proof, Proofs, PublicKey, SecretKey, State,
  34. };
  35. use lightning_invoice::Bolt11Invoice;
  36. use migrations::MIGRATIONS;
  37. use tracing::instrument;
  38. use uuid::Uuid;
  39. use crate::common::migrate;
  40. use crate::database::{ConnectionWithTransaction, DatabaseExecutor};
  41. use crate::pool::{DatabasePool, Pool, PooledResource};
  42. use crate::stmt::{query, Column};
  43. use crate::{
  44. column_as_nullable_number, column_as_nullable_string, column_as_number, column_as_string,
  45. unpack_into,
  46. };
  47. #[cfg(feature = "auth")]
  48. mod auth;
  49. #[rustfmt::skip]
  50. mod migrations;
  51. #[cfg(feature = "auth")]
  52. pub use auth::SQLMintAuthDatabase;
  53. /// Mint SQL Database
  54. #[derive(Debug, Clone)]
  55. pub struct SQLMintDatabase<RM>
  56. where
  57. RM: DatabasePool + 'static,
  58. {
  59. pool: Arc<Pool<RM>>,
  60. }
  61. /// SQL Transaction Writer
  62. pub struct SQLTransaction<RM>
  63. where
  64. RM: DatabasePool + 'static,
  65. {
  66. inner: ConnectionWithTransaction<RM::Connection, PooledResource<RM>>,
  67. }
  68. #[inline(always)]
  69. async fn get_current_states<C>(
  70. conn: &C,
  71. ys: &[PublicKey],
  72. ) -> Result<HashMap<PublicKey, State>, Error>
  73. where
  74. C: DatabaseExecutor + Send + Sync,
  75. {
  76. if ys.is_empty() {
  77. return Ok(Default::default());
  78. }
  79. query(r#"SELECT y, state FROM proof WHERE y IN (:ys)"#)?
  80. .bind_vec("ys", ys.iter().map(|y| y.to_bytes().to_vec()).collect())
  81. .fetch_all(conn)
  82. .await?
  83. .into_iter()
  84. .map(|row| {
  85. Ok((
  86. column_as_string!(&row[0], PublicKey::from_hex, PublicKey::from_slice),
  87. column_as_string!(&row[1], State::from_str),
  88. ))
  89. })
  90. .collect::<Result<HashMap<_, _>, _>>()
  91. }
  92. #[inline(always)]
  93. async fn set_to_config<C, V>(conn: &C, id: &str, value: &V) -> Result<(), Error>
  94. where
  95. C: DatabaseExecutor + Send + Sync,
  96. V: ?Sized + serde::Serialize,
  97. {
  98. query(
  99. r#"
  100. INSERT INTO config (id, value) VALUES (:id, :value)
  101. ON CONFLICT(id) DO UPDATE SET value = excluded.value
  102. "#,
  103. )?
  104. .bind("id", id.to_owned())
  105. .bind("value", serde_json::to_string(&value)?)
  106. .execute(conn)
  107. .await?;
  108. Ok(())
  109. }
  110. impl<RM> SQLMintDatabase<RM>
  111. where
  112. RM: DatabasePool + 'static,
  113. {
  114. /// Creates a new instance
  115. pub async fn new<X>(db: X) -> Result<Self, Error>
  116. where
  117. X: Into<RM::Config>,
  118. {
  119. let pool = Pool::new(db.into());
  120. Self::migrate(pool.get().map_err(|e| Error::Database(Box::new(e)))?).await?;
  121. Ok(Self { pool })
  122. }
  123. /// Migrate
  124. async fn migrate(conn: PooledResource<RM>) -> Result<(), Error> {
  125. let tx = ConnectionWithTransaction::new(conn).await?;
  126. migrate(&tx, RM::Connection::name(), MIGRATIONS).await?;
  127. tx.commit().await?;
  128. Ok(())
  129. }
  130. #[inline(always)]
  131. async fn fetch_from_config<R>(&self, id: &str) -> Result<R, Error>
  132. where
  133. R: serde::de::DeserializeOwned,
  134. {
  135. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  136. let value = column_as_string!(query(r#"SELECT value FROM config WHERE id = :id LIMIT 1"#)?
  137. .bind("id", id.to_owned())
  138. .pluck(&*conn)
  139. .await?
  140. .ok_or(Error::UnknownQuoteTTL)?);
  141. Ok(serde_json::from_str(&value)?)
  142. }
  143. }
  144. #[async_trait]
  145. impl<RM> database::MintProofsTransaction<'_> for SQLTransaction<RM>
  146. where
  147. RM: DatabasePool + 'static,
  148. {
  149. type Err = Error;
  150. async fn add_proofs(
  151. &mut self,
  152. proofs: Proofs,
  153. quote_id: Option<Uuid>,
  154. ) -> Result<(), Self::Err> {
  155. let current_time = unix_time();
  156. // Check any previous proof, this query should return None in order to proceed storing
  157. // Any result here would error
  158. match query(r#"SELECT state FROM proof WHERE y IN (:ys) LIMIT 1 FOR UPDATE"#)?
  159. .bind_vec(
  160. "ys",
  161. proofs
  162. .iter()
  163. .map(|y| y.y().map(|y| y.to_bytes().to_vec()))
  164. .collect::<Result<_, _>>()?,
  165. )
  166. .pluck(&self.inner)
  167. .await?
  168. .map(|state| Ok::<_, Error>(column_as_string!(&state, State::from_str)))
  169. .transpose()?
  170. {
  171. Some(State::Spent) => Err(database::Error::AttemptUpdateSpentProof),
  172. Some(_) => Err(database::Error::Duplicate),
  173. None => Ok(()), // no previous record
  174. }?;
  175. for proof in proofs {
  176. query(
  177. r#"
  178. INSERT INTO proof
  179. (y, amount, keyset_id, secret, c, witness, state, quote_id, created_time)
  180. VALUES
  181. (:y, :amount, :keyset_id, :secret, :c, :witness, :state, :quote_id, :created_time)
  182. "#,
  183. )?
  184. .bind("y", proof.y()?.to_bytes().to_vec())
  185. .bind("amount", proof.amount.to_i64())
  186. .bind("keyset_id", proof.keyset_id.to_string())
  187. .bind("secret", proof.secret.to_string())
  188. .bind("c", proof.c.to_bytes().to_vec())
  189. .bind(
  190. "witness",
  191. proof.witness.map(|w| serde_json::to_string(&w).unwrap()),
  192. )
  193. .bind("state", "UNSPENT".to_string())
  194. .bind("quote_id", quote_id.map(|q| q.hyphenated().to_string()))
  195. .bind("created_time", current_time as i64)
  196. .execute(&self.inner)
  197. .await?;
  198. }
  199. Ok(())
  200. }
  201. async fn update_proofs_states(
  202. &mut self,
  203. ys: &[PublicKey],
  204. new_state: State,
  205. ) -> Result<Vec<Option<State>>, Self::Err> {
  206. let mut current_states = get_current_states(&self.inner, ys).await?;
  207. if current_states.len() != ys.len() {
  208. tracing::warn!(
  209. "Attempted to update state of non-existent proof {} {}",
  210. current_states.len(),
  211. ys.len()
  212. );
  213. return Err(database::Error::ProofNotFound);
  214. }
  215. for state in current_states.values() {
  216. check_state_transition(*state, new_state)?;
  217. }
  218. query(r#"UPDATE proof SET state = :new_state WHERE y IN (:ys)"#)?
  219. .bind("new_state", new_state.to_string())
  220. .bind_vec("ys", ys.iter().map(|y| y.to_bytes().to_vec()).collect())
  221. .execute(&self.inner)
  222. .await?;
  223. Ok(ys.iter().map(|y| current_states.remove(y)).collect())
  224. }
  225. async fn remove_proofs(
  226. &mut self,
  227. ys: &[PublicKey],
  228. _quote_id: Option<Uuid>,
  229. ) -> Result<(), Self::Err> {
  230. let total_deleted = query(
  231. r#"
  232. DELETE FROM proof WHERE y IN (:ys) AND state NOT IN (:exclude_state)
  233. "#,
  234. )?
  235. .bind_vec("ys", ys.iter().map(|y| y.to_bytes().to_vec()).collect())
  236. .bind_vec("exclude_state", vec![State::Spent.to_string()])
  237. .execute(&self.inner)
  238. .await?;
  239. if total_deleted != ys.len() {
  240. return Err(Self::Err::AttemptRemoveSpentProof);
  241. }
  242. Ok(())
  243. }
  244. }
  245. #[async_trait]
  246. impl<RM> database::MintTransaction<'_, Error> for SQLTransaction<RM>
  247. where
  248. RM: DatabasePool + 'static,
  249. {
  250. async fn set_mint_info(&mut self, mint_info: MintInfo) -> Result<(), Error> {
  251. Ok(set_to_config(&self.inner, "mint_info", &mint_info).await?)
  252. }
  253. async fn set_quote_ttl(&mut self, quote_ttl: QuoteTTL) -> Result<(), Error> {
  254. Ok(set_to_config(&self.inner, "quote_ttl", &quote_ttl).await?)
  255. }
  256. }
  257. #[async_trait]
  258. impl<RM> MintDbWriterFinalizer for SQLTransaction<RM>
  259. where
  260. RM: DatabasePool + 'static,
  261. {
  262. type Err = Error;
  263. async fn commit(self: Box<Self>) -> Result<(), Error> {
  264. self.inner.commit().await
  265. }
  266. async fn rollback(self: Box<Self>) -> Result<(), Error> {
  267. self.inner.rollback().await
  268. }
  269. }
  270. #[inline(always)]
  271. async fn get_mint_quote_payments<C>(
  272. conn: &C,
  273. quote_id: &Uuid,
  274. ) -> Result<Vec<IncomingPayment>, Error>
  275. where
  276. C: DatabaseExecutor + Send + Sync,
  277. {
  278. // Get payment IDs and timestamps from the mint_quote_payments table
  279. query(
  280. r#"
  281. SELECT payment_id, timestamp, amount
  282. FROM mint_quote_payments
  283. WHERE quote_id=:quote_id;
  284. "#,
  285. )?
  286. .bind("quote_id", quote_id.as_hyphenated().to_string())
  287. .fetch_all(conn)
  288. .await?
  289. .into_iter()
  290. .map(|row| {
  291. let amount: u64 = column_as_number!(row[2].clone());
  292. let time: u64 = column_as_number!(row[1].clone());
  293. Ok(IncomingPayment::new(
  294. amount.into(),
  295. column_as_string!(&row[0]),
  296. time,
  297. ))
  298. })
  299. .collect()
  300. }
  301. #[inline(always)]
  302. async fn get_mint_quote_issuance<C>(conn: &C, quote_id: &Uuid) -> Result<Vec<Issuance>, Error>
  303. where
  304. C: DatabaseExecutor + Send + Sync,
  305. {
  306. // Get payment IDs and timestamps from the mint_quote_payments table
  307. query(
  308. r#"
  309. SELECT amount, timestamp
  310. FROM mint_quote_issued
  311. WHERE quote_id=:quote_id
  312. "#,
  313. )?
  314. .bind("quote_id", quote_id.as_hyphenated().to_string())
  315. .fetch_all(conn)
  316. .await?
  317. .into_iter()
  318. .map(|row| {
  319. let time: u64 = column_as_number!(row[1].clone());
  320. Ok(Issuance::new(
  321. Amount::from_i64(column_as_number!(row[0].clone()))
  322. .expect("Is amount when put into db"),
  323. time,
  324. ))
  325. })
  326. .collect()
  327. }
  328. #[async_trait]
  329. impl<RM> MintKeyDatabaseTransaction<'_, Error> for SQLTransaction<RM>
  330. where
  331. RM: DatabasePool + 'static,
  332. {
  333. async fn add_keyset_info(&mut self, keyset: MintKeySetInfo) -> Result<(), Error> {
  334. query(
  335. r#"
  336. INSERT INTO
  337. keyset (
  338. id, unit, active, valid_from, valid_to, derivation_path,
  339. max_order, input_fee_ppk, derivation_path_index
  340. )
  341. VALUES (
  342. :id, :unit, :active, :valid_from, :valid_to, :derivation_path,
  343. :max_order, :input_fee_ppk, :derivation_path_index
  344. )
  345. ON CONFLICT(id) DO UPDATE SET
  346. unit = excluded.unit,
  347. active = excluded.active,
  348. valid_from = excluded.valid_from,
  349. valid_to = excluded.valid_to,
  350. derivation_path = excluded.derivation_path,
  351. max_order = excluded.max_order,
  352. input_fee_ppk = excluded.input_fee_ppk,
  353. derivation_path_index = excluded.derivation_path_index
  354. "#,
  355. )?
  356. .bind("id", keyset.id.to_string())
  357. .bind("unit", keyset.unit.to_string())
  358. .bind("active", keyset.active)
  359. .bind("valid_from", keyset.valid_from as i64)
  360. .bind("valid_to", keyset.final_expiry.map(|v| v as i64))
  361. .bind("derivation_path", keyset.derivation_path.to_string())
  362. .bind("max_order", keyset.max_order)
  363. .bind("input_fee_ppk", keyset.input_fee_ppk as i64)
  364. .bind("derivation_path_index", keyset.derivation_path_index)
  365. .execute(&self.inner)
  366. .await?;
  367. Ok(())
  368. }
  369. async fn set_active_keyset(&mut self, unit: CurrencyUnit, id: Id) -> Result<(), Error> {
  370. query(r#"UPDATE keyset SET active=FALSE WHERE unit = :unit"#)?
  371. .bind("unit", unit.to_string())
  372. .execute(&self.inner)
  373. .await?;
  374. query(r#"UPDATE keyset SET active=TRUE WHERE unit = :unit AND id = :id"#)?
  375. .bind("unit", unit.to_string())
  376. .bind("id", id.to_string())
  377. .execute(&self.inner)
  378. .await?;
  379. Ok(())
  380. }
  381. }
  382. #[async_trait]
  383. impl<RM> MintKeysDatabase for SQLMintDatabase<RM>
  384. where
  385. RM: DatabasePool + 'static,
  386. {
  387. type Err = Error;
  388. async fn begin_transaction<'a>(
  389. &'a self,
  390. ) -> Result<Box<dyn MintKeyDatabaseTransaction<'a, Error> + Send + Sync + 'a>, Error> {
  391. Ok(Box::new(SQLTransaction {
  392. inner: ConnectionWithTransaction::new(
  393. self.pool.get().map_err(|e| Error::Database(Box::new(e)))?,
  394. )
  395. .await?,
  396. }))
  397. }
  398. async fn get_active_keyset_id(&self, unit: &CurrencyUnit) -> Result<Option<Id>, Self::Err> {
  399. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  400. Ok(
  401. query(r#" SELECT id FROM keyset WHERE active = :active AND unit = :unit"#)?
  402. .bind("active", true)
  403. .bind("unit", unit.to_string())
  404. .pluck(&*conn)
  405. .await?
  406. .map(|id| match id {
  407. Column::Text(text) => Ok(Id::from_str(&text)?),
  408. Column::Blob(id) => Ok(Id::from_bytes(&id)?),
  409. _ => Err(Error::InvalidKeysetId),
  410. })
  411. .transpose()?,
  412. )
  413. }
  414. async fn get_active_keysets(&self) -> Result<HashMap<CurrencyUnit, Id>, Self::Err> {
  415. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  416. Ok(
  417. query(r#"SELECT id, unit FROM keyset WHERE active = :active"#)?
  418. .bind("active", true)
  419. .fetch_all(&*conn)
  420. .await?
  421. .into_iter()
  422. .map(|row| {
  423. Ok((
  424. column_as_string!(&row[1], CurrencyUnit::from_str),
  425. column_as_string!(&row[0], Id::from_str, Id::from_bytes),
  426. ))
  427. })
  428. .collect::<Result<HashMap<_, _>, Error>>()?,
  429. )
  430. }
  431. async fn get_keyset_info(&self, id: &Id) -> Result<Option<MintKeySetInfo>, Self::Err> {
  432. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  433. Ok(query(
  434. r#"SELECT
  435. id,
  436. unit,
  437. active,
  438. valid_from,
  439. valid_to,
  440. derivation_path,
  441. derivation_path_index,
  442. max_order,
  443. input_fee_ppk
  444. FROM
  445. keyset
  446. WHERE id=:id"#,
  447. )?
  448. .bind("id", id.to_string())
  449. .fetch_one(&*conn)
  450. .await?
  451. .map(sql_row_to_keyset_info)
  452. .transpose()?)
  453. }
  454. async fn get_keyset_infos(&self) -> Result<Vec<MintKeySetInfo>, Self::Err> {
  455. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  456. Ok(query(
  457. r#"SELECT
  458. id,
  459. unit,
  460. active,
  461. valid_from,
  462. valid_to,
  463. derivation_path,
  464. derivation_path_index,
  465. max_order,
  466. input_fee_ppk
  467. FROM
  468. keyset
  469. "#,
  470. )?
  471. .fetch_all(&*conn)
  472. .await?
  473. .into_iter()
  474. .map(sql_row_to_keyset_info)
  475. .collect::<Result<Vec<_>, _>>()?)
  476. }
  477. }
  478. #[async_trait]
  479. impl<RM> MintQuotesTransaction<'_> for SQLTransaction<RM>
  480. where
  481. RM: DatabasePool + 'static,
  482. {
  483. type Err = Error;
  484. #[instrument(skip(self))]
  485. async fn increment_mint_quote_amount_paid(
  486. &mut self,
  487. quote_id: &Uuid,
  488. amount_paid: Amount,
  489. payment_id: String,
  490. ) -> Result<Amount, Self::Err> {
  491. // Check if payment_id already exists in mint_quote_payments
  492. let exists = query(
  493. r#"
  494. SELECT payment_id
  495. FROM mint_quote_payments
  496. WHERE payment_id = :payment_id
  497. FOR UPDATE
  498. "#,
  499. )?
  500. .bind("payment_id", payment_id.clone())
  501. .fetch_one(&self.inner)
  502. .await?;
  503. if exists.is_some() {
  504. tracing::error!("Payment ID already exists: {}", payment_id);
  505. return Err(database::Error::Duplicate);
  506. }
  507. // Get current amount_paid from quote
  508. let current_amount = query(
  509. r#"
  510. SELECT amount_paid
  511. FROM mint_quote
  512. WHERE id = :quote_id
  513. FOR UPDATE
  514. "#,
  515. )?
  516. .bind("quote_id", quote_id.as_hyphenated().to_string())
  517. .fetch_one(&self.inner)
  518. .await
  519. .inspect_err(|err| {
  520. tracing::error!("SQLite could not get mint quote amount_paid: {}", err);
  521. })?;
  522. let current_amount_paid = if let Some(current_amount) = current_amount {
  523. let amount: u64 = column_as_number!(current_amount[0].clone());
  524. Amount::from(amount)
  525. } else {
  526. Amount::ZERO
  527. };
  528. // Calculate new amount_paid with overflow check
  529. let new_amount_paid = current_amount_paid
  530. .checked_add(amount_paid)
  531. .ok_or_else(|| database::Error::AmountOverflow)?;
  532. // Update the amount_paid
  533. query(
  534. r#"
  535. UPDATE mint_quote
  536. SET amount_paid = :amount_paid
  537. WHERE id = :quote_id
  538. "#,
  539. )?
  540. .bind("amount_paid", new_amount_paid.to_i64())
  541. .bind("quote_id", quote_id.as_hyphenated().to_string())
  542. .execute(&self.inner)
  543. .await
  544. .inspect_err(|err| {
  545. tracing::error!("SQLite could not update mint quote amount_paid: {}", err);
  546. })?;
  547. // Add payment_id to mint_quote_payments table
  548. query(
  549. r#"
  550. INSERT INTO mint_quote_payments
  551. (quote_id, payment_id, amount, timestamp)
  552. VALUES (:quote_id, :payment_id, :amount, :timestamp)
  553. "#,
  554. )?
  555. .bind("quote_id", quote_id.as_hyphenated().to_string())
  556. .bind("payment_id", payment_id)
  557. .bind("amount", amount_paid.to_i64())
  558. .bind("timestamp", unix_time() as i64)
  559. .execute(&self.inner)
  560. .await
  561. .map_err(|err| {
  562. tracing::error!("SQLite could not insert payment ID: {}", err);
  563. err
  564. })?;
  565. Ok(new_amount_paid)
  566. }
  567. #[instrument(skip_all)]
  568. async fn increment_mint_quote_amount_issued(
  569. &mut self,
  570. quote_id: &Uuid,
  571. amount_issued: Amount,
  572. ) -> Result<Amount, Self::Err> {
  573. // Get current amount_issued from quote
  574. let current_amount = query(
  575. r#"
  576. SELECT amount_issued
  577. FROM mint_quote
  578. WHERE id = :quote_id
  579. FOR UPDATE
  580. "#,
  581. )?
  582. .bind("quote_id", quote_id.as_hyphenated().to_string())
  583. .fetch_one(&self.inner)
  584. .await
  585. .inspect_err(|err| {
  586. tracing::error!("SQLite could not get mint quote amount_issued: {}", err);
  587. })?;
  588. let current_amount_issued = if let Some(current_amount) = current_amount {
  589. let amount: u64 = column_as_number!(current_amount[0].clone());
  590. Amount::from(amount)
  591. } else {
  592. Amount::ZERO
  593. };
  594. // Calculate new amount_issued with overflow check
  595. let new_amount_issued = current_amount_issued
  596. .checked_add(amount_issued)
  597. .ok_or_else(|| database::Error::AmountOverflow)?;
  598. // Update the amount_issued
  599. query(
  600. r#"
  601. UPDATE mint_quote
  602. SET amount_issued = :amount_issued
  603. WHERE id = :quote_id
  604. FOR UPDATE
  605. "#,
  606. )?
  607. .bind("amount_issued", new_amount_issued.to_i64())
  608. .bind("quote_id", quote_id.as_hyphenated().to_string())
  609. .execute(&self.inner)
  610. .await
  611. .inspect_err(|err| {
  612. tracing::error!("SQLite could not update mint quote amount_issued: {}", err);
  613. })?;
  614. let current_time = unix_time();
  615. query(
  616. r#"
  617. INSERT INTO mint_quote_issued
  618. (quote_id, amount, timestamp)
  619. VALUES (:quote_id, :amount, :timestamp);
  620. "#,
  621. )?
  622. .bind("quote_id", quote_id.as_hyphenated().to_string())
  623. .bind("amount", amount_issued.to_i64())
  624. .bind("timestamp", current_time as i64)
  625. .execute(&self.inner)
  626. .await?;
  627. Ok(new_amount_issued)
  628. }
  629. #[instrument(skip_all)]
  630. async fn add_mint_quote(&mut self, quote: MintQuote) -> Result<(), Self::Err> {
  631. tracing::debug!("Adding quote with: {}", quote.payment_method);
  632. query(
  633. r#"
  634. INSERT INTO mint_quote (
  635. id, amount, unit, request, expiry, request_lookup_id, pubkey, created_time, payment_method, request_lookup_id_kind
  636. )
  637. VALUES (
  638. :id, :amount, :unit, :request, :expiry, :request_lookup_id, :pubkey, :created_time, :payment_method, :request_lookup_id_kind
  639. )
  640. "#,
  641. )?
  642. .bind("id", quote.id.to_string())
  643. .bind("amount", quote.amount.map(|a| a.to_i64()))
  644. .bind("unit", quote.unit.to_string())
  645. .bind("request", quote.request)
  646. .bind("expiry", quote.expiry as i64)
  647. .bind(
  648. "request_lookup_id",
  649. quote.request_lookup_id.to_string(),
  650. )
  651. .bind("pubkey", quote.pubkey.map(|p| p.to_string()))
  652. .bind("created_time", quote.created_time as i64)
  653. .bind("payment_method", quote.payment_method.to_string())
  654. .bind("request_lookup_id_kind", quote.request_lookup_id.kind())
  655. .execute(&self.inner)
  656. .await?;
  657. Ok(())
  658. }
  659. async fn remove_mint_quote(&mut self, quote_id: &Uuid) -> Result<(), Self::Err> {
  660. query(r#"DELETE FROM mint_quote WHERE id=:id"#)?
  661. .bind("id", quote_id.as_hyphenated().to_string())
  662. .execute(&self.inner)
  663. .await?;
  664. Ok(())
  665. }
  666. async fn add_melt_quote(&mut self, quote: mint::MeltQuote) -> Result<(), Self::Err> {
  667. // First try to find and replace any expired UNPAID quotes with the same request_lookup_id
  668. let current_time = unix_time();
  669. let row_affected = query(
  670. r#"
  671. DELETE FROM melt_quote
  672. WHERE request_lookup_id = :request_lookup_id
  673. AND state = :state
  674. AND expiry < :current_time
  675. "#,
  676. )?
  677. .bind("request_lookup_id", quote.request_lookup_id.to_string())
  678. .bind("state", MeltQuoteState::Unpaid.to_string())
  679. .bind("current_time", current_time as i64)
  680. .execute(&self.inner)
  681. .await?;
  682. if row_affected > 0 {
  683. tracing::info!("Received new melt quote for existing invoice with expired quote.");
  684. }
  685. // Now insert the new quote
  686. query(
  687. r#"
  688. INSERT INTO melt_quote
  689. (
  690. id, unit, amount, request, fee_reserve, state,
  691. expiry, payment_preimage, request_lookup_id,
  692. created_time, paid_time, options, request_lookup_id_kind, payment_method
  693. )
  694. VALUES
  695. (
  696. :id, :unit, :amount, :request, :fee_reserve, :state,
  697. :expiry, :payment_preimage, :request_lookup_id,
  698. :created_time, :paid_time, :options, :request_lookup_id_kind, :payment_method
  699. )
  700. "#,
  701. )?
  702. .bind("id", quote.id.to_string())
  703. .bind("unit", quote.unit.to_string())
  704. .bind("amount", quote.amount.to_i64())
  705. .bind("request", serde_json::to_string(&quote.request)?)
  706. .bind("fee_reserve", quote.fee_reserve.to_i64())
  707. .bind("state", quote.state.to_string())
  708. .bind("expiry", quote.expiry as i64)
  709. .bind("payment_preimage", quote.payment_preimage)
  710. .bind("request_lookup_id", quote.request_lookup_id.to_string())
  711. .bind("created_time", quote.created_time as i64)
  712. .bind("paid_time", quote.paid_time.map(|t| t as i64))
  713. .bind(
  714. "options",
  715. quote.options.map(|o| serde_json::to_string(&o).ok()),
  716. )
  717. .bind("request_lookup_id_kind", quote.request_lookup_id.kind())
  718. .bind("payment_method", quote.payment_method.to_string())
  719. .execute(&self.inner)
  720. .await?;
  721. Ok(())
  722. }
  723. async fn update_melt_quote_request_lookup_id(
  724. &mut self,
  725. quote_id: &Uuid,
  726. new_request_lookup_id: &PaymentIdentifier,
  727. ) -> Result<(), Self::Err> {
  728. query(r#"UPDATE melt_quote SET request_lookup_id = :new_req_id, request_lookup_id_kind = :new_kind WHERE id = :id"#)?
  729. .bind("new_req_id", new_request_lookup_id.to_string())
  730. .bind("new_kind",new_request_lookup_id.kind() )
  731. .bind("id", quote_id.as_hyphenated().to_string())
  732. .execute(&self.inner)
  733. .await?;
  734. Ok(())
  735. }
  736. async fn update_melt_quote_state(
  737. &mut self,
  738. quote_id: &Uuid,
  739. state: MeltQuoteState,
  740. payment_proof: Option<String>,
  741. ) -> Result<(MeltQuoteState, mint::MeltQuote), Self::Err> {
  742. let mut quote = query(
  743. r#"
  744. SELECT
  745. id,
  746. unit,
  747. amount,
  748. request,
  749. fee_reserve,
  750. expiry,
  751. state,
  752. payment_preimage,
  753. request_lookup_id,
  754. created_time,
  755. paid_time,
  756. payment_method,
  757. options,
  758. request_lookup_id_kind
  759. FROM
  760. melt_quote
  761. WHERE
  762. id=:id
  763. AND state != :state
  764. "#,
  765. )?
  766. .bind("id", quote_id.as_hyphenated().to_string())
  767. .bind("state", state.to_string())
  768. .fetch_one(&self.inner)
  769. .await?
  770. .map(sql_row_to_melt_quote)
  771. .transpose()?
  772. .ok_or(Error::QuoteNotFound)?;
  773. let rec = if state == MeltQuoteState::Paid {
  774. let current_time = unix_time();
  775. query(r#"UPDATE melt_quote SET state = :state, paid_time = :paid_time, payment_preimage = :payment_preimage WHERE id = :id"#)?
  776. .bind("state", state.to_string())
  777. .bind("paid_time", current_time as i64)
  778. .bind("payment_preimage", payment_proof)
  779. .bind("id", quote_id.as_hyphenated().to_string())
  780. .execute(&self.inner)
  781. .await
  782. } else {
  783. query(r#"UPDATE melt_quote SET state = :state WHERE id = :id"#)?
  784. .bind("state", state.to_string())
  785. .bind("id", quote_id.as_hyphenated().to_string())
  786. .execute(&self.inner)
  787. .await
  788. };
  789. match rec {
  790. Ok(_) => {}
  791. Err(err) => {
  792. tracing::error!("SQLite Could not update melt quote");
  793. return Err(err);
  794. }
  795. };
  796. let old_state = quote.state;
  797. quote.state = state;
  798. Ok((old_state, quote))
  799. }
  800. async fn remove_melt_quote(&mut self, quote_id: &Uuid) -> Result<(), Self::Err> {
  801. query(
  802. r#"
  803. DELETE FROM melt_quote
  804. WHERE id=?
  805. "#,
  806. )?
  807. .bind("id", quote_id.as_hyphenated().to_string())
  808. .execute(&self.inner)
  809. .await?;
  810. Ok(())
  811. }
  812. async fn get_mint_quote(&mut self, quote_id: &Uuid) -> Result<Option<MintQuote>, Self::Err> {
  813. let payments = get_mint_quote_payments(&self.inner, quote_id).await?;
  814. let issuance = get_mint_quote_issuance(&self.inner, quote_id).await?;
  815. Ok(query(
  816. r#"
  817. SELECT
  818. id,
  819. amount,
  820. unit,
  821. request,
  822. expiry,
  823. request_lookup_id,
  824. pubkey,
  825. created_time,
  826. amount_paid,
  827. amount_issued,
  828. payment_method,
  829. request_lookup_id_kind
  830. FROM
  831. mint_quote
  832. WHERE id = :id
  833. FOR UPDATE
  834. "#,
  835. )?
  836. .bind("id", quote_id.as_hyphenated().to_string())
  837. .fetch_one(&self.inner)
  838. .await?
  839. .map(|row| sql_row_to_mint_quote(row, payments, issuance))
  840. .transpose()?)
  841. }
  842. async fn get_melt_quote(
  843. &mut self,
  844. quote_id: &Uuid,
  845. ) -> Result<Option<mint::MeltQuote>, Self::Err> {
  846. Ok(query(
  847. r#"
  848. SELECT
  849. id,
  850. unit,
  851. amount,
  852. request,
  853. fee_reserve,
  854. expiry,
  855. state,
  856. payment_preimage,
  857. request_lookup_id,
  858. created_time,
  859. paid_time,
  860. payment_method,
  861. options,
  862. request_lookup_id
  863. FROM
  864. melt_quote
  865. WHERE
  866. id=:id
  867. "#,
  868. )?
  869. .bind("id", quote_id.as_hyphenated().to_string())
  870. .fetch_one(&self.inner)
  871. .await?
  872. .map(sql_row_to_melt_quote)
  873. .transpose()?)
  874. }
  875. async fn get_mint_quote_by_request(
  876. &mut self,
  877. request: &str,
  878. ) -> Result<Option<MintQuote>, Self::Err> {
  879. let mut mint_quote = query(
  880. r#"
  881. SELECT
  882. id,
  883. amount,
  884. unit,
  885. request,
  886. expiry,
  887. request_lookup_id,
  888. pubkey,
  889. created_time,
  890. amount_paid,
  891. amount_issued,
  892. payment_method,
  893. request_lookup_id_kind
  894. FROM
  895. mint_quote
  896. WHERE request = :request
  897. FOR UPDATE
  898. "#,
  899. )?
  900. .bind("request", request.to_string())
  901. .fetch_one(&self.inner)
  902. .await?
  903. .map(|row| sql_row_to_mint_quote(row, vec![], vec![]))
  904. .transpose()?;
  905. if let Some(quote) = mint_quote.as_mut() {
  906. let payments = get_mint_quote_payments(&self.inner, &quote.id).await?;
  907. let issuance = get_mint_quote_issuance(&self.inner, &quote.id).await?;
  908. quote.issuance = issuance;
  909. quote.payments = payments;
  910. }
  911. Ok(mint_quote)
  912. }
  913. async fn get_mint_quote_by_request_lookup_id(
  914. &mut self,
  915. request_lookup_id: &PaymentIdentifier,
  916. ) -> Result<Option<MintQuote>, Self::Err> {
  917. let mut mint_quote = query(
  918. r#"
  919. SELECT
  920. id,
  921. amount,
  922. unit,
  923. request,
  924. expiry,
  925. request_lookup_id,
  926. pubkey,
  927. created_time,
  928. amount_paid,
  929. amount_issued,
  930. payment_method,
  931. request_lookup_id_kind
  932. FROM
  933. mint_quote
  934. WHERE request_lookup_id = :request_lookup_id
  935. AND request_lookup_id_kind = :request_lookup_id_kind
  936. FOR UPDATE
  937. "#,
  938. )?
  939. .bind("request_lookup_id", request_lookup_id.to_string())
  940. .bind("request_lookup_id_kind", request_lookup_id.kind())
  941. .fetch_one(&self.inner)
  942. .await?
  943. .map(|row| sql_row_to_mint_quote(row, vec![], vec![]))
  944. .transpose()?;
  945. if let Some(quote) = mint_quote.as_mut() {
  946. let payments = get_mint_quote_payments(&self.inner, &quote.id).await?;
  947. let issuance = get_mint_quote_issuance(&self.inner, &quote.id).await?;
  948. quote.issuance = issuance;
  949. quote.payments = payments;
  950. }
  951. Ok(mint_quote)
  952. }
  953. }
  954. #[async_trait]
  955. impl<RM> MintQuotesDatabase for SQLMintDatabase<RM>
  956. where
  957. RM: DatabasePool + 'static,
  958. {
  959. type Err = Error;
  960. async fn get_mint_quote(&self, quote_id: &Uuid) -> Result<Option<MintQuote>, Self::Err> {
  961. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  962. let payments = get_mint_quote_payments(&*conn, quote_id).await?;
  963. let issuance = get_mint_quote_issuance(&*conn, quote_id).await?;
  964. Ok(query(
  965. r#"
  966. SELECT
  967. id,
  968. amount,
  969. unit,
  970. request,
  971. expiry,
  972. request_lookup_id,
  973. pubkey,
  974. created_time,
  975. amount_paid,
  976. amount_issued,
  977. payment_method,
  978. request_lookup_id_kind
  979. FROM
  980. mint_quote
  981. WHERE id = :id"#,
  982. )?
  983. .bind("id", quote_id.as_hyphenated().to_string())
  984. .fetch_one(&*conn)
  985. .await?
  986. .map(|row| sql_row_to_mint_quote(row, payments, issuance))
  987. .transpose()?)
  988. }
  989. async fn get_mint_quote_by_request(
  990. &self,
  991. request: &str,
  992. ) -> Result<Option<MintQuote>, Self::Err> {
  993. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  994. let mut mint_quote = query(
  995. r#"
  996. SELECT
  997. id,
  998. amount,
  999. unit,
  1000. request,
  1001. expiry,
  1002. request_lookup_id,
  1003. pubkey,
  1004. created_time,
  1005. amount_paid,
  1006. amount_issued,
  1007. payment_method,
  1008. request_lookup_id_kind
  1009. FROM
  1010. mint_quote
  1011. WHERE request = :request"#,
  1012. )?
  1013. .bind("request", request.to_owned())
  1014. .fetch_one(&*conn)
  1015. .await?
  1016. .map(|row| sql_row_to_mint_quote(row, vec![], vec![]))
  1017. .transpose()?;
  1018. if let Some(quote) = mint_quote.as_mut() {
  1019. let payments = get_mint_quote_payments(&*conn, &quote.id).await?;
  1020. let issuance = get_mint_quote_issuance(&*conn, &quote.id).await?;
  1021. quote.issuance = issuance;
  1022. quote.payments = payments;
  1023. }
  1024. Ok(mint_quote)
  1025. }
  1026. async fn get_mint_quote_by_request_lookup_id(
  1027. &self,
  1028. request_lookup_id: &PaymentIdentifier,
  1029. ) -> Result<Option<MintQuote>, Self::Err> {
  1030. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1031. let mut mint_quote = query(
  1032. r#"
  1033. SELECT
  1034. id,
  1035. amount,
  1036. unit,
  1037. request,
  1038. expiry,
  1039. request_lookup_id,
  1040. pubkey,
  1041. created_time,
  1042. amount_paid,
  1043. amount_issued,
  1044. payment_method,
  1045. request_lookup_id_kind
  1046. FROM
  1047. mint_quote
  1048. WHERE request_lookup_id = :request_lookup_id
  1049. AND request_lookup_id_kind = :request_lookup_id_kind
  1050. "#,
  1051. )?
  1052. .bind("request_lookup_id", request_lookup_id.to_string())
  1053. .bind("request_lookup_id_kind", request_lookup_id.kind())
  1054. .fetch_one(&*conn)
  1055. .await?
  1056. .map(|row| sql_row_to_mint_quote(row, vec![], vec![]))
  1057. .transpose()?;
  1058. // TODO: these should use an sql join so they can be done in one query
  1059. if let Some(quote) = mint_quote.as_mut() {
  1060. let payments = get_mint_quote_payments(&*conn, &quote.id).await?;
  1061. let issuance = get_mint_quote_issuance(&*conn, &quote.id).await?;
  1062. quote.issuance = issuance;
  1063. quote.payments = payments;
  1064. }
  1065. Ok(mint_quote)
  1066. }
  1067. async fn get_mint_quotes(&self) -> Result<Vec<MintQuote>, Self::Err> {
  1068. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1069. let mut mint_quotes = query(
  1070. r#"
  1071. SELECT
  1072. id,
  1073. amount,
  1074. unit,
  1075. request,
  1076. expiry,
  1077. request_lookup_id,
  1078. pubkey,
  1079. created_time,
  1080. amount_paid,
  1081. amount_issued,
  1082. payment_method,
  1083. request_lookup_id_kind
  1084. FROM
  1085. mint_quote
  1086. "#,
  1087. )?
  1088. .fetch_all(&*conn)
  1089. .await?
  1090. .into_iter()
  1091. .map(|row| sql_row_to_mint_quote(row, vec![], vec![]))
  1092. .collect::<Result<Vec<_>, _>>()?;
  1093. for quote in mint_quotes.as_mut_slice() {
  1094. let payments = get_mint_quote_payments(&*conn, &quote.id).await?;
  1095. let issuance = get_mint_quote_issuance(&*conn, &quote.id).await?;
  1096. quote.issuance = issuance;
  1097. quote.payments = payments;
  1098. }
  1099. Ok(mint_quotes)
  1100. }
  1101. async fn get_melt_quote(&self, quote_id: &Uuid) -> Result<Option<mint::MeltQuote>, Self::Err> {
  1102. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1103. Ok(query(
  1104. r#"
  1105. SELECT
  1106. id,
  1107. unit,
  1108. amount,
  1109. request,
  1110. fee_reserve,
  1111. expiry,
  1112. state,
  1113. payment_preimage,
  1114. request_lookup_id,
  1115. created_time,
  1116. paid_time,
  1117. payment_method,
  1118. options,
  1119. request_lookup_id_kind
  1120. FROM
  1121. melt_quote
  1122. WHERE
  1123. id=:id
  1124. "#,
  1125. )?
  1126. .bind("id", quote_id.as_hyphenated().to_string())
  1127. .fetch_one(&*conn)
  1128. .await?
  1129. .map(sql_row_to_melt_quote)
  1130. .transpose()?)
  1131. }
  1132. async fn get_melt_quotes(&self) -> Result<Vec<mint::MeltQuote>, Self::Err> {
  1133. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1134. Ok(query(
  1135. r#"
  1136. SELECT
  1137. id,
  1138. unit,
  1139. amount,
  1140. request,
  1141. fee_reserve,
  1142. expiry,
  1143. state,
  1144. payment_preimage,
  1145. request_lookup_id,
  1146. created_time,
  1147. paid_time,
  1148. payment_method,
  1149. options,
  1150. request_lookup_id_kind
  1151. FROM
  1152. melt_quote
  1153. "#,
  1154. )?
  1155. .fetch_all(&*conn)
  1156. .await?
  1157. .into_iter()
  1158. .map(sql_row_to_melt_quote)
  1159. .collect::<Result<Vec<_>, _>>()?)
  1160. }
  1161. }
  1162. #[async_trait]
  1163. impl<RM> MintProofsDatabase for SQLMintDatabase<RM>
  1164. where
  1165. RM: DatabasePool + 'static,
  1166. {
  1167. type Err = Error;
  1168. async fn get_proofs_by_ys(&self, ys: &[PublicKey]) -> Result<Vec<Option<Proof>>, Self::Err> {
  1169. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1170. let mut proofs = query(
  1171. r#"
  1172. SELECT
  1173. amount,
  1174. keyset_id,
  1175. secret,
  1176. c,
  1177. witness,
  1178. y
  1179. FROM
  1180. proof
  1181. WHERE
  1182. y IN (:ys)
  1183. "#,
  1184. )?
  1185. .bind_vec("ys", ys.iter().map(|y| y.to_bytes().to_vec()).collect())
  1186. .fetch_all(&*conn)
  1187. .await?
  1188. .into_iter()
  1189. .map(|mut row| {
  1190. Ok((
  1191. column_as_string!(
  1192. row.pop().ok_or(Error::InvalidDbResponse)?,
  1193. PublicKey::from_hex,
  1194. PublicKey::from_slice
  1195. ),
  1196. sql_row_to_proof(row)?,
  1197. ))
  1198. })
  1199. .collect::<Result<HashMap<_, _>, Error>>()?;
  1200. Ok(ys.iter().map(|y| proofs.remove(y)).collect())
  1201. }
  1202. async fn get_proof_ys_by_quote_id(&self, quote_id: &Uuid) -> Result<Vec<PublicKey>, Self::Err> {
  1203. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1204. Ok(query(
  1205. r#"
  1206. SELECT
  1207. amount,
  1208. keyset_id,
  1209. secret,
  1210. c,
  1211. witness
  1212. FROM
  1213. proof
  1214. WHERE
  1215. quote_id = :quote_id
  1216. "#,
  1217. )?
  1218. .bind("quote_id", quote_id.as_hyphenated().to_string())
  1219. .fetch_all(&*conn)
  1220. .await?
  1221. .into_iter()
  1222. .map(sql_row_to_proof)
  1223. .collect::<Result<Vec<Proof>, _>>()?
  1224. .ys()?)
  1225. }
  1226. async fn get_proofs_states(&self, ys: &[PublicKey]) -> Result<Vec<Option<State>>, Self::Err> {
  1227. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1228. let mut current_states = get_current_states(&*conn, ys).await?;
  1229. Ok(ys.iter().map(|y| current_states.remove(y)).collect())
  1230. }
  1231. async fn get_proofs_by_keyset_id(
  1232. &self,
  1233. keyset_id: &Id,
  1234. ) -> Result<(Proofs, Vec<Option<State>>), Self::Err> {
  1235. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1236. Ok(query(
  1237. r#"
  1238. SELECT
  1239. keyset_id,
  1240. amount,
  1241. secret,
  1242. c,
  1243. witness,
  1244. state
  1245. FROM
  1246. proof
  1247. WHERE
  1248. keyset_id=?
  1249. "#,
  1250. )?
  1251. .bind("keyset_id", keyset_id.to_string())
  1252. .fetch_all(&*conn)
  1253. .await?
  1254. .into_iter()
  1255. .map(sql_row_to_proof_with_state)
  1256. .collect::<Result<Vec<_>, _>>()?
  1257. .into_iter()
  1258. .unzip())
  1259. }
  1260. }
  1261. #[async_trait]
  1262. impl<RM> MintSignatureTransaction<'_> for SQLTransaction<RM>
  1263. where
  1264. RM: DatabasePool + 'static,
  1265. {
  1266. type Err = Error;
  1267. async fn add_blind_signatures(
  1268. &mut self,
  1269. blinded_messages: &[PublicKey],
  1270. blind_signatures: &[BlindSignature],
  1271. quote_id: Option<Uuid>,
  1272. ) -> Result<(), Self::Err> {
  1273. let current_time = unix_time();
  1274. for (message, signature) in blinded_messages.iter().zip(blind_signatures) {
  1275. query(
  1276. r#"
  1277. INSERT INTO blind_signature
  1278. (blinded_message, amount, keyset_id, c, quote_id, dleq_e, dleq_s, created_time)
  1279. VALUES
  1280. (:blinded_message, :amount, :keyset_id, :c, :quote_id, :dleq_e, :dleq_s, :created_time)
  1281. "#,
  1282. )?
  1283. .bind("blinded_message", message.to_bytes().to_vec())
  1284. .bind("amount", u64::from(signature.amount) as i64)
  1285. .bind("keyset_id", signature.keyset_id.to_string())
  1286. .bind("c", signature.c.to_bytes().to_vec())
  1287. .bind("quote_id", quote_id.map(|q| q.hyphenated().to_string()))
  1288. .bind(
  1289. "dleq_e",
  1290. signature.dleq.as_ref().map(|dleq| dleq.e.to_secret_hex()),
  1291. )
  1292. .bind(
  1293. "dleq_s",
  1294. signature.dleq.as_ref().map(|dleq| dleq.s.to_secret_hex()),
  1295. )
  1296. .bind("created_time", current_time as i64)
  1297. .execute(&self.inner)
  1298. .await?;
  1299. }
  1300. Ok(())
  1301. }
  1302. async fn get_blind_signatures(
  1303. &mut self,
  1304. blinded_messages: &[PublicKey],
  1305. ) -> Result<Vec<Option<BlindSignature>>, Self::Err> {
  1306. let mut blinded_signatures = query(
  1307. r#"SELECT
  1308. keyset_id,
  1309. amount,
  1310. c,
  1311. dleq_e,
  1312. dleq_s,
  1313. blinded_message
  1314. FROM
  1315. blind_signature
  1316. WHERE blinded_message IN (:y)
  1317. "#,
  1318. )?
  1319. .bind_vec(
  1320. "y",
  1321. blinded_messages
  1322. .iter()
  1323. .map(|y| y.to_bytes().to_vec())
  1324. .collect(),
  1325. )
  1326. .fetch_all(&self.inner)
  1327. .await?
  1328. .into_iter()
  1329. .map(|mut row| {
  1330. Ok((
  1331. column_as_string!(
  1332. &row.pop().ok_or(Error::InvalidDbResponse)?,
  1333. PublicKey::from_hex,
  1334. PublicKey::from_slice
  1335. ),
  1336. sql_row_to_blind_signature(row)?,
  1337. ))
  1338. })
  1339. .collect::<Result<HashMap<_, _>, Error>>()?;
  1340. Ok(blinded_messages
  1341. .iter()
  1342. .map(|y| blinded_signatures.remove(y))
  1343. .collect())
  1344. }
  1345. }
  1346. #[async_trait]
  1347. impl<RM> MintSignaturesDatabase for SQLMintDatabase<RM>
  1348. where
  1349. RM: DatabasePool + 'static,
  1350. {
  1351. type Err = Error;
  1352. async fn get_blind_signatures(
  1353. &self,
  1354. blinded_messages: &[PublicKey],
  1355. ) -> Result<Vec<Option<BlindSignature>>, Self::Err> {
  1356. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1357. let mut blinded_signatures = query(
  1358. r#"SELECT
  1359. keyset_id,
  1360. amount,
  1361. c,
  1362. dleq_e,
  1363. dleq_s,
  1364. blinded_message
  1365. FROM
  1366. blind_signature
  1367. WHERE blinded_message IN (:blinded_message)
  1368. "#,
  1369. )?
  1370. .bind_vec(
  1371. "blinded_message",
  1372. blinded_messages
  1373. .iter()
  1374. .map(|b_| b_.to_bytes().to_vec())
  1375. .collect(),
  1376. )
  1377. .fetch_all(&*conn)
  1378. .await?
  1379. .into_iter()
  1380. .map(|mut row| {
  1381. Ok((
  1382. column_as_string!(
  1383. &row.pop().ok_or(Error::InvalidDbResponse)?,
  1384. PublicKey::from_hex,
  1385. PublicKey::from_slice
  1386. ),
  1387. sql_row_to_blind_signature(row)?,
  1388. ))
  1389. })
  1390. .collect::<Result<HashMap<_, _>, Error>>()?;
  1391. Ok(blinded_messages
  1392. .iter()
  1393. .map(|y| blinded_signatures.remove(y))
  1394. .collect())
  1395. }
  1396. async fn get_blind_signatures_for_keyset(
  1397. &self,
  1398. keyset_id: &Id,
  1399. ) -> Result<Vec<BlindSignature>, Self::Err> {
  1400. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1401. Ok(query(
  1402. r#"
  1403. SELECT
  1404. keyset_id,
  1405. amount,
  1406. c,
  1407. dleq_e,
  1408. dleq_s
  1409. FROM
  1410. blind_signature
  1411. WHERE
  1412. keyset_id=:keyset_id
  1413. "#,
  1414. )?
  1415. .bind("keyset_id", keyset_id.to_string())
  1416. .fetch_all(&*conn)
  1417. .await?
  1418. .into_iter()
  1419. .map(sql_row_to_blind_signature)
  1420. .collect::<Result<Vec<BlindSignature>, _>>()?)
  1421. }
  1422. /// Get [`BlindSignature`]s for quote
  1423. async fn get_blind_signatures_for_quote(
  1424. &self,
  1425. quote_id: &Uuid,
  1426. ) -> Result<Vec<BlindSignature>, Self::Err> {
  1427. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1428. Ok(query(
  1429. r#"
  1430. SELECT
  1431. keyset_id,
  1432. amount,
  1433. c,
  1434. dleq_e,
  1435. dleq_s
  1436. FROM
  1437. blind_signature
  1438. WHERE
  1439. quote_id=:quote_id
  1440. "#,
  1441. )?
  1442. .bind("quote_id", quote_id.to_string())
  1443. .fetch_all(&*conn)
  1444. .await?
  1445. .into_iter()
  1446. .map(sql_row_to_blind_signature)
  1447. .collect::<Result<Vec<BlindSignature>, _>>()?)
  1448. }
  1449. }
  1450. #[async_trait]
  1451. impl<RM> MintDatabase<Error> for SQLMintDatabase<RM>
  1452. where
  1453. RM: DatabasePool + 'static,
  1454. {
  1455. async fn begin_transaction<'a>(
  1456. &'a self,
  1457. ) -> Result<Box<dyn database::MintTransaction<'a, Error> + Send + Sync + 'a>, Error> {
  1458. Ok(Box::new(SQLTransaction {
  1459. inner: ConnectionWithTransaction::new(
  1460. self.pool.get().map_err(|e| Error::Database(Box::new(e)))?,
  1461. )
  1462. .await?,
  1463. }))
  1464. }
  1465. async fn get_mint_info(&self) -> Result<MintInfo, Error> {
  1466. Ok(self.fetch_from_config("mint_info").await?)
  1467. }
  1468. async fn get_quote_ttl(&self) -> Result<QuoteTTL, Error> {
  1469. Ok(self.fetch_from_config("quote_ttl").await?)
  1470. }
  1471. }
  1472. fn sql_row_to_keyset_info(row: Vec<Column>) -> Result<MintKeySetInfo, Error> {
  1473. unpack_into!(
  1474. let (
  1475. id,
  1476. unit,
  1477. active,
  1478. valid_from,
  1479. valid_to,
  1480. derivation_path,
  1481. derivation_path_index,
  1482. max_order,
  1483. row_keyset_ppk
  1484. ) = row
  1485. );
  1486. Ok(MintKeySetInfo {
  1487. id: column_as_string!(id, Id::from_str, Id::from_bytes),
  1488. unit: column_as_string!(unit, CurrencyUnit::from_str),
  1489. active: matches!(active, Column::Integer(1)),
  1490. valid_from: column_as_number!(valid_from),
  1491. derivation_path: column_as_string!(derivation_path, DerivationPath::from_str),
  1492. derivation_path_index: column_as_nullable_number!(derivation_path_index),
  1493. max_order: column_as_number!(max_order),
  1494. input_fee_ppk: column_as_number!(row_keyset_ppk),
  1495. final_expiry: column_as_nullable_number!(valid_to),
  1496. })
  1497. }
  1498. #[instrument(skip_all)]
  1499. fn sql_row_to_mint_quote(
  1500. row: Vec<Column>,
  1501. payments: Vec<IncomingPayment>,
  1502. issueances: Vec<Issuance>,
  1503. ) -> Result<MintQuote, Error> {
  1504. unpack_into!(
  1505. let (
  1506. id, amount, unit, request, expiry, request_lookup_id,
  1507. pubkey, created_time, amount_paid, amount_issued, payment_method, request_lookup_id_kind
  1508. ) = row
  1509. );
  1510. let request_str = column_as_string!(&request);
  1511. let request_lookup_id = column_as_nullable_string!(&request_lookup_id).unwrap_or_else(|| {
  1512. Bolt11Invoice::from_str(&request_str)
  1513. .map(|invoice| invoice.payment_hash().to_string())
  1514. .unwrap_or_else(|_| request_str.clone())
  1515. });
  1516. let request_lookup_id_kind = column_as_string!(request_lookup_id_kind);
  1517. let pubkey = column_as_nullable_string!(&pubkey)
  1518. .map(|pk| PublicKey::from_hex(&pk))
  1519. .transpose()?;
  1520. let id = column_as_string!(id);
  1521. let amount: Option<u64> = column_as_nullable_number!(amount);
  1522. let amount_paid: u64 = column_as_number!(amount_paid);
  1523. let amount_issued: u64 = column_as_number!(amount_issued);
  1524. let payment_method = column_as_string!(payment_method, PaymentMethod::from_str);
  1525. Ok(MintQuote::new(
  1526. Some(Uuid::parse_str(&id).map_err(|_| Error::InvalidUuid(id))?),
  1527. request_str,
  1528. column_as_string!(unit, CurrencyUnit::from_str),
  1529. amount.map(Amount::from),
  1530. column_as_number!(expiry),
  1531. PaymentIdentifier::new(&request_lookup_id_kind, &request_lookup_id)
  1532. .map_err(|_| ConversionError::MissingParameter("Payment id".to_string()))?,
  1533. pubkey,
  1534. amount_paid.into(),
  1535. amount_issued.into(),
  1536. payment_method,
  1537. column_as_number!(created_time),
  1538. payments,
  1539. issueances,
  1540. ))
  1541. }
  1542. fn sql_row_to_melt_quote(row: Vec<Column>) -> Result<mint::MeltQuote, Error> {
  1543. unpack_into!(
  1544. let (
  1545. id,
  1546. unit,
  1547. amount,
  1548. request,
  1549. fee_reserve,
  1550. expiry,
  1551. state,
  1552. payment_preimage,
  1553. request_lookup_id,
  1554. created_time,
  1555. paid_time,
  1556. payment_method,
  1557. options,
  1558. request_lookup_id_kind
  1559. ) = row
  1560. );
  1561. let id = column_as_string!(id);
  1562. let amount: u64 = column_as_number!(amount);
  1563. let fee_reserve: u64 = column_as_number!(fee_reserve);
  1564. let expiry = column_as_number!(expiry);
  1565. let payment_preimage = column_as_nullable_string!(payment_preimage);
  1566. let options = column_as_nullable_string!(options);
  1567. let options = options.and_then(|o| serde_json::from_str(&o).ok());
  1568. let created_time: i64 = column_as_number!(created_time);
  1569. let paid_time = column_as_nullable_number!(paid_time);
  1570. let payment_method = PaymentMethod::from_str(&column_as_string!(payment_method))?;
  1571. let state =
  1572. MeltQuoteState::from_str(&column_as_string!(&state)).map_err(ConversionError::from)?;
  1573. let unit = column_as_string!(unit);
  1574. let request = column_as_string!(request);
  1575. let mut request_lookup_id_kind = column_as_string!(request_lookup_id_kind);
  1576. let request_lookup_id = column_as_nullable_string!(&request_lookup_id).unwrap_or_else(|| {
  1577. Bolt11Invoice::from_str(&request)
  1578. .map(|invoice| invoice.payment_hash().to_string())
  1579. .unwrap_or_else(|_| {
  1580. request_lookup_id_kind = "custom".to_string();
  1581. request.clone()
  1582. })
  1583. });
  1584. let request_lookup_id = PaymentIdentifier::new(&request_lookup_id_kind, &request_lookup_id)
  1585. .map_err(|_| ConversionError::MissingParameter("Payment id".to_string()))?;
  1586. let request = match serde_json::from_str(&request) {
  1587. Ok(req) => req,
  1588. Err(err) => {
  1589. tracing::debug!(
  1590. "Melt quote from pre migrations defaulting to bolt11 {}.",
  1591. err
  1592. );
  1593. let bolt11 = Bolt11Invoice::from_str(&request).unwrap();
  1594. MeltPaymentRequest::Bolt11 { bolt11 }
  1595. }
  1596. };
  1597. Ok(MeltQuote {
  1598. id: Uuid::parse_str(&id).map_err(|_| Error::InvalidUuid(id))?,
  1599. unit: CurrencyUnit::from_str(&unit)?,
  1600. amount: Amount::from(amount),
  1601. request,
  1602. fee_reserve: Amount::from(fee_reserve),
  1603. state,
  1604. expiry,
  1605. payment_preimage,
  1606. request_lookup_id,
  1607. options,
  1608. created_time: created_time as u64,
  1609. paid_time,
  1610. payment_method,
  1611. })
  1612. }
  1613. fn sql_row_to_proof(row: Vec<Column>) -> Result<Proof, Error> {
  1614. unpack_into!(
  1615. let (
  1616. amount,
  1617. keyset_id,
  1618. secret,
  1619. c,
  1620. witness
  1621. ) = row
  1622. );
  1623. let amount: u64 = column_as_number!(amount);
  1624. Ok(Proof {
  1625. amount: Amount::from(amount),
  1626. keyset_id: column_as_string!(keyset_id, Id::from_str),
  1627. secret: column_as_string!(secret, Secret::from_str),
  1628. c: column_as_string!(c, PublicKey::from_hex, PublicKey::from_slice),
  1629. witness: column_as_nullable_string!(witness).and_then(|w| serde_json::from_str(&w).ok()),
  1630. dleq: None,
  1631. })
  1632. }
  1633. fn sql_row_to_proof_with_state(row: Vec<Column>) -> Result<(Proof, Option<State>), Error> {
  1634. unpack_into!(
  1635. let (
  1636. keyset_id, amount, secret, c, witness, state
  1637. ) = row
  1638. );
  1639. let amount: u64 = column_as_number!(amount);
  1640. let state = column_as_nullable_string!(state).and_then(|s| State::from_str(&s).ok());
  1641. Ok((
  1642. Proof {
  1643. amount: Amount::from(amount),
  1644. keyset_id: column_as_string!(keyset_id, Id::from_str, Id::from_bytes),
  1645. secret: column_as_string!(secret, Secret::from_str),
  1646. c: column_as_string!(c, PublicKey::from_hex, PublicKey::from_slice),
  1647. witness: column_as_nullable_string!(witness)
  1648. .and_then(|w| serde_json::from_str(&w).ok()),
  1649. dleq: None,
  1650. },
  1651. state,
  1652. ))
  1653. }
  1654. fn sql_row_to_blind_signature(row: Vec<Column>) -> Result<BlindSignature, Error> {
  1655. unpack_into!(
  1656. let (
  1657. keyset_id, amount, c, dleq_e, dleq_s
  1658. ) = row
  1659. );
  1660. let dleq = match (
  1661. column_as_nullable_string!(dleq_e),
  1662. column_as_nullable_string!(dleq_s),
  1663. ) {
  1664. (Some(e), Some(s)) => Some(BlindSignatureDleq {
  1665. e: SecretKey::from_hex(e)?,
  1666. s: SecretKey::from_hex(s)?,
  1667. }),
  1668. _ => None,
  1669. };
  1670. let amount: u64 = column_as_number!(amount);
  1671. Ok(BlindSignature {
  1672. amount: Amount::from(amount),
  1673. keyset_id: column_as_string!(keyset_id, Id::from_str, Id::from_bytes),
  1674. c: column_as_string!(c, PublicKey::from_hex, PublicKey::from_slice),
  1675. dleq,
  1676. })
  1677. }