mod.rs 49 KB

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