mod.rs 48 KB

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