mod.rs 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822
  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 IS :unit"#)?
  371. .bind("unit", unit.to_string())
  372. .execute(&self.inner)
  373. .await?;
  374. query(r#"UPDATE keyset SET active=TRUE WHERE unit IS :unit AND id IS :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 = 1 AND unit IS :unit"#)?
  402. .bind("unit", unit.to_string())
  403. .pluck(&*conn)
  404. .await?
  405. .map(|id| match id {
  406. Column::Text(text) => Ok(Id::from_str(&text)?),
  407. Column::Blob(id) => Ok(Id::from_bytes(&id)?),
  408. _ => Err(Error::InvalidKeysetId),
  409. })
  410. .transpose()?,
  411. )
  412. }
  413. async fn get_active_keysets(&self) -> Result<HashMap<CurrencyUnit, Id>, Self::Err> {
  414. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  415. Ok(query(r#"SELECT id, unit FROM keyset WHERE active = 1"#)?
  416. .fetch_all(&*conn)
  417. .await?
  418. .into_iter()
  419. .map(|row| {
  420. Ok((
  421. column_as_string!(&row[1], CurrencyUnit::from_str),
  422. column_as_string!(&row[0], Id::from_str, Id::from_bytes),
  423. ))
  424. })
  425. .collect::<Result<HashMap<_, _>, Error>>()?)
  426. }
  427. async fn get_keyset_info(&self, id: &Id) -> Result<Option<MintKeySetInfo>, Self::Err> {
  428. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  429. Ok(query(
  430. r#"SELECT
  431. id,
  432. unit,
  433. active,
  434. valid_from,
  435. valid_to,
  436. derivation_path,
  437. derivation_path_index,
  438. max_order,
  439. input_fee_ppk
  440. FROM
  441. keyset
  442. WHERE id=:id"#,
  443. )?
  444. .bind("id", id.to_string())
  445. .fetch_one(&*conn)
  446. .await?
  447. .map(sql_row_to_keyset_info)
  448. .transpose()?)
  449. }
  450. async fn get_keyset_infos(&self) -> Result<Vec<MintKeySetInfo>, Self::Err> {
  451. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  452. Ok(query(
  453. r#"SELECT
  454. id,
  455. unit,
  456. active,
  457. valid_from,
  458. valid_to,
  459. derivation_path,
  460. derivation_path_index,
  461. max_order,
  462. input_fee_ppk
  463. FROM
  464. keyset
  465. "#,
  466. )?
  467. .fetch_all(&*conn)
  468. .await?
  469. .into_iter()
  470. .map(sql_row_to_keyset_info)
  471. .collect::<Result<Vec<_>, _>>()?)
  472. }
  473. }
  474. #[async_trait]
  475. impl<RM> MintQuotesTransaction<'_> for SQLTransaction<RM>
  476. where
  477. RM: DatabasePool + 'static,
  478. {
  479. type Err = Error;
  480. #[instrument(skip(self))]
  481. async fn increment_mint_quote_amount_paid(
  482. &mut self,
  483. quote_id: &Uuid,
  484. amount_paid: Amount,
  485. payment_id: String,
  486. ) -> Result<Amount, Self::Err> {
  487. // Check if payment_id already exists in mint_quote_payments
  488. let exists = query(
  489. r#"
  490. SELECT payment_id
  491. FROM mint_quote_payments
  492. WHERE payment_id = :payment_id
  493. FOR UPDATE
  494. "#,
  495. )?
  496. .bind("payment_id", payment_id.clone())
  497. .fetch_one(&self.inner)
  498. .await?;
  499. if exists.is_some() {
  500. tracing::error!("Payment ID already exists: {}", payment_id);
  501. return Err(database::Error::Duplicate);
  502. }
  503. // Get current amount_paid from quote
  504. let current_amount = query(
  505. r#"
  506. SELECT amount_paid
  507. FROM mint_quote
  508. WHERE id = :quote_id
  509. FOR UPDATE
  510. "#,
  511. )?
  512. .bind("quote_id", quote_id.as_hyphenated().to_string())
  513. .fetch_one(&self.inner)
  514. .await
  515. .inspect_err(|err| {
  516. tracing::error!("SQLite could not get mint quote amount_paid: {}", err);
  517. })?;
  518. let current_amount_paid = if let Some(current_amount) = current_amount {
  519. let amount: u64 = column_as_number!(current_amount[0].clone());
  520. Amount::from(amount)
  521. } else {
  522. Amount::ZERO
  523. };
  524. // Calculate new amount_paid with overflow check
  525. let new_amount_paid = current_amount_paid
  526. .checked_add(amount_paid)
  527. .ok_or_else(|| database::Error::AmountOverflow)?;
  528. // Update the amount_paid
  529. query(
  530. r#"
  531. UPDATE mint_quote
  532. SET amount_paid = :amount_paid
  533. WHERE id = :quote_id
  534. "#,
  535. )?
  536. .bind("amount_paid", new_amount_paid.to_i64())
  537. .bind("quote_id", quote_id.as_hyphenated().to_string())
  538. .execute(&self.inner)
  539. .await
  540. .inspect_err(|err| {
  541. tracing::error!("SQLite could not update mint quote amount_paid: {}", err);
  542. })?;
  543. // Add payment_id to mint_quote_payments table
  544. query(
  545. r#"
  546. INSERT INTO mint_quote_payments
  547. (quote_id, payment_id, amount, timestamp)
  548. VALUES (:quote_id, :payment_id, :amount, :timestamp)
  549. "#,
  550. )?
  551. .bind("quote_id", quote_id.as_hyphenated().to_string())
  552. .bind("payment_id", payment_id)
  553. .bind("amount", amount_paid.to_i64())
  554. .bind("timestamp", unix_time() as i64)
  555. .execute(&self.inner)
  556. .await
  557. .map_err(|err| {
  558. tracing::error!("SQLite could not insert payment ID: {}", err);
  559. err
  560. })?;
  561. Ok(new_amount_paid)
  562. }
  563. #[instrument(skip_all)]
  564. async fn increment_mint_quote_amount_issued(
  565. &mut self,
  566. quote_id: &Uuid,
  567. amount_issued: Amount,
  568. ) -> Result<Amount, Self::Err> {
  569. // Get current amount_issued from quote
  570. let current_amount = query(
  571. r#"
  572. SELECT amount_issued
  573. FROM mint_quote
  574. WHERE id = :quote_id
  575. FOR UPDATE
  576. "#,
  577. )?
  578. .bind("quote_id", quote_id.as_hyphenated().to_string())
  579. .fetch_one(&self.inner)
  580. .await
  581. .inspect_err(|err| {
  582. tracing::error!("SQLite could not get mint quote amount_issued: {}", err);
  583. })?;
  584. let current_amount_issued = if let Some(current_amount) = current_amount {
  585. let amount: u64 = column_as_number!(current_amount[0].clone());
  586. Amount::from(amount)
  587. } else {
  588. Amount::ZERO
  589. };
  590. // Calculate new amount_issued with overflow check
  591. let new_amount_issued = current_amount_issued
  592. .checked_add(amount_issued)
  593. .ok_or_else(|| database::Error::AmountOverflow)?;
  594. // Update the amount_issued
  595. query(
  596. r#"
  597. UPDATE mint_quote
  598. SET amount_issued = :amount_issued
  599. WHERE id = :quote_id
  600. FOR UPDATE
  601. "#,
  602. )?
  603. .bind("amount_issued", new_amount_issued.to_i64())
  604. .bind("quote_id", quote_id.as_hyphenated().to_string())
  605. .execute(&self.inner)
  606. .await
  607. .inspect_err(|err| {
  608. tracing::error!("SQLite could not update mint quote amount_issued: {}", err);
  609. })?;
  610. let current_time = unix_time();
  611. query(
  612. r#"
  613. INSERT INTO mint_quote_issued
  614. (quote_id, amount, timestamp)
  615. VALUES (:quote_id, :amount, :timestamp);
  616. "#,
  617. )?
  618. .bind("quote_id", quote_id.as_hyphenated().to_string())
  619. .bind("amount", amount_issued.to_i64())
  620. .bind("timestamp", current_time as i64)
  621. .execute(&self.inner)
  622. .await?;
  623. Ok(new_amount_issued)
  624. }
  625. #[instrument(skip_all)]
  626. async fn add_mint_quote(&mut self, quote: MintQuote) -> Result<(), Self::Err> {
  627. tracing::debug!("Adding quote with: {}", quote.payment_method.to_string());
  628. println!("Adding quote with: {}", quote.payment_method.to_string());
  629. query(
  630. r#"
  631. INSERT INTO mint_quote (
  632. id, amount, unit, request, expiry, request_lookup_id, pubkey, created_time, payment_method, request_lookup_id_kind
  633. )
  634. VALUES (
  635. :id, :amount, :unit, :request, :expiry, :request_lookup_id, :pubkey, :created_time, :payment_method, :request_lookup_id_kind
  636. )
  637. "#,
  638. )?
  639. .bind("id", quote.id.to_string())
  640. .bind("amount", quote.amount.map(|a| a.to_i64()))
  641. .bind("unit", quote.unit.to_string())
  642. .bind("request", quote.request)
  643. .bind("expiry", quote.expiry as i64)
  644. .bind(
  645. "request_lookup_id",
  646. quote.request_lookup_id.to_string(),
  647. )
  648. .bind("pubkey", quote.pubkey.map(|p| p.to_string()))
  649. .bind("created_time", quote.created_time as i64)
  650. .bind("payment_method", quote.payment_method.to_string())
  651. .bind("request_lookup_id_kind", quote.request_lookup_id.kind())
  652. .execute(&self.inner)
  653. .await?;
  654. Ok(())
  655. }
  656. async fn remove_mint_quote(&mut self, quote_id: &Uuid) -> Result<(), Self::Err> {
  657. query(r#"DELETE FROM mint_quote WHERE id=:id"#)?
  658. .bind("id", quote_id.as_hyphenated().to_string())
  659. .execute(&self.inner)
  660. .await?;
  661. Ok(())
  662. }
  663. async fn add_melt_quote(&mut self, quote: mint::MeltQuote) -> Result<(), Self::Err> {
  664. // First try to find and replace any expired UNPAID quotes with the same request_lookup_id
  665. let current_time = unix_time();
  666. let row_affected = query(
  667. r#"
  668. DELETE FROM melt_quote
  669. WHERE request_lookup_id = :request_lookup_id
  670. AND state = :state
  671. AND expiry < :current_time
  672. "#,
  673. )?
  674. .bind("request_lookup_id", quote.request_lookup_id.to_string())
  675. .bind("state", MeltQuoteState::Unpaid.to_string())
  676. .bind("current_time", current_time as i64)
  677. .execute(&self.inner)
  678. .await?;
  679. if row_affected > 0 {
  680. tracing::info!("Received new melt quote for existing invoice with expired quote.");
  681. }
  682. // Now insert the new quote
  683. query(
  684. r#"
  685. INSERT INTO melt_quote
  686. (
  687. id, unit, amount, request, fee_reserve, state,
  688. expiry, payment_preimage, request_lookup_id,
  689. created_time, paid_time, options, request_lookup_id_kind, payment_method
  690. )
  691. VALUES
  692. (
  693. :id, :unit, :amount, :request, :fee_reserve, :state,
  694. :expiry, :payment_preimage, :request_lookup_id,
  695. :created_time, :paid_time, :options, :request_lookup_id_kind, :payment_method
  696. )
  697. "#,
  698. )?
  699. .bind("id", quote.id.to_string())
  700. .bind("unit", quote.unit.to_string())
  701. .bind("amount", quote.amount.to_i64())
  702. .bind("request", serde_json::to_string(&quote.request)?)
  703. .bind("fee_reserve", quote.fee_reserve.to_i64())
  704. .bind("state", quote.state.to_string())
  705. .bind("expiry", quote.expiry as i64)
  706. .bind("payment_preimage", quote.payment_preimage)
  707. .bind("request_lookup_id", quote.request_lookup_id.to_string())
  708. .bind("created_time", quote.created_time as i64)
  709. .bind("paid_time", quote.paid_time.map(|t| t as i64))
  710. .bind(
  711. "options",
  712. quote.options.map(|o| serde_json::to_string(&o).ok()),
  713. )
  714. .bind("request_lookup_id_kind", quote.request_lookup_id.kind())
  715. .bind("payment_method", quote.payment_method.to_string())
  716. .execute(&self.inner)
  717. .await?;
  718. Ok(())
  719. }
  720. async fn update_melt_quote_request_lookup_id(
  721. &mut self,
  722. quote_id: &Uuid,
  723. new_request_lookup_id: &PaymentIdentifier,
  724. ) -> Result<(), Self::Err> {
  725. query(r#"UPDATE melt_quote SET request_lookup_id = :new_req_id, request_lookup_id_kind = :new_kind WHERE id = :id"#)?
  726. .bind("new_req_id", new_request_lookup_id.to_string())
  727. .bind("new_kind",new_request_lookup_id.kind() )
  728. .bind("id", quote_id.as_hyphenated().to_string())
  729. .execute(&self.inner)
  730. .await?;
  731. Ok(())
  732. }
  733. async fn update_melt_quote_state(
  734. &mut self,
  735. quote_id: &Uuid,
  736. state: MeltQuoteState,
  737. payment_proof: Option<String>,
  738. ) -> Result<(MeltQuoteState, mint::MeltQuote), Self::Err> {
  739. let mut quote = query(
  740. r#"
  741. SELECT
  742. id,
  743. unit,
  744. amount,
  745. request,
  746. fee_reserve,
  747. expiry,
  748. state,
  749. payment_preimage,
  750. request_lookup_id,
  751. created_time,
  752. paid_time,
  753. payment_method,
  754. options,
  755. request_lookup_id_kind
  756. FROM
  757. melt_quote
  758. WHERE
  759. id=:id
  760. AND state != :state
  761. "#,
  762. )?
  763. .bind("id", quote_id.as_hyphenated().to_string())
  764. .bind("state", state.to_string())
  765. .fetch_one(&self.inner)
  766. .await?
  767. .map(sql_row_to_melt_quote)
  768. .transpose()?
  769. .ok_or(Error::QuoteNotFound)?;
  770. let rec = if state == MeltQuoteState::Paid {
  771. let current_time = unix_time();
  772. query(r#"UPDATE melt_quote SET state = :state, paid_time = :paid_time, payment_preimage = :payment_preimage WHERE id = :id"#)?
  773. .bind("state", state.to_string())
  774. .bind("paid_time", current_time as i64)
  775. .bind("payment_preimage", payment_proof)
  776. .bind("id", quote_id.as_hyphenated().to_string())
  777. .execute(&self.inner)
  778. .await
  779. } else {
  780. query(r#"UPDATE melt_quote SET state = :state WHERE id = :id"#)?
  781. .bind("state", state.to_string())
  782. .bind("id", quote_id.as_hyphenated().to_string())
  783. .execute(&self.inner)
  784. .await
  785. };
  786. match rec {
  787. Ok(_) => {}
  788. Err(err) => {
  789. tracing::error!("SQLite Could not update melt quote");
  790. return Err(err);
  791. }
  792. };
  793. let old_state = quote.state;
  794. quote.state = state;
  795. Ok((old_state, quote))
  796. }
  797. async fn remove_melt_quote(&mut self, quote_id: &Uuid) -> Result<(), Self::Err> {
  798. query(
  799. r#"
  800. DELETE FROM melt_quote
  801. WHERE id=?
  802. "#,
  803. )?
  804. .bind("id", quote_id.as_hyphenated().to_string())
  805. .execute(&self.inner)
  806. .await?;
  807. Ok(())
  808. }
  809. async fn get_mint_quote(&mut self, quote_id: &Uuid) -> Result<Option<MintQuote>, Self::Err> {
  810. let payments = get_mint_quote_payments(&self.inner, quote_id).await?;
  811. let issuance = get_mint_quote_issuance(&self.inner, quote_id).await?;
  812. Ok(query(
  813. r#"
  814. SELECT
  815. id,
  816. amount,
  817. unit,
  818. request,
  819. expiry,
  820. request_lookup_id,
  821. pubkey,
  822. created_time,
  823. amount_paid,
  824. amount_issued,
  825. payment_method,
  826. request_lookup_id_kind
  827. FROM
  828. mint_quote
  829. WHERE id = :id
  830. FOR UPDATE
  831. "#,
  832. )?
  833. .bind("id", quote_id.as_hyphenated().to_string())
  834. .fetch_one(&self.inner)
  835. .await?
  836. .map(|row| sql_row_to_mint_quote(row, payments, issuance))
  837. .transpose()?)
  838. }
  839. async fn get_melt_quote(
  840. &mut self,
  841. quote_id: &Uuid,
  842. ) -> Result<Option<mint::MeltQuote>, Self::Err> {
  843. Ok(query(
  844. r#"
  845. SELECT
  846. id,
  847. unit,
  848. amount,
  849. request,
  850. fee_reserve,
  851. expiry,
  852. state,
  853. payment_preimage,
  854. request_lookup_id,
  855. created_time,
  856. paid_time,
  857. payment_method,
  858. options,
  859. request_lookup_id
  860. FROM
  861. melt_quote
  862. WHERE
  863. id=:id
  864. "#,
  865. )?
  866. .bind("id", quote_id.as_hyphenated().to_string())
  867. .fetch_one(&self.inner)
  868. .await?
  869. .map(sql_row_to_melt_quote)
  870. .transpose()?)
  871. }
  872. async fn get_mint_quote_by_request(
  873. &mut self,
  874. request: &str,
  875. ) -> Result<Option<MintQuote>, Self::Err> {
  876. let mut mint_quote = query(
  877. r#"
  878. SELECT
  879. id,
  880. amount,
  881. unit,
  882. request,
  883. expiry,
  884. request_lookup_id,
  885. pubkey,
  886. created_time,
  887. amount_paid,
  888. amount_issued,
  889. payment_method,
  890. request_lookup_id_kind
  891. FROM
  892. mint_quote
  893. WHERE request = :request
  894. FOR UPDATE
  895. "#,
  896. )?
  897. .bind("request", request.to_string())
  898. .fetch_one(&self.inner)
  899. .await?
  900. .map(|row| sql_row_to_mint_quote(row, vec![], vec![]))
  901. .transpose()?;
  902. if let Some(quote) = mint_quote.as_mut() {
  903. let payments = get_mint_quote_payments(&self.inner, &quote.id).await?;
  904. let issuance = get_mint_quote_issuance(&self.inner, &quote.id).await?;
  905. quote.issuance = issuance;
  906. quote.payments = payments;
  907. }
  908. Ok(mint_quote)
  909. }
  910. async fn get_mint_quote_by_request_lookup_id(
  911. &mut self,
  912. request_lookup_id: &PaymentIdentifier,
  913. ) -> Result<Option<MintQuote>, Self::Err> {
  914. let mut mint_quote = query(
  915. r#"
  916. SELECT
  917. id,
  918. amount,
  919. unit,
  920. request,
  921. expiry,
  922. request_lookup_id,
  923. pubkey,
  924. created_time,
  925. amount_paid,
  926. amount_issued,
  927. payment_method,
  928. request_lookup_id_kind
  929. FROM
  930. mint_quote
  931. WHERE request_lookup_id = :request_lookup_id
  932. AND request_lookup_id_kind = :request_lookup_id_kind
  933. FOR UPDATE
  934. "#,
  935. )?
  936. .bind("request_lookup_id", request_lookup_id.to_string())
  937. .bind("request_lookup_id_kind", request_lookup_id.kind())
  938. .fetch_one(&self.inner)
  939. .await?
  940. .map(|row| sql_row_to_mint_quote(row, vec![], vec![]))
  941. .transpose()?;
  942. if let Some(quote) = mint_quote.as_mut() {
  943. let payments = get_mint_quote_payments(&self.inner, &quote.id).await?;
  944. let issuance = get_mint_quote_issuance(&self.inner, &quote.id).await?;
  945. quote.issuance = issuance;
  946. quote.payments = payments;
  947. }
  948. Ok(mint_quote)
  949. }
  950. }
  951. #[async_trait]
  952. impl<RM> MintQuotesDatabase for SQLMintDatabase<RM>
  953. where
  954. RM: DatabasePool + 'static,
  955. {
  956. type Err = Error;
  957. async fn get_mint_quote(&self, quote_id: &Uuid) -> Result<Option<MintQuote>, Self::Err> {
  958. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  959. let payments = get_mint_quote_payments(&*conn, quote_id).await?;
  960. let issuance = get_mint_quote_issuance(&*conn, quote_id).await?;
  961. Ok(query(
  962. r#"
  963. SELECT
  964. id,
  965. amount,
  966. unit,
  967. request,
  968. expiry,
  969. request_lookup_id,
  970. pubkey,
  971. created_time,
  972. amount_paid,
  973. amount_issued,
  974. payment_method,
  975. request_lookup_id_kind
  976. FROM
  977. mint_quote
  978. WHERE id = :id"#,
  979. )?
  980. .bind("id", quote_id.as_hyphenated().to_string())
  981. .fetch_one(&*conn)
  982. .await?
  983. .map(|row| sql_row_to_mint_quote(row, payments, issuance))
  984. .transpose()?)
  985. }
  986. async fn get_mint_quote_by_request(
  987. &self,
  988. request: &str,
  989. ) -> Result<Option<MintQuote>, Self::Err> {
  990. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  991. let mut mint_quote = query(
  992. r#"
  993. SELECT
  994. id,
  995. amount,
  996. unit,
  997. request,
  998. expiry,
  999. request_lookup_id,
  1000. pubkey,
  1001. created_time,
  1002. amount_paid,
  1003. amount_issued,
  1004. payment_method,
  1005. request_lookup_id_kind
  1006. FROM
  1007. mint_quote
  1008. WHERE request = :request"#,
  1009. )?
  1010. .bind("request", request.to_owned())
  1011. .fetch_one(&*conn)
  1012. .await?
  1013. .map(|row| sql_row_to_mint_quote(row, vec![], vec![]))
  1014. .transpose()?;
  1015. if let Some(quote) = mint_quote.as_mut() {
  1016. let payments = get_mint_quote_payments(&*conn, &quote.id).await?;
  1017. let issuance = get_mint_quote_issuance(&*conn, &quote.id).await?;
  1018. quote.issuance = issuance;
  1019. quote.payments = payments;
  1020. }
  1021. Ok(mint_quote)
  1022. }
  1023. async fn get_mint_quote_by_request_lookup_id(
  1024. &self,
  1025. request_lookup_id: &PaymentIdentifier,
  1026. ) -> Result<Option<MintQuote>, Self::Err> {
  1027. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1028. let mut mint_quote = query(
  1029. r#"
  1030. SELECT
  1031. id,
  1032. amount,
  1033. unit,
  1034. request,
  1035. expiry,
  1036. request_lookup_id,
  1037. pubkey,
  1038. created_time,
  1039. amount_paid,
  1040. amount_issued,
  1041. payment_method,
  1042. request_lookup_id_kind
  1043. FROM
  1044. mint_quote
  1045. WHERE request_lookup_id = :request_lookup_id
  1046. AND request_lookup_id_kind = :request_lookup_id_kind
  1047. "#,
  1048. )?
  1049. .bind("request_lookup_id", request_lookup_id.to_string())
  1050. .bind("request_lookup_id_kind", request_lookup_id.kind())
  1051. .fetch_one(&*conn)
  1052. .await?
  1053. .map(|row| sql_row_to_mint_quote(row, vec![], vec![]))
  1054. .transpose()?;
  1055. // TODO: these should use an sql join so they can be done in one query
  1056. if let Some(quote) = mint_quote.as_mut() {
  1057. let payments = get_mint_quote_payments(&*conn, &quote.id).await?;
  1058. let issuance = get_mint_quote_issuance(&*conn, &quote.id).await?;
  1059. quote.issuance = issuance;
  1060. quote.payments = payments;
  1061. }
  1062. Ok(mint_quote)
  1063. }
  1064. async fn get_mint_quotes(&self) -> Result<Vec<MintQuote>, Self::Err> {
  1065. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1066. let mut mint_quotes = query(
  1067. r#"
  1068. SELECT
  1069. id,
  1070. amount,
  1071. unit,
  1072. request,
  1073. expiry,
  1074. request_lookup_id,
  1075. pubkey,
  1076. created_time,
  1077. amount_paid,
  1078. amount_issued,
  1079. payment_method,
  1080. request_lookup_id_kind
  1081. FROM
  1082. mint_quote
  1083. "#,
  1084. )?
  1085. .fetch_all(&*conn)
  1086. .await?
  1087. .into_iter()
  1088. .map(|row| sql_row_to_mint_quote(row, vec![], vec![]))
  1089. .collect::<Result<Vec<_>, _>>()?;
  1090. for quote in mint_quotes.as_mut_slice() {
  1091. let payments = get_mint_quote_payments(&*conn, &quote.id).await?;
  1092. let issuance = get_mint_quote_issuance(&*conn, &quote.id).await?;
  1093. quote.issuance = issuance;
  1094. quote.payments = payments;
  1095. }
  1096. Ok(mint_quotes)
  1097. }
  1098. async fn get_melt_quote(&self, quote_id: &Uuid) -> Result<Option<mint::MeltQuote>, Self::Err> {
  1099. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1100. Ok(query(
  1101. r#"
  1102. SELECT
  1103. id,
  1104. unit,
  1105. amount,
  1106. request,
  1107. fee_reserve,
  1108. expiry,
  1109. state,
  1110. payment_preimage,
  1111. request_lookup_id,
  1112. created_time,
  1113. paid_time,
  1114. payment_method,
  1115. options,
  1116. request_lookup_id_kind
  1117. FROM
  1118. melt_quote
  1119. WHERE
  1120. id=:id
  1121. "#,
  1122. )?
  1123. .bind("id", quote_id.as_hyphenated().to_string())
  1124. .fetch_one(&*conn)
  1125. .await?
  1126. .map(sql_row_to_melt_quote)
  1127. .transpose()?)
  1128. }
  1129. async fn get_melt_quotes(&self) -> Result<Vec<mint::MeltQuote>, Self::Err> {
  1130. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1131. Ok(query(
  1132. r#"
  1133. SELECT
  1134. id,
  1135. unit,
  1136. amount,
  1137. request,
  1138. fee_reserve,
  1139. expiry,
  1140. state,
  1141. payment_preimage,
  1142. request_lookup_id,
  1143. created_time,
  1144. paid_time,
  1145. payment_method,
  1146. options,
  1147. request_lookup_id_kind
  1148. FROM
  1149. melt_quote
  1150. "#,
  1151. )?
  1152. .fetch_all(&*conn)
  1153. .await?
  1154. .into_iter()
  1155. .map(sql_row_to_melt_quote)
  1156. .collect::<Result<Vec<_>, _>>()?)
  1157. }
  1158. }
  1159. #[async_trait]
  1160. impl<RM> MintProofsDatabase for SQLMintDatabase<RM>
  1161. where
  1162. RM: DatabasePool + 'static,
  1163. {
  1164. type Err = Error;
  1165. async fn get_proofs_by_ys(&self, ys: &[PublicKey]) -> Result<Vec<Option<Proof>>, Self::Err> {
  1166. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1167. let mut proofs = query(
  1168. r#"
  1169. SELECT
  1170. amount,
  1171. keyset_id,
  1172. secret,
  1173. c,
  1174. witness,
  1175. y
  1176. FROM
  1177. proof
  1178. WHERE
  1179. y IN (:ys)
  1180. "#,
  1181. )?
  1182. .bind_vec("ys", ys.iter().map(|y| y.to_bytes().to_vec()).collect())
  1183. .fetch_all(&*conn)
  1184. .await?
  1185. .into_iter()
  1186. .map(|mut row| {
  1187. Ok((
  1188. column_as_string!(
  1189. row.pop().ok_or(Error::InvalidDbResponse)?,
  1190. PublicKey::from_hex,
  1191. PublicKey::from_slice
  1192. ),
  1193. sql_row_to_proof(row)?,
  1194. ))
  1195. })
  1196. .collect::<Result<HashMap<_, _>, Error>>()?;
  1197. Ok(ys.iter().map(|y| proofs.remove(y)).collect())
  1198. }
  1199. async fn get_proof_ys_by_quote_id(&self, quote_id: &Uuid) -> Result<Vec<PublicKey>, Self::Err> {
  1200. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1201. Ok(query(
  1202. r#"
  1203. SELECT
  1204. amount,
  1205. keyset_id,
  1206. secret,
  1207. c,
  1208. witness
  1209. FROM
  1210. proof
  1211. WHERE
  1212. quote_id = :quote_id
  1213. "#,
  1214. )?
  1215. .bind("quote_id", quote_id.as_hyphenated().to_string())
  1216. .fetch_all(&*conn)
  1217. .await?
  1218. .into_iter()
  1219. .map(sql_row_to_proof)
  1220. .collect::<Result<Vec<Proof>, _>>()?
  1221. .ys()?)
  1222. }
  1223. async fn get_proofs_states(&self, ys: &[PublicKey]) -> Result<Vec<Option<State>>, Self::Err> {
  1224. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1225. let mut current_states = get_current_states(&*conn, ys).await?;
  1226. Ok(ys.iter().map(|y| current_states.remove(y)).collect())
  1227. }
  1228. async fn get_proofs_by_keyset_id(
  1229. &self,
  1230. keyset_id: &Id,
  1231. ) -> Result<(Proofs, Vec<Option<State>>), Self::Err> {
  1232. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1233. Ok(query(
  1234. r#"
  1235. SELECT
  1236. keyset_id,
  1237. amount,
  1238. secret,
  1239. c,
  1240. witness,
  1241. state
  1242. FROM
  1243. proof
  1244. WHERE
  1245. keyset_id=?
  1246. "#,
  1247. )?
  1248. .bind("keyset_id", keyset_id.to_string())
  1249. .fetch_all(&*conn)
  1250. .await?
  1251. .into_iter()
  1252. .map(sql_row_to_proof_with_state)
  1253. .collect::<Result<Vec<_>, _>>()?
  1254. .into_iter()
  1255. .unzip())
  1256. }
  1257. }
  1258. #[async_trait]
  1259. impl<RM> MintSignatureTransaction<'_> for SQLTransaction<RM>
  1260. where
  1261. RM: DatabasePool + 'static,
  1262. {
  1263. type Err = Error;
  1264. async fn add_blind_signatures(
  1265. &mut self,
  1266. blinded_messages: &[PublicKey],
  1267. blind_signatures: &[BlindSignature],
  1268. quote_id: Option<Uuid>,
  1269. ) -> Result<(), Self::Err> {
  1270. let current_time = unix_time();
  1271. for (message, signature) in blinded_messages.iter().zip(blind_signatures) {
  1272. query(
  1273. r#"
  1274. INSERT INTO blind_signature
  1275. (blinded_message, amount, keyset_id, c, quote_id, dleq_e, dleq_s, created_time)
  1276. VALUES
  1277. (:blinded_message, :amount, :keyset_id, :c, :quote_id, :dleq_e, :dleq_s, :created_time)
  1278. "#,
  1279. )?
  1280. .bind("blinded_message", message.to_bytes().to_vec())
  1281. .bind("amount", u64::from(signature.amount) as i64)
  1282. .bind("keyset_id", signature.keyset_id.to_string())
  1283. .bind("c", signature.c.to_bytes().to_vec())
  1284. .bind("quote_id", quote_id.map(|q| q.hyphenated().to_string()))
  1285. .bind(
  1286. "dleq_e",
  1287. signature.dleq.as_ref().map(|dleq| dleq.e.to_secret_hex()),
  1288. )
  1289. .bind(
  1290. "dleq_s",
  1291. signature.dleq.as_ref().map(|dleq| dleq.s.to_secret_hex()),
  1292. )
  1293. .bind("created_time", current_time as i64)
  1294. .execute(&self.inner)
  1295. .await?;
  1296. }
  1297. Ok(())
  1298. }
  1299. async fn get_blind_signatures(
  1300. &mut self,
  1301. blinded_messages: &[PublicKey],
  1302. ) -> Result<Vec<Option<BlindSignature>>, Self::Err> {
  1303. let mut blinded_signatures = query(
  1304. r#"SELECT
  1305. keyset_id,
  1306. amount,
  1307. c,
  1308. dleq_e,
  1309. dleq_s,
  1310. blinded_message
  1311. FROM
  1312. blind_signature
  1313. WHERE blinded_message IN (:y)
  1314. "#,
  1315. )?
  1316. .bind_vec(
  1317. "y",
  1318. blinded_messages
  1319. .iter()
  1320. .map(|y| y.to_bytes().to_vec())
  1321. .collect(),
  1322. )
  1323. .fetch_all(&self.inner)
  1324. .await?
  1325. .into_iter()
  1326. .map(|mut row| {
  1327. Ok((
  1328. column_as_string!(
  1329. &row.pop().ok_or(Error::InvalidDbResponse)?,
  1330. PublicKey::from_hex,
  1331. PublicKey::from_slice
  1332. ),
  1333. sql_row_to_blind_signature(row)?,
  1334. ))
  1335. })
  1336. .collect::<Result<HashMap<_, _>, Error>>()?;
  1337. Ok(blinded_messages
  1338. .iter()
  1339. .map(|y| blinded_signatures.remove(y))
  1340. .collect())
  1341. }
  1342. }
  1343. #[async_trait]
  1344. impl<RM> MintSignaturesDatabase for SQLMintDatabase<RM>
  1345. where
  1346. RM: DatabasePool + 'static,
  1347. {
  1348. type Err = Error;
  1349. async fn get_blind_signatures(
  1350. &self,
  1351. blinded_messages: &[PublicKey],
  1352. ) -> Result<Vec<Option<BlindSignature>>, Self::Err> {
  1353. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1354. let mut blinded_signatures = query(
  1355. r#"SELECT
  1356. keyset_id,
  1357. amount,
  1358. c,
  1359. dleq_e,
  1360. dleq_s,
  1361. blinded_message
  1362. FROM
  1363. blind_signature
  1364. WHERE blinded_message IN (:blinded_message)
  1365. "#,
  1366. )?
  1367. .bind_vec(
  1368. "blinded_message",
  1369. blinded_messages
  1370. .iter()
  1371. .map(|b_| b_.to_bytes().to_vec())
  1372. .collect(),
  1373. )
  1374. .fetch_all(&*conn)
  1375. .await?
  1376. .into_iter()
  1377. .map(|mut row| {
  1378. Ok((
  1379. column_as_string!(
  1380. &row.pop().ok_or(Error::InvalidDbResponse)?,
  1381. PublicKey::from_hex,
  1382. PublicKey::from_slice
  1383. ),
  1384. sql_row_to_blind_signature(row)?,
  1385. ))
  1386. })
  1387. .collect::<Result<HashMap<_, _>, Error>>()?;
  1388. Ok(blinded_messages
  1389. .iter()
  1390. .map(|y| blinded_signatures.remove(y))
  1391. .collect())
  1392. }
  1393. async fn get_blind_signatures_for_keyset(
  1394. &self,
  1395. keyset_id: &Id,
  1396. ) -> Result<Vec<BlindSignature>, Self::Err> {
  1397. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1398. Ok(query(
  1399. r#"
  1400. SELECT
  1401. keyset_id,
  1402. amount,
  1403. c,
  1404. dleq_e,
  1405. dleq_s
  1406. FROM
  1407. blind_signature
  1408. WHERE
  1409. keyset_id=:keyset_id
  1410. "#,
  1411. )?
  1412. .bind("keyset_id", keyset_id.to_string())
  1413. .fetch_all(&*conn)
  1414. .await?
  1415. .into_iter()
  1416. .map(sql_row_to_blind_signature)
  1417. .collect::<Result<Vec<BlindSignature>, _>>()?)
  1418. }
  1419. /// Get [`BlindSignature`]s for quote
  1420. async fn get_blind_signatures_for_quote(
  1421. &self,
  1422. quote_id: &Uuid,
  1423. ) -> Result<Vec<BlindSignature>, Self::Err> {
  1424. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1425. Ok(query(
  1426. r#"
  1427. SELECT
  1428. keyset_id,
  1429. amount,
  1430. c,
  1431. dleq_e,
  1432. dleq_s
  1433. FROM
  1434. blind_signature
  1435. WHERE
  1436. quote_id=:quote_id
  1437. "#,
  1438. )?
  1439. .bind("quote_id", quote_id.to_string())
  1440. .fetch_all(&*conn)
  1441. .await?
  1442. .into_iter()
  1443. .map(sql_row_to_blind_signature)
  1444. .collect::<Result<Vec<BlindSignature>, _>>()?)
  1445. }
  1446. }
  1447. #[async_trait]
  1448. impl<RM> MintDatabase<Error> for SQLMintDatabase<RM>
  1449. where
  1450. RM: DatabasePool + 'static,
  1451. {
  1452. async fn begin_transaction<'a>(
  1453. &'a self,
  1454. ) -> Result<Box<dyn database::MintTransaction<'a, Error> + Send + Sync + 'a>, Error> {
  1455. Ok(Box::new(SQLTransaction {
  1456. inner: ConnectionWithTransaction::new(
  1457. self.pool.get().map_err(|e| Error::Database(Box::new(e)))?,
  1458. )
  1459. .await?,
  1460. }))
  1461. }
  1462. async fn get_mint_info(&self) -> Result<MintInfo, Error> {
  1463. Ok(self.fetch_from_config("mint_info").await?)
  1464. }
  1465. async fn get_quote_ttl(&self) -> Result<QuoteTTL, Error> {
  1466. Ok(self.fetch_from_config("quote_ttl").await?)
  1467. }
  1468. }
  1469. fn sql_row_to_keyset_info(row: Vec<Column>) -> Result<MintKeySetInfo, Error> {
  1470. unpack_into!(
  1471. let (
  1472. id,
  1473. unit,
  1474. active,
  1475. valid_from,
  1476. valid_to,
  1477. derivation_path,
  1478. derivation_path_index,
  1479. max_order,
  1480. row_keyset_ppk
  1481. ) = row
  1482. );
  1483. Ok(MintKeySetInfo {
  1484. id: column_as_string!(id, Id::from_str, Id::from_bytes),
  1485. unit: column_as_string!(unit, CurrencyUnit::from_str),
  1486. active: matches!(active, Column::Integer(1)),
  1487. valid_from: column_as_number!(valid_from),
  1488. derivation_path: column_as_string!(derivation_path, DerivationPath::from_str),
  1489. derivation_path_index: column_as_nullable_number!(derivation_path_index),
  1490. max_order: column_as_number!(max_order),
  1491. input_fee_ppk: column_as_number!(row_keyset_ppk),
  1492. final_expiry: column_as_nullable_number!(valid_to),
  1493. })
  1494. }
  1495. #[instrument(skip_all)]
  1496. fn sql_row_to_mint_quote(
  1497. row: Vec<Column>,
  1498. payments: Vec<IncomingPayment>,
  1499. issueances: Vec<Issuance>,
  1500. ) -> Result<MintQuote, Error> {
  1501. unpack_into!(
  1502. let (
  1503. id, amount, unit, request, expiry, request_lookup_id,
  1504. pubkey, created_time, amount_paid, amount_issued, payment_method, request_lookup_id_kind
  1505. ) = row
  1506. );
  1507. let request_str = column_as_string!(&request);
  1508. let request_lookup_id = column_as_nullable_string!(&request_lookup_id).unwrap_or_else(|| {
  1509. Bolt11Invoice::from_str(&request_str)
  1510. .map(|invoice| invoice.payment_hash().to_string())
  1511. .unwrap_or_else(|_| request_str.clone())
  1512. });
  1513. let request_lookup_id_kind = column_as_string!(request_lookup_id_kind);
  1514. let pubkey = column_as_nullable_string!(&pubkey)
  1515. .map(|pk| PublicKey::from_hex(&pk))
  1516. .transpose()?;
  1517. let id = column_as_string!(id);
  1518. let amount: Option<u64> = column_as_nullable_number!(amount);
  1519. let amount_paid: u64 = column_as_number!(amount_paid);
  1520. let amount_issued: u64 = column_as_number!(amount_issued);
  1521. let payment_method = column_as_string!(payment_method, PaymentMethod::from_str);
  1522. Ok(MintQuote::new(
  1523. Some(Uuid::parse_str(&id).map_err(|_| Error::InvalidUuid(id))?),
  1524. request_str,
  1525. column_as_string!(unit, CurrencyUnit::from_str),
  1526. amount.map(Amount::from),
  1527. column_as_number!(expiry),
  1528. PaymentIdentifier::new(&request_lookup_id_kind, &request_lookup_id)
  1529. .map_err(|_| ConversionError::MissingParameter("Payment id".to_string()))?,
  1530. pubkey,
  1531. amount_paid.into(),
  1532. amount_issued.into(),
  1533. payment_method,
  1534. column_as_number!(created_time),
  1535. payments,
  1536. issueances,
  1537. ))
  1538. }
  1539. fn sql_row_to_melt_quote(row: Vec<Column>) -> Result<mint::MeltQuote, Error> {
  1540. unpack_into!(
  1541. let (
  1542. id,
  1543. unit,
  1544. amount,
  1545. request,
  1546. fee_reserve,
  1547. expiry,
  1548. state,
  1549. payment_preimage,
  1550. request_lookup_id,
  1551. created_time,
  1552. paid_time,
  1553. payment_method,
  1554. options,
  1555. request_lookup_id_kind
  1556. ) = row
  1557. );
  1558. let id = column_as_string!(id);
  1559. let amount: u64 = column_as_number!(amount);
  1560. let fee_reserve: u64 = column_as_number!(fee_reserve);
  1561. let expiry = column_as_number!(expiry);
  1562. let payment_preimage = column_as_nullable_string!(payment_preimage);
  1563. let options = column_as_nullable_string!(options);
  1564. let options = options.and_then(|o| serde_json::from_str(&o).ok());
  1565. let created_time: i64 = column_as_number!(created_time);
  1566. let paid_time = column_as_nullable_number!(paid_time);
  1567. let payment_method = PaymentMethod::from_str(&column_as_string!(payment_method))?;
  1568. let state =
  1569. MeltQuoteState::from_str(&column_as_string!(&state)).map_err(ConversionError::from)?;
  1570. let unit = column_as_string!(unit);
  1571. let request = column_as_string!(request);
  1572. let mut request_lookup_id_kind = column_as_string!(request_lookup_id_kind);
  1573. let request_lookup_id = column_as_nullable_string!(&request_lookup_id).unwrap_or_else(|| {
  1574. Bolt11Invoice::from_str(&request)
  1575. .map(|invoice| invoice.payment_hash().to_string())
  1576. .unwrap_or_else(|_| {
  1577. request_lookup_id_kind = "custom".to_string();
  1578. request.clone()
  1579. })
  1580. });
  1581. let request_lookup_id = PaymentIdentifier::new(&request_lookup_id_kind, &request_lookup_id)
  1582. .map_err(|_| ConversionError::MissingParameter("Payment id".to_string()))?;
  1583. let request = match serde_json::from_str(&request) {
  1584. Ok(req) => req,
  1585. Err(err) => {
  1586. tracing::debug!(
  1587. "Melt quote from pre migrations defaulting to bolt11 {}.",
  1588. err
  1589. );
  1590. let bolt11 = Bolt11Invoice::from_str(&request).unwrap();
  1591. MeltPaymentRequest::Bolt11 { bolt11 }
  1592. }
  1593. };
  1594. Ok(MeltQuote {
  1595. id: Uuid::parse_str(&id).map_err(|_| Error::InvalidUuid(id))?,
  1596. unit: CurrencyUnit::from_str(&unit)?,
  1597. amount: Amount::from(amount),
  1598. request,
  1599. fee_reserve: Amount::from(fee_reserve),
  1600. state,
  1601. expiry,
  1602. payment_preimage,
  1603. request_lookup_id,
  1604. options,
  1605. created_time: created_time as u64,
  1606. paid_time,
  1607. payment_method,
  1608. })
  1609. }
  1610. fn sql_row_to_proof(row: Vec<Column>) -> Result<Proof, Error> {
  1611. unpack_into!(
  1612. let (
  1613. amount,
  1614. keyset_id,
  1615. secret,
  1616. c,
  1617. witness
  1618. ) = row
  1619. );
  1620. let amount: u64 = column_as_number!(amount);
  1621. Ok(Proof {
  1622. amount: Amount::from(amount),
  1623. keyset_id: column_as_string!(keyset_id, Id::from_str),
  1624. secret: column_as_string!(secret, Secret::from_str),
  1625. c: column_as_string!(c, PublicKey::from_hex, PublicKey::from_slice),
  1626. witness: column_as_nullable_string!(witness).and_then(|w| serde_json::from_str(&w).ok()),
  1627. dleq: None,
  1628. })
  1629. }
  1630. fn sql_row_to_proof_with_state(row: Vec<Column>) -> Result<(Proof, Option<State>), Error> {
  1631. unpack_into!(
  1632. let (
  1633. keyset_id, amount, secret, c, witness, state
  1634. ) = row
  1635. );
  1636. let amount: u64 = column_as_number!(amount);
  1637. let state = column_as_nullable_string!(state).and_then(|s| State::from_str(&s).ok());
  1638. Ok((
  1639. Proof {
  1640. amount: Amount::from(amount),
  1641. keyset_id: column_as_string!(keyset_id, Id::from_str, Id::from_bytes),
  1642. secret: column_as_string!(secret, Secret::from_str),
  1643. c: column_as_string!(c, PublicKey::from_hex, PublicKey::from_slice),
  1644. witness: column_as_nullable_string!(witness)
  1645. .and_then(|w| serde_json::from_str(&w).ok()),
  1646. dleq: None,
  1647. },
  1648. state,
  1649. ))
  1650. }
  1651. fn sql_row_to_blind_signature(row: Vec<Column>) -> Result<BlindSignature, Error> {
  1652. unpack_into!(
  1653. let (
  1654. keyset_id, amount, c, dleq_e, dleq_s
  1655. ) = row
  1656. );
  1657. let dleq = match (
  1658. column_as_nullable_string!(dleq_e),
  1659. column_as_nullable_string!(dleq_s),
  1660. ) {
  1661. (Some(e), Some(s)) => Some(BlindSignatureDleq {
  1662. e: SecretKey::from_hex(e)?,
  1663. s: SecretKey::from_hex(s)?,
  1664. }),
  1665. _ => None,
  1666. };
  1667. let amount: u64 = column_as_number!(amount);
  1668. Ok(BlindSignature {
  1669. amount: Amount::from(amount),
  1670. keyset_id: column_as_string!(keyset_id, Id::from_str, Id::from_bytes),
  1671. c: column_as_string!(c, PublicKey::from_hex, PublicKey::from_slice),
  1672. dleq,
  1673. })
  1674. }