mod.rs 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523
  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, Self::Err> {
  630. let transaction = self.pool.begin().await?;
  631. let 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. "#,
  651. )
  652. .bind(":id", quote_id.as_hyphenated().to_string())
  653. .fetch_one(&transaction)
  654. .await?
  655. .map(sqlite_row_to_melt_quote)
  656. .transpose()?
  657. .ok_or(Error::QuoteNotFound)?;
  658. let rec = if state == MeltQuoteState::Paid {
  659. let current_time = unix_time();
  660. query(r#"UPDATE melt_quote SET state = :state, paid_time = :paid_time WHERE id = :id"#)
  661. .bind(":state", state.to_string())
  662. .bind(":paid_time", current_time as i64)
  663. .bind(":id", quote_id.as_hyphenated().to_string())
  664. .execute(&transaction)
  665. .await
  666. } else {
  667. query(r#"UPDATE melt_quote SET state = :state WHERE id = :id"#)
  668. .bind(":state", state.to_string())
  669. .bind(":id", quote_id.as_hyphenated().to_string())
  670. .execute(&transaction)
  671. .await
  672. };
  673. match rec {
  674. Ok(_) => {
  675. transaction.commit().await?;
  676. }
  677. Err(err) => {
  678. tracing::error!("SQLite Could not update melt quote");
  679. transaction.rollback().await?;
  680. return Err(err.into());
  681. }
  682. };
  683. Ok(quote.state)
  684. }
  685. async fn remove_melt_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err> {
  686. query(
  687. r#"
  688. DELETE FROM melt_quote
  689. WHERE id=?
  690. "#,
  691. )
  692. .bind(":id", quote_id.as_hyphenated().to_string())
  693. .execute(&self.pool)
  694. .await?;
  695. Ok(())
  696. }
  697. async fn add_melt_request(
  698. &self,
  699. melt_request: MeltRequest<Uuid>,
  700. ln_key: PaymentProcessorKey,
  701. ) -> Result<(), Self::Err> {
  702. query(
  703. r#"
  704. INSERT INTO melt_request
  705. (id, inputs, outputs, method, unit)
  706. VALUES
  707. (:id, :inputs, :outputs, :method, :unit)
  708. ON CONFLICT(id) DO UPDATE SET
  709. inputs = excluded.inputs,
  710. outputs = excluded.outputs,
  711. method = excluded.method,
  712. unit = excluded.unit
  713. "#,
  714. )
  715. .bind(":id", melt_request.quote().to_string())
  716. .bind(":inputs", serde_json::to_string(&melt_request.inputs())?)
  717. .bind(":outputs", serde_json::to_string(&melt_request.outputs())?)
  718. .bind(":method", ln_key.method.to_string())
  719. .bind(":unit", ln_key.unit.to_string())
  720. .execute(&self.pool)
  721. .await?;
  722. Ok(())
  723. }
  724. async fn get_melt_request(
  725. &self,
  726. quote_id: &Uuid,
  727. ) -> Result<Option<(MeltRequest<Uuid>, PaymentProcessorKey)>, Self::Err> {
  728. Ok(query(
  729. r#"
  730. SELECT
  731. id,
  732. inputs,
  733. outputs,
  734. method,
  735. unit
  736. FROM
  737. melt_request
  738. WHERE
  739. id=?;
  740. "#,
  741. )
  742. .bind(":id", quote_id.hyphenated().to_string())
  743. .fetch_one(&self.pool)
  744. .await?
  745. .map(sqlite_row_to_melt_request)
  746. .transpose()?)
  747. }
  748. }
  749. #[async_trait]
  750. impl MintProofsDatabase for MintSqliteDatabase {
  751. type Err = database::Error;
  752. async fn add_proofs(&self, proofs: Proofs, quote_id: Option<Uuid>) -> Result<(), Self::Err> {
  753. let transaction = self.pool.begin().await?;
  754. let current_time = unix_time();
  755. for proof in proofs {
  756. query(
  757. r#"
  758. INSERT OR IGNORE INTO proof
  759. (y, amount, keyset_id, secret, c, witness, state, quote_id, created_time)
  760. VALUES
  761. (:y, :amount, :keyset_id, :secret, :c, :witness, :state, :quote_id, :created_time)
  762. "#,
  763. )
  764. .bind(":y", proof.y()?.to_bytes().to_vec())
  765. .bind(":amount", u64::from(proof.amount) as i64)
  766. .bind(":keyset_id", proof.keyset_id.to_string())
  767. .bind(":secret", proof.secret.to_string())
  768. .bind(":c", proof.c.to_bytes().to_vec())
  769. .bind(
  770. ":witness",
  771. proof.witness.map(|w| serde_json::to_string(&w).unwrap()),
  772. )
  773. .bind(":state", "UNSPENT".to_string())
  774. .bind(":quote_id", quote_id.map(|q| q.hyphenated().to_string()))
  775. .bind(":created_time", current_time as i64)
  776. .execute(&transaction)
  777. .await?;
  778. }
  779. transaction.commit().await?;
  780. Ok(())
  781. }
  782. async fn remove_proofs(
  783. &self,
  784. ys: &[PublicKey],
  785. _quote_id: Option<Uuid>,
  786. ) -> Result<(), Self::Err> {
  787. let transaction = self.pool.begin().await?;
  788. let total_deleted = query(
  789. r#"
  790. DELETE FROM proof WHERE y IN (:ys) AND state != 'SPENT'
  791. "#,
  792. )
  793. .bind_vec(":ys", ys.iter().map(|y| y.to_bytes().to_vec()).collect())
  794. .execute(&transaction)
  795. .await?;
  796. if total_deleted != ys.len() {
  797. transaction.rollback().await?;
  798. return Err(Self::Err::AttemptRemoveSpentProof);
  799. }
  800. transaction.commit().await?;
  801. Ok(())
  802. }
  803. async fn get_proofs_by_ys(&self, ys: &[PublicKey]) -> Result<Vec<Option<Proof>>, Self::Err> {
  804. let mut proofs = query(
  805. r#"
  806. SELECT
  807. amount,
  808. keyset_id,
  809. secret,
  810. c,
  811. witness,
  812. y
  813. FROM
  814. proof
  815. WHERE
  816. y IN (:ys)
  817. "#,
  818. )
  819. .bind_vec(":ys", ys.iter().map(|y| y.to_bytes().to_vec()).collect())
  820. .fetch_all(&self.pool)
  821. .await?
  822. .into_iter()
  823. .map(|mut row| {
  824. Ok((
  825. column_as_string!(
  826. row.pop().ok_or(Error::InvalidDbPath)?,
  827. PublicKey::from_hex,
  828. PublicKey::from_slice
  829. ),
  830. sqlite_row_to_proof(row)?,
  831. ))
  832. })
  833. .collect::<Result<HashMap<_, _>, Error>>()?;
  834. Ok(ys.iter().map(|y| proofs.remove(y)).collect())
  835. }
  836. async fn get_proof_ys_by_quote_id(&self, quote_id: &Uuid) -> Result<Vec<PublicKey>, Self::Err> {
  837. Ok(query(
  838. r#"
  839. SELECT
  840. amount,
  841. keyset_id,
  842. secret,
  843. c,
  844. witness
  845. FROM
  846. proof
  847. WHERE
  848. quote_id = :quote_id
  849. "#,
  850. )
  851. .bind(":quote_id", quote_id.as_hyphenated().to_string())
  852. .fetch_all(&self.pool)
  853. .await?
  854. .into_iter()
  855. .map(sqlite_row_to_proof)
  856. .collect::<Result<Vec<Proof>, _>>()?
  857. .ys()?)
  858. }
  859. async fn get_proofs_states(&self, ys: &[PublicKey]) -> Result<Vec<Option<State>>, Self::Err> {
  860. let mut current_states = self.get_current_states(&self.pool, ys).await?;
  861. Ok(ys.iter().map(|y| current_states.remove(y)).collect())
  862. }
  863. async fn get_proofs_by_keyset_id(
  864. &self,
  865. keyset_id: &Id,
  866. ) -> Result<(Proofs, Vec<Option<State>>), Self::Err> {
  867. Ok(query(
  868. r#"
  869. SELECT
  870. keyset_id,
  871. amount,
  872. secret,
  873. c,
  874. witness
  875. state
  876. FROM
  877. proof
  878. WHERE
  879. keyset_id=?
  880. "#,
  881. )
  882. .bind(":keyset_id", keyset_id.to_string())
  883. .fetch_all(&self.pool)
  884. .await?
  885. .into_iter()
  886. .map(sqlite_row_to_proof_with_state)
  887. .collect::<Result<Vec<_>, _>>()?
  888. .into_iter()
  889. .unzip())
  890. }
  891. async fn update_proofs_states(
  892. &self,
  893. ys: &[PublicKey],
  894. new_state: State,
  895. ) -> Result<Vec<Option<State>>, Self::Err> {
  896. let transaction = self.pool.begin().await?;
  897. let mut current_states = self.get_current_states(&transaction, ys).await?;
  898. if current_states.len() != ys.len() {
  899. transaction.rollback().await?;
  900. tracing::warn!("Attempted to update state of non-existent proof");
  901. return Err(database::Error::ProofNotFound);
  902. }
  903. for state in current_states.values() {
  904. check_state_transition(*state, new_state)?;
  905. }
  906. query(r#"UPDATE proof SET state = :new_state WHERE y IN (:ys)"#)
  907. .bind(":new_state", new_state.to_string())
  908. .bind_vec(":ys", ys.iter().map(|y| y.to_bytes().to_vec()).collect())
  909. .execute(&transaction)
  910. .await?;
  911. transaction.commit().await?;
  912. Ok(ys.iter().map(|y| current_states.remove(y)).collect())
  913. }
  914. }
  915. #[async_trait]
  916. impl MintSignaturesDatabase for MintSqliteDatabase {
  917. type Err = database::Error;
  918. async fn add_blind_signatures(
  919. &self,
  920. blinded_messages: &[PublicKey],
  921. blind_signatures: &[BlindSignature],
  922. quote_id: Option<Uuid>,
  923. ) -> Result<(), Self::Err> {
  924. let transaction = self.pool.begin().await?;
  925. let current_time = unix_time();
  926. for (message, signature) in blinded_messages.iter().zip(blind_signatures) {
  927. query(
  928. r#"
  929. INSERT INTO blind_signature
  930. (y, amount, keyset_id, c, quote_id, dleq_e, dleq_s, created_time)
  931. VALUES
  932. (:y, :amount, :keyset_id, :c, :quote_id, :dleq_e, :dleq_s, :created_time)
  933. "#,
  934. )
  935. .bind(":y", message.to_bytes().to_vec())
  936. .bind(":amount", u64::from(signature.amount) as i64)
  937. .bind(":keyset_id", signature.keyset_id.to_string())
  938. .bind(":c", signature.c.to_bytes().to_vec())
  939. .bind(":quote_id", quote_id.map(|q| q.hyphenated().to_string()))
  940. .bind(
  941. ":dleq_e",
  942. signature.dleq.as_ref().map(|dleq| dleq.e.to_secret_hex()),
  943. )
  944. .bind(
  945. ":dleq_s",
  946. signature.dleq.as_ref().map(|dleq| dleq.s.to_secret_hex()),
  947. )
  948. .bind(":created_time", current_time as i64)
  949. .execute(&transaction)
  950. .await
  951. .expect("fasdas");
  952. }
  953. transaction.commit().await?;
  954. Ok(())
  955. }
  956. async fn get_blind_signatures(
  957. &self,
  958. blinded_messages: &[PublicKey],
  959. ) -> Result<Vec<Option<BlindSignature>>, Self::Err> {
  960. let mut blinded_signatures = query(
  961. r#"SELECT
  962. keyset_id,
  963. amount,
  964. c,
  965. dleq_e,
  966. dleq_s,
  967. y
  968. FROM
  969. blind_signature
  970. WHERE y IN (:y)
  971. "#,
  972. )
  973. .bind_vec(
  974. ":y",
  975. blinded_messages
  976. .iter()
  977. .map(|y| y.to_bytes().to_vec())
  978. .collect(),
  979. )
  980. .fetch_all(&self.pool)
  981. .await?
  982. .into_iter()
  983. .map(|mut row| {
  984. Ok((
  985. column_as_string!(
  986. &row.pop().ok_or(Error::InvalidDbResponse)?,
  987. PublicKey::from_hex,
  988. PublicKey::from_slice
  989. ),
  990. sqlite_row_to_blind_signature(row)?,
  991. ))
  992. })
  993. .collect::<Result<HashMap<_, _>, Error>>()?;
  994. Ok(blinded_messages
  995. .iter()
  996. .map(|y| blinded_signatures.remove(y))
  997. .collect())
  998. }
  999. async fn get_blind_signatures_for_keyset(
  1000. &self,
  1001. keyset_id: &Id,
  1002. ) -> Result<Vec<BlindSignature>, Self::Err> {
  1003. Ok(query(
  1004. r#"
  1005. SELECT
  1006. keyset_id,
  1007. amount,
  1008. c,
  1009. dleq_e,
  1010. dleq_s
  1011. FROM
  1012. blind_signature
  1013. WHERE
  1014. keyset_id=:keyset_id
  1015. "#,
  1016. )
  1017. .bind(":keyset_id", keyset_id.to_string())
  1018. .fetch_all(&self.pool)
  1019. .await?
  1020. .into_iter()
  1021. .map(sqlite_row_to_blind_signature)
  1022. .collect::<Result<Vec<BlindSignature>, _>>()?)
  1023. }
  1024. /// Get [`BlindSignature`]s for quote
  1025. async fn get_blind_signatures_for_quote(
  1026. &self,
  1027. quote_id: &Uuid,
  1028. ) -> Result<Vec<BlindSignature>, Self::Err> {
  1029. Ok(query(
  1030. r#"
  1031. SELECT
  1032. keyset_id,
  1033. amount,
  1034. c,
  1035. dleq_e,
  1036. dleq_s
  1037. FROM
  1038. blind_signature
  1039. WHERE
  1040. quote_id=:quote_id
  1041. "#,
  1042. )
  1043. .bind(":quote_id", quote_id.to_string())
  1044. .fetch_all(&self.pool)
  1045. .await?
  1046. .into_iter()
  1047. .map(sqlite_row_to_blind_signature)
  1048. .collect::<Result<Vec<BlindSignature>, _>>()?)
  1049. }
  1050. }
  1051. #[async_trait]
  1052. impl MintDatabase<database::Error> for MintSqliteDatabase {
  1053. async fn set_mint_info(&self, mint_info: MintInfo) -> Result<(), database::Error> {
  1054. Ok(self.set_to_config("mint_info", &mint_info).await?)
  1055. }
  1056. async fn get_mint_info(&self) -> Result<MintInfo, database::Error> {
  1057. Ok(self.fetch_from_config("mint_info").await?)
  1058. }
  1059. async fn set_quote_ttl(&self, quote_ttl: QuoteTTL) -> Result<(), database::Error> {
  1060. Ok(self.set_to_config("quote_ttl", &quote_ttl).await?)
  1061. }
  1062. async fn get_quote_ttl(&self) -> Result<QuoteTTL, database::Error> {
  1063. Ok(self.fetch_from_config("quote_ttl").await?)
  1064. }
  1065. }
  1066. fn sqlite_row_to_keyset_info(row: Vec<Column>) -> Result<MintKeySetInfo, Error> {
  1067. unpack_into!(
  1068. let (
  1069. id,
  1070. unit,
  1071. active,
  1072. valid_from,
  1073. valid_to,
  1074. derivation_path,
  1075. derivation_path_index,
  1076. max_order,
  1077. row_keyset_ppk
  1078. ) = row
  1079. );
  1080. Ok(MintKeySetInfo {
  1081. id: column_as_string!(id, Id::from_str, Id::from_bytes),
  1082. unit: column_as_string!(unit, CurrencyUnit::from_str),
  1083. active: matches!(active, Column::Integer(1)),
  1084. valid_from: column_as_number!(valid_from),
  1085. valid_to: column_as_nullable_number!(valid_to),
  1086. derivation_path: column_as_string!(derivation_path, DerivationPath::from_str),
  1087. derivation_path_index: column_as_nullable_number!(derivation_path_index),
  1088. max_order: column_as_number!(max_order),
  1089. input_fee_ppk: column_as_number!(row_keyset_ppk),
  1090. })
  1091. }
  1092. fn sqlite_row_to_mint_quote(row: Vec<Column>) -> Result<MintQuote, Error> {
  1093. unpack_into!(
  1094. let (
  1095. id, amount, unit, request, state, expiry, request_lookup_id,
  1096. pubkey, created_time, paid_time, issued_time
  1097. ) = row
  1098. );
  1099. let request = column_as_string!(&request);
  1100. let request_lookup_id = column_as_nullable_string!(&request_lookup_id).unwrap_or_else(|| {
  1101. Bolt11Invoice::from_str(&request)
  1102. .map(|invoice| invoice.payment_hash().to_string())
  1103. .unwrap_or_else(|_| request.clone())
  1104. });
  1105. let pubkey = column_as_nullable_string!(&pubkey)
  1106. .map(|pk| PublicKey::from_hex(&pk))
  1107. .transpose()?;
  1108. let id = column_as_string!(id);
  1109. let amount: u64 = column_as_number!(amount);
  1110. Ok(MintQuote {
  1111. id: Uuid::parse_str(&id).map_err(|_| Error::InvalidUuid(id))?,
  1112. amount: Amount::from(amount),
  1113. unit: column_as_string!(unit, CurrencyUnit::from_str),
  1114. request,
  1115. state: column_as_string!(state, MintQuoteState::from_str),
  1116. expiry: column_as_number!(expiry),
  1117. request_lookup_id,
  1118. pubkey,
  1119. created_time: column_as_number!(created_time),
  1120. paid_time: column_as_nullable_number!(paid_time).map(|p| p),
  1121. issued_time: column_as_nullable_number!(issued_time).map(|p| p),
  1122. })
  1123. }
  1124. fn sqlite_row_to_melt_quote(row: Vec<Column>) -> Result<mint::MeltQuote, Error> {
  1125. unpack_into!(
  1126. let (
  1127. id,
  1128. unit,
  1129. amount,
  1130. request,
  1131. fee_reserve,
  1132. state,
  1133. expiry,
  1134. payment_preimage,
  1135. request_lookup_id,
  1136. msat_to_pay,
  1137. created_time,
  1138. paid_time
  1139. ) = row
  1140. );
  1141. let id = column_as_string!(id);
  1142. let amount: u64 = column_as_number!(amount);
  1143. let fee_reserve: u64 = column_as_number!(fee_reserve);
  1144. let request = column_as_string!(&request);
  1145. let request_lookup_id = column_as_nullable_string!(&request_lookup_id).unwrap_or_else(|| {
  1146. Bolt11Invoice::from_str(&request)
  1147. .map(|invoice| invoice.payment_hash().to_string())
  1148. .unwrap_or_else(|_| request.clone())
  1149. });
  1150. let msat_to_pay: Option<u64> = column_as_nullable_number!(msat_to_pay);
  1151. Ok(mint::MeltQuote {
  1152. id: Uuid::parse_str(&id).map_err(|_| Error::InvalidUuid(id))?,
  1153. amount: Amount::from(amount),
  1154. fee_reserve: Amount::from(fee_reserve),
  1155. unit: column_as_string!(unit, CurrencyUnit::from_str),
  1156. request,
  1157. payment_preimage: column_as_nullable_string!(payment_preimage),
  1158. msat_to_pay: msat_to_pay.map(Amount::from),
  1159. state: column_as_string!(state, QuoteState::from_str),
  1160. expiry: column_as_number!(expiry),
  1161. request_lookup_id,
  1162. created_time: column_as_number!(created_time),
  1163. paid_time: column_as_nullable_number!(paid_time).map(|p| p),
  1164. })
  1165. }
  1166. fn sqlite_row_to_proof(row: Vec<Column>) -> Result<Proof, Error> {
  1167. unpack_into!(
  1168. let (
  1169. amount,
  1170. keyset_id,
  1171. secret,
  1172. c,
  1173. witness
  1174. ) = row
  1175. );
  1176. let amount: u64 = column_as_number!(amount);
  1177. Ok(Proof {
  1178. amount: Amount::from(amount),
  1179. keyset_id: column_as_string!(keyset_id, Id::from_str),
  1180. secret: column_as_string!(secret, Secret::from_str),
  1181. c: column_as_string!(c, PublicKey::from_hex, PublicKey::from_slice),
  1182. witness: column_as_nullable_string!(witness).and_then(|w| serde_json::from_str(&w).ok()),
  1183. dleq: None,
  1184. })
  1185. }
  1186. fn sqlite_row_to_proof_with_state(row: Vec<Column>) -> Result<(Proof, Option<State>), Error> {
  1187. unpack_into!(
  1188. let (
  1189. keyset_id, amount, secret, c, witness, state
  1190. ) = row
  1191. );
  1192. let amount: u64 = column_as_number!(amount);
  1193. let state = column_as_nullable_string!(state).and_then(|s| State::from_str(&s).ok());
  1194. Ok((
  1195. Proof {
  1196. amount: Amount::from(amount),
  1197. keyset_id: column_as_string!(keyset_id, Id::from_str, Id::from_bytes),
  1198. secret: column_as_string!(secret, Secret::from_str),
  1199. c: column_as_string!(c, PublicKey::from_hex, PublicKey::from_slice),
  1200. witness: column_as_nullable_string!(witness)
  1201. .and_then(|w| serde_json::from_str(&w).ok()),
  1202. dleq: None,
  1203. },
  1204. state,
  1205. ))
  1206. }
  1207. fn sqlite_row_to_blind_signature(row: Vec<Column>) -> Result<BlindSignature, Error> {
  1208. unpack_into!(
  1209. let (
  1210. keyset_id, amount, c, dleq_e, dleq_s
  1211. ) = row
  1212. );
  1213. let dleq = match (
  1214. column_as_nullable_string!(dleq_e),
  1215. column_as_nullable_string!(dleq_s),
  1216. ) {
  1217. (Some(e), Some(s)) => Some(BlindSignatureDleq {
  1218. e: SecretKey::from_hex(e)?,
  1219. s: SecretKey::from_hex(s)?,
  1220. }),
  1221. _ => None,
  1222. };
  1223. let amount: u64 = column_as_number!(amount);
  1224. Ok(BlindSignature {
  1225. amount: Amount::from(amount),
  1226. keyset_id: column_as_string!(keyset_id, Id::from_str, Id::from_bytes),
  1227. c: column_as_string!(c, PublicKey::from_hex, PublicKey::from_slice),
  1228. dleq,
  1229. })
  1230. }
  1231. fn sqlite_row_to_melt_request(
  1232. row: Vec<Column>,
  1233. ) -> Result<(MeltRequest<Uuid>, PaymentProcessorKey), Error> {
  1234. unpack_into!(
  1235. let (
  1236. id,
  1237. inputs,
  1238. outputs,
  1239. method,
  1240. unit
  1241. ) = row
  1242. );
  1243. let id = column_as_string!(id);
  1244. let melt_request = MeltRequest::new(
  1245. Uuid::parse_str(&id).map_err(|_| Error::InvalidUuid(id))?,
  1246. column_as_string!(&inputs, serde_json::from_str),
  1247. column_as_nullable_string!(&outputs).and_then(|w| serde_json::from_str(&w).ok()),
  1248. );
  1249. let ln_key = PaymentProcessorKey {
  1250. unit: column_as_string!(&unit, CurrencyUnit::from_str),
  1251. method: column_as_string!(&method, PaymentMethod::from_str),
  1252. };
  1253. Ok((melt_request, ln_key))
  1254. }
  1255. #[cfg(test)]
  1256. mod tests {
  1257. use cdk_common::mint::MintKeySetInfo;
  1258. use cdk_common::{mint_db_test, Amount};
  1259. use super::*;
  1260. #[tokio::test]
  1261. async fn test_remove_spent_proofs() {
  1262. let db = memory::empty().await.unwrap();
  1263. // Create a keyset and add it to the database
  1264. let keyset_id = Id::from_str("00916bbf7ef91a36").unwrap();
  1265. let keyset_info = MintKeySetInfo {
  1266. id: keyset_id,
  1267. unit: CurrencyUnit::Sat,
  1268. active: true,
  1269. valid_from: 0,
  1270. valid_to: None,
  1271. derivation_path: bitcoin::bip32::DerivationPath::from_str("m/0'/0'/0'").unwrap(),
  1272. derivation_path_index: Some(0),
  1273. max_order: 32,
  1274. input_fee_ppk: 0,
  1275. };
  1276. db.add_keyset_info(keyset_info).await.unwrap();
  1277. let proofs = vec![
  1278. Proof {
  1279. amount: Amount::from(100),
  1280. keyset_id,
  1281. secret: Secret::generate(),
  1282. c: SecretKey::generate().public_key(),
  1283. witness: None,
  1284. dleq: None,
  1285. },
  1286. Proof {
  1287. amount: Amount::from(200),
  1288. keyset_id,
  1289. secret: Secret::generate(),
  1290. c: SecretKey::generate().public_key(),
  1291. witness: None,
  1292. dleq: None,
  1293. },
  1294. ];
  1295. // Add proofs to database
  1296. db.add_proofs(proofs.clone(), None).await.unwrap();
  1297. // Mark one proof as spent
  1298. db.update_proofs_states(&[proofs[0].y().unwrap()], State::Spent)
  1299. .await
  1300. .unwrap();
  1301. // Try to remove both proofs - should fail because one is spent
  1302. let result = db
  1303. .remove_proofs(&[proofs[0].y().unwrap(), proofs[1].y().unwrap()], None)
  1304. .await;
  1305. assert!(result.is_err());
  1306. assert!(matches!(
  1307. result.unwrap_err(),
  1308. database::Error::AttemptRemoveSpentProof
  1309. ));
  1310. // Verify both proofs still exist
  1311. let states = db
  1312. .get_proofs_states(&[proofs[0].y().unwrap(), proofs[1].y().unwrap()])
  1313. .await
  1314. .unwrap();
  1315. assert_eq!(states.len(), 2);
  1316. assert_eq!(states[0], Some(State::Spent));
  1317. assert_eq!(states[1], Some(State::Unspent));
  1318. }
  1319. #[tokio::test]
  1320. async fn test_update_spent_proofs() {
  1321. let db = memory::empty().await.unwrap();
  1322. // Create a keyset and add it to the database
  1323. let keyset_id = Id::from_str("00916bbf7ef91a36").unwrap();
  1324. let keyset_info = MintKeySetInfo {
  1325. id: keyset_id,
  1326. unit: CurrencyUnit::Sat,
  1327. active: true,
  1328. valid_from: 0,
  1329. valid_to: None,
  1330. derivation_path: bitcoin::bip32::DerivationPath::from_str("m/0'/0'/0'").unwrap(),
  1331. derivation_path_index: Some(0),
  1332. max_order: 32,
  1333. input_fee_ppk: 0,
  1334. };
  1335. db.add_keyset_info(keyset_info).await.unwrap();
  1336. let proofs = vec![
  1337. Proof {
  1338. amount: Amount::from(100),
  1339. keyset_id,
  1340. secret: Secret::generate(),
  1341. c: SecretKey::generate().public_key(),
  1342. witness: None,
  1343. dleq: None,
  1344. },
  1345. Proof {
  1346. amount: Amount::from(200),
  1347. keyset_id,
  1348. secret: Secret::generate(),
  1349. c: SecretKey::generate().public_key(),
  1350. witness: None,
  1351. dleq: None,
  1352. },
  1353. ];
  1354. // Add proofs to database
  1355. db.add_proofs(proofs.clone(), None).await.unwrap();
  1356. // Mark one proof as spent
  1357. db.update_proofs_states(&[proofs[0].y().unwrap()], State::Spent)
  1358. .await
  1359. .unwrap();
  1360. // Try to update both proofs - should fail because one is spent
  1361. let result = db
  1362. .update_proofs_states(&[proofs[0].y().unwrap()], State::Unspent)
  1363. .await;
  1364. assert!(result.is_err());
  1365. assert!(matches!(
  1366. result.unwrap_err(),
  1367. database::Error::AttemptUpdateSpentProof
  1368. ));
  1369. // Verify states haven't changed
  1370. let states = db
  1371. .get_proofs_states(&[proofs[0].y().unwrap(), proofs[1].y().unwrap()])
  1372. .await
  1373. .unwrap();
  1374. assert_eq!(states.len(), 2);
  1375. assert_eq!(states[0], Some(State::Spent));
  1376. assert_eq!(states[1], Some(State::Unspent));
  1377. }
  1378. async fn provide_db() -> MintSqliteDatabase {
  1379. memory::empty().await.unwrap()
  1380. }
  1381. mint_db_test!(provide_db);
  1382. }