mod.rs 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538
  1. //! SQLite Wallet Database
  2. use std::collections::HashMap;
  3. use std::fmt::Debug;
  4. use std::str::FromStr;
  5. use std::sync::Arc;
  6. use async_trait::async_trait;
  7. use cdk_common::common::ProofInfo;
  8. use cdk_common::database::{
  9. ConversionError, DbTransactionFinalizer, Error, WalletDatabase, WalletDatabaseTransaction,
  10. };
  11. use cdk_common::mint_url::MintUrl;
  12. use cdk_common::nuts::{MeltQuoteState, MintQuoteState};
  13. use cdk_common::secret::Secret;
  14. use cdk_common::wallet::{self, MintQuote, Transaction, TransactionDirection, TransactionId};
  15. use cdk_common::{
  16. database, Amount, CurrencyUnit, Id, KeySet, KeySetInfo, Keys, MintInfo, PaymentMethod, Proof,
  17. ProofDleq, PublicKey, SecretKey, SpendingConditions, State,
  18. };
  19. use tracing::instrument;
  20. use crate::common::migrate;
  21. use crate::database::{ConnectionWithTransaction, DatabaseExecutor};
  22. use crate::pool::{DatabasePool, Pool, PooledResource};
  23. use crate::stmt::{query, Column};
  24. use crate::{
  25. column_as_binary, column_as_nullable_binary, column_as_nullable_number,
  26. column_as_nullable_string, column_as_number, column_as_string, unpack_into,
  27. };
  28. #[rustfmt::skip]
  29. mod migrations {
  30. include!(concat!(env!("OUT_DIR"), "/migrations_wallet.rs"));
  31. }
  32. /// Wallet SQLite Database
  33. #[derive(Debug, Clone)]
  34. pub struct SQLWalletDatabase<RM>
  35. where
  36. RM: DatabasePool + 'static,
  37. {
  38. pool: Arc<Pool<RM>>,
  39. }
  40. /// SQL Transaction Writer
  41. pub struct SQLWalletTransaction<RM>
  42. where
  43. RM: DatabasePool + 'static,
  44. {
  45. inner: ConnectionWithTransaction<RM::Connection, PooledResource<RM>>,
  46. }
  47. #[async_trait]
  48. impl<RM> WalletDatabaseTransaction<Error> for SQLWalletTransaction<RM>
  49. where
  50. RM: DatabasePool + 'static,
  51. {
  52. #[instrument(skip(self, mint_info))]
  53. async fn add_mint(
  54. &mut self,
  55. mint_url: MintUrl,
  56. mint_info: Option<MintInfo>,
  57. ) -> Result<(), Error> {
  58. let (
  59. name,
  60. pubkey,
  61. version,
  62. description,
  63. description_long,
  64. contact,
  65. nuts,
  66. icon_url,
  67. urls,
  68. motd,
  69. time,
  70. tos_url,
  71. ) = match mint_info {
  72. Some(mint_info) => {
  73. let MintInfo {
  74. name,
  75. pubkey,
  76. version,
  77. description,
  78. description_long,
  79. contact,
  80. nuts,
  81. icon_url,
  82. urls,
  83. motd,
  84. time,
  85. tos_url,
  86. } = mint_info;
  87. (
  88. name,
  89. pubkey.map(|p| p.to_bytes().to_vec()),
  90. version.map(|v| serde_json::to_string(&v).ok()),
  91. description,
  92. description_long,
  93. contact.map(|c| serde_json::to_string(&c).ok()),
  94. serde_json::to_string(&nuts).ok(),
  95. icon_url,
  96. urls.map(|c| serde_json::to_string(&c).ok()),
  97. motd,
  98. time,
  99. tos_url,
  100. )
  101. }
  102. None => (
  103. None, None, None, None, None, None, None, None, None, None, None, None,
  104. ),
  105. };
  106. query(
  107. r#"
  108. INSERT INTO mint
  109. (
  110. mint_url, name, pubkey, version, description, description_long,
  111. contact, nuts, icon_url, urls, motd, mint_time, tos_url
  112. )
  113. VALUES
  114. (
  115. :mint_url, :name, :pubkey, :version, :description, :description_long,
  116. :contact, :nuts, :icon_url, :urls, :motd, :mint_time, :tos_url
  117. )
  118. ON CONFLICT(mint_url) DO UPDATE SET
  119. name = excluded.name,
  120. pubkey = excluded.pubkey,
  121. version = excluded.version,
  122. description = excluded.description,
  123. description_long = excluded.description_long,
  124. contact = excluded.contact,
  125. nuts = excluded.nuts,
  126. icon_url = excluded.icon_url,
  127. urls = excluded.urls,
  128. motd = excluded.motd,
  129. mint_time = excluded.mint_time,
  130. tos_url = excluded.tos_url
  131. ;
  132. "#,
  133. )?
  134. .bind("mint_url", mint_url.to_string())
  135. .bind("name", name)
  136. .bind("pubkey", pubkey)
  137. .bind("version", version)
  138. .bind("description", description)
  139. .bind("description_long", description_long)
  140. .bind("contact", contact)
  141. .bind("nuts", nuts)
  142. .bind("icon_url", icon_url)
  143. .bind("urls", urls)
  144. .bind("motd", motd)
  145. .bind("mint_time", time.map(|v| v as i64))
  146. .bind("tos_url", tos_url)
  147. .execute(&self.inner)
  148. .await?;
  149. Ok(())
  150. }
  151. #[instrument(skip(self))]
  152. async fn remove_mint(&mut self, mint_url: MintUrl) -> Result<(), Error> {
  153. query(r#"DELETE FROM mint WHERE mint_url=:mint_url"#)?
  154. .bind("mint_url", mint_url.to_string())
  155. .execute(&self.inner)
  156. .await?;
  157. Ok(())
  158. }
  159. #[instrument(skip(self))]
  160. async fn update_mint_url(
  161. &mut self,
  162. old_mint_url: MintUrl,
  163. new_mint_url: MintUrl,
  164. ) -> Result<(), Error> {
  165. let tables = ["mint_quote", "proof"];
  166. for table in &tables {
  167. query(&format!(
  168. r#"
  169. UPDATE {table}
  170. SET mint_url = :new_mint_url
  171. WHERE mint_url = :old_mint_url
  172. "#
  173. ))?
  174. .bind("new_mint_url", new_mint_url.to_string())
  175. .bind("old_mint_url", old_mint_url.to_string())
  176. .execute(&self.inner)
  177. .await?;
  178. }
  179. Ok(())
  180. }
  181. #[instrument(skip(self, keysets))]
  182. async fn add_mint_keysets(
  183. &mut self,
  184. mint_url: MintUrl,
  185. keysets: Vec<KeySetInfo>,
  186. ) -> Result<(), Error> {
  187. for keyset in keysets {
  188. query(
  189. r#"
  190. INSERT INTO keyset
  191. (mint_url, id, unit, active, input_fee_ppk, final_expiry, keyset_u32)
  192. VALUES
  193. (:mint_url, :id, :unit, :active, :input_fee_ppk, :final_expiry, :keyset_u32)
  194. ON CONFLICT(id) DO UPDATE SET
  195. active = excluded.active,
  196. input_fee_ppk = excluded.input_fee_ppk
  197. "#,
  198. )?
  199. .bind("mint_url", mint_url.to_string())
  200. .bind("id", keyset.id.to_string())
  201. .bind("unit", keyset.unit.to_string())
  202. .bind("active", keyset.active)
  203. .bind("input_fee_ppk", keyset.input_fee_ppk as i64)
  204. .bind("final_expiry", keyset.final_expiry.map(|v| v as i64))
  205. .bind("keyset_u32", u32::from(keyset.id))
  206. .execute(&self.inner)
  207. .await?;
  208. }
  209. Ok(())
  210. }
  211. #[instrument(skip_all)]
  212. async fn add_mint_quote(&mut self, quote: MintQuote) -> Result<(), Error> {
  213. query(
  214. r#"
  215. INSERT INTO mint_quote
  216. (id, mint_url, amount, unit, request, state, expiry, secret_key, payment_method, amount_issued, amount_paid)
  217. VALUES
  218. (:id, :mint_url, :amount, :unit, :request, :state, :expiry, :secret_key, :payment_method, :amount_issued, :amount_paid)
  219. ON CONFLICT(id) DO UPDATE SET
  220. mint_url = excluded.mint_url,
  221. amount = excluded.amount,
  222. unit = excluded.unit,
  223. request = excluded.request,
  224. state = excluded.state,
  225. expiry = excluded.expiry,
  226. secret_key = excluded.secret_key,
  227. payment_method = excluded.payment_method,
  228. amount_issued = excluded.amount_issued,
  229. amount_paid = excluded.amount_paid
  230. ;
  231. "#,
  232. )?
  233. .bind("id", quote.id.to_string())
  234. .bind("mint_url", quote.mint_url.to_string())
  235. .bind("amount", quote.amount.map(|a| a.to_i64()))
  236. .bind("unit", quote.unit.to_string())
  237. .bind("request", quote.request)
  238. .bind("state", quote.state.to_string())
  239. .bind("expiry", quote.expiry as i64)
  240. .bind("secret_key", quote.secret_key.map(|p| p.to_string()))
  241. .bind("payment_method", quote.payment_method.to_string())
  242. .bind("amount_issued", quote.amount_issued.to_i64())
  243. .bind("amount_paid", quote.amount_paid.to_i64())
  244. .execute(&self.inner).await?;
  245. Ok(())
  246. }
  247. #[instrument(skip(self))]
  248. async fn remove_mint_quote(&mut self, quote_id: &str) -> Result<(), Error> {
  249. query(r#"DELETE FROM mint_quote WHERE id=:id"#)?
  250. .bind("id", quote_id.to_string())
  251. .execute(&self.inner)
  252. .await?;
  253. Ok(())
  254. }
  255. #[instrument(skip_all)]
  256. async fn add_melt_quote(&mut self, quote: wallet::MeltQuote) -> Result<(), Error> {
  257. query(
  258. r#"
  259. INSERT INTO melt_quote
  260. (id, unit, amount, request, fee_reserve, state, expiry, payment_method)
  261. VALUES
  262. (:id, :unit, :amount, :request, :fee_reserve, :state, :expiry, :payment_method)
  263. ON CONFLICT(id) DO UPDATE SET
  264. unit = excluded.unit,
  265. amount = excluded.amount,
  266. request = excluded.request,
  267. fee_reserve = excluded.fee_reserve,
  268. state = excluded.state,
  269. expiry = excluded.expiry,
  270. payment_method = excluded.payment_method
  271. ;
  272. "#,
  273. )?
  274. .bind("id", quote.id.to_string())
  275. .bind("unit", quote.unit.to_string())
  276. .bind("amount", u64::from(quote.amount) as i64)
  277. .bind("request", quote.request)
  278. .bind("fee_reserve", u64::from(quote.fee_reserve) as i64)
  279. .bind("state", quote.state.to_string())
  280. .bind("expiry", quote.expiry as i64)
  281. .bind("payment_method", quote.payment_method.to_string())
  282. .execute(&self.inner)
  283. .await?;
  284. Ok(())
  285. }
  286. #[instrument(skip(self))]
  287. async fn remove_melt_quote(&mut self, quote_id: &str) -> Result<(), Error> {
  288. query(r#"DELETE FROM melt_quote WHERE id=:id"#)?
  289. .bind("id", quote_id.to_owned())
  290. .execute(&self.inner)
  291. .await?;
  292. Ok(())
  293. }
  294. #[instrument(skip_all)]
  295. async fn add_keys(&mut self, keyset: KeySet) -> Result<(), Error> {
  296. // Recompute ID for verification
  297. keyset.verify_id()?;
  298. query(
  299. r#"
  300. INSERT INTO key
  301. (id, keys, keyset_u32)
  302. VALUES
  303. (:id, :keys, :keyset_u32)
  304. "#,
  305. )?
  306. .bind("id", keyset.id.to_string())
  307. .bind(
  308. "keys",
  309. serde_json::to_string(&keyset.keys).map_err(Error::from)?,
  310. )
  311. .bind("keyset_u32", u32::from(keyset.id))
  312. .execute(&self.inner)
  313. .await?;
  314. Ok(())
  315. }
  316. #[instrument(skip(self))]
  317. async fn add_transaction(&mut self, transaction: Transaction) -> Result<(), Error> {
  318. let mint_url = transaction.mint_url.to_string();
  319. let direction = transaction.direction.to_string();
  320. let unit = transaction.unit.to_string();
  321. let amount = u64::from(transaction.amount) as i64;
  322. let fee = u64::from(transaction.fee) as i64;
  323. let ys = transaction
  324. .ys
  325. .iter()
  326. .flat_map(|y| y.to_bytes().to_vec())
  327. .collect::<Vec<_>>();
  328. let id = transaction.id();
  329. query(
  330. r#"
  331. INSERT INTO transactions
  332. (id, mint_url, direction, unit, amount, fee, ys, timestamp, memo, metadata, quote_id, payment_request, payment_proof)
  333. VALUES
  334. (:id, :mint_url, :direction, :unit, :amount, :fee, :ys, :timestamp, :memo, :metadata, :quote_id, :payment_request, :payment_proof)
  335. ON CONFLICT(id) DO UPDATE SET
  336. mint_url = excluded.mint_url,
  337. direction = excluded.direction,
  338. unit = excluded.unit,
  339. amount = excluded.amount,
  340. fee = excluded.fee,
  341. timestamp = excluded.timestamp,
  342. memo = excluded.memo,
  343. metadata = excluded.metadata,
  344. quote_id = excluded.quote_id,
  345. payment_request = excluded.payment_request,
  346. payment_proof = excluded.payment_proof
  347. ;
  348. "#,
  349. )?
  350. .bind("id", id.as_slice().to_vec())
  351. .bind("mint_url", mint_url)
  352. .bind("direction", direction)
  353. .bind("unit", unit)
  354. .bind("amount", amount)
  355. .bind("fee", fee)
  356. .bind("ys", ys)
  357. .bind("timestamp", transaction.timestamp as i64)
  358. .bind("memo", transaction.memo)
  359. .bind(
  360. "metadata",
  361. serde_json::to_string(&transaction.metadata).map_err(Error::from)?,
  362. )
  363. .bind("quote_id", transaction.quote_id)
  364. .bind("payment_request", transaction.payment_request)
  365. .bind("payment_proof", transaction.payment_proof)
  366. .execute(&self.inner)
  367. .await?;
  368. Ok(())
  369. }
  370. #[instrument(skip(self))]
  371. async fn remove_transaction(&mut self, transaction_id: TransactionId) -> Result<(), Error> {
  372. query(r#"DELETE FROM transactions WHERE id=:id"#)?
  373. .bind("id", transaction_id.as_slice().to_vec())
  374. .execute(&self.inner)
  375. .await?;
  376. Ok(())
  377. }
  378. #[instrument(skip(self), fields(keyset_id = %keyset_id))]
  379. async fn increment_keyset_counter(&mut self, keyset_id: &Id, count: u32) -> Result<u32, Error> {
  380. // Lock the row and get current counter from keyset_counter table
  381. let current_counter = query(
  382. r#"
  383. SELECT counter
  384. FROM keyset_counter
  385. WHERE keyset_id=:keyset_id
  386. FOR UPDATE
  387. "#,
  388. )?
  389. .bind("keyset_id", keyset_id.to_string())
  390. .pluck(&self.inner)
  391. .await?
  392. .map(|n| Ok::<_, Error>(column_as_number!(n)))
  393. .transpose()?
  394. .unwrap_or(0);
  395. let new_counter = current_counter + count;
  396. // Upsert the new counter value
  397. query(
  398. r#"
  399. INSERT INTO keyset_counter (keyset_id, counter)
  400. VALUES (:keyset_id, :new_counter)
  401. ON CONFLICT(keyset_id) DO UPDATE SET
  402. counter = excluded.counter
  403. "#,
  404. )?
  405. .bind("keyset_id", keyset_id.to_string())
  406. .bind("new_counter", new_counter)
  407. .execute(&self.inner)
  408. .await?;
  409. Ok(new_counter)
  410. }
  411. #[instrument(skip(self))]
  412. async fn remove_keys(&mut self, id: &Id) -> Result<(), Error> {
  413. query(r#"DELETE FROM key WHERE id = :id"#)?
  414. .bind("id", id.to_string())
  415. .pluck(&self.inner)
  416. .await?;
  417. Ok(())
  418. }
  419. async fn update_proofs_state(&mut self, ys: Vec<PublicKey>, state: State) -> Result<(), Error> {
  420. query("UPDATE proof SET state = :state WHERE y IN (:ys)")?
  421. .bind_vec("ys", ys.iter().map(|y| y.to_bytes().to_vec()).collect())
  422. .bind("state", state.to_string())
  423. .execute(&self.inner)
  424. .await?;
  425. Ok(())
  426. }
  427. async fn update_proofs(
  428. &mut self,
  429. added: Vec<ProofInfo>,
  430. removed_ys: Vec<PublicKey>,
  431. ) -> Result<(), Error> {
  432. // TODO: Use a transaction for all these operations
  433. for proof in added {
  434. query(
  435. r#"
  436. INSERT INTO proof
  437. (y, mint_url, state, spending_condition, unit, amount, keyset_id, secret, c, witness, dleq_e, dleq_s, dleq_r)
  438. VALUES
  439. (:y, :mint_url, :state, :spending_condition, :unit, :amount, :keyset_id, :secret, :c, :witness, :dleq_e, :dleq_s, :dleq_r)
  440. ON CONFLICT(y) DO UPDATE SET
  441. mint_url = excluded.mint_url,
  442. state = excluded.state,
  443. spending_condition = excluded.spending_condition,
  444. unit = excluded.unit,
  445. amount = excluded.amount,
  446. keyset_id = excluded.keyset_id,
  447. secret = excluded.secret,
  448. c = excluded.c,
  449. witness = excluded.witness,
  450. dleq_e = excluded.dleq_e,
  451. dleq_s = excluded.dleq_s,
  452. dleq_r = excluded.dleq_r
  453. ;
  454. "#,
  455. )?
  456. .bind("y", proof.y.to_bytes().to_vec())
  457. .bind("mint_url", proof.mint_url.to_string())
  458. .bind("state",proof.state.to_string())
  459. .bind(
  460. "spending_condition",
  461. proof
  462. .spending_condition
  463. .map(|s| serde_json::to_string(&s).ok()),
  464. )
  465. .bind("unit", proof.unit.to_string())
  466. .bind("amount", u64::from(proof.proof.amount) as i64)
  467. .bind("keyset_id", proof.proof.keyset_id.to_string())
  468. .bind("secret", proof.proof.secret.to_string())
  469. .bind("c", proof.proof.c.to_bytes().to_vec())
  470. .bind(
  471. "witness",
  472. proof
  473. .proof
  474. .witness
  475. .and_then(|w| serde_json::to_string(&w).ok()),
  476. )
  477. .bind(
  478. "dleq_e",
  479. proof.proof.dleq.as_ref().map(|dleq| dleq.e.to_secret_bytes().to_vec()),
  480. )
  481. .bind(
  482. "dleq_s",
  483. proof.proof.dleq.as_ref().map(|dleq| dleq.s.to_secret_bytes().to_vec()),
  484. )
  485. .bind(
  486. "dleq_r",
  487. proof.proof.dleq.as_ref().map(|dleq| dleq.r.to_secret_bytes().to_vec()),
  488. )
  489. .execute(&self.inner).await?;
  490. }
  491. if !removed_ys.is_empty() {
  492. query(r#"DELETE FROM proof WHERE y IN (:ys)"#)?
  493. .bind_vec(
  494. "ys",
  495. removed_ys.iter().map(|y| y.to_bytes().to_vec()).collect(),
  496. )
  497. .execute(&self.inner)
  498. .await?;
  499. }
  500. Ok(())
  501. }
  502. #[instrument(skip(self), fields(keyset_id = %keyset_id))]
  503. async fn get_keyset_by_id(&mut self, keyset_id: &Id) -> Result<Option<KeySetInfo>, Error> {
  504. get_keyset_by_id_inner(&self.inner, keyset_id, true).await
  505. }
  506. #[instrument(skip(self), fields(keyset_id = %id))]
  507. async fn get_keys(&mut self, id: &Id) -> Result<Option<Keys>, Error> {
  508. get_keys_inner(&self.inner, id).await
  509. }
  510. #[instrument(skip(self))]
  511. async fn get_mint_quote(&mut self, quote_id: &str) -> Result<Option<MintQuote>, Error> {
  512. get_mint_quote_inner(&self.inner, quote_id, true).await
  513. }
  514. #[instrument(skip(self))]
  515. async fn get_melt_quote(&mut self, quote_id: &str) -> Result<Option<wallet::MeltQuote>, Error> {
  516. get_melt_quote_inner(&self.inner, quote_id, true).await
  517. }
  518. #[instrument(skip(self, state, spending_conditions))]
  519. async fn get_proofs(
  520. &mut self,
  521. mint_url: Option<MintUrl>,
  522. unit: Option<CurrencyUnit>,
  523. state: Option<Vec<State>>,
  524. spending_conditions: Option<Vec<SpendingConditions>>,
  525. ) -> Result<Vec<ProofInfo>, Error> {
  526. get_proofs_inner(
  527. &self.inner,
  528. mint_url,
  529. unit,
  530. state,
  531. spending_conditions,
  532. true,
  533. )
  534. .await
  535. }
  536. }
  537. #[async_trait]
  538. impl<RM> DbTransactionFinalizer for SQLWalletTransaction<RM>
  539. where
  540. RM: DatabasePool + 'static,
  541. {
  542. type Err = Error;
  543. async fn commit(self: Box<Self>) -> Result<(), Error> {
  544. Ok(self.inner.commit().await?)
  545. }
  546. async fn rollback(self: Box<Self>) -> Result<(), Error> {
  547. Ok(self.inner.rollback().await?)
  548. }
  549. }
  550. // Inline helper functions that work with both connections and transactions
  551. #[inline]
  552. async fn get_keyset_by_id_inner<T>(
  553. executor: &T,
  554. keyset_id: &Id,
  555. for_update: bool,
  556. ) -> Result<Option<KeySetInfo>, Error>
  557. where
  558. T: DatabaseExecutor,
  559. {
  560. let for_update_clause = if for_update { "FOR UPDATE" } else { "" };
  561. let query_str = format!(
  562. r#"
  563. SELECT
  564. id,
  565. unit,
  566. active,
  567. input_fee_ppk,
  568. final_expiry
  569. FROM
  570. keyset
  571. WHERE id = :id
  572. {for_update_clause}
  573. "#
  574. );
  575. query(&query_str)?
  576. .bind("id", keyset_id.to_string())
  577. .fetch_one(executor)
  578. .await?
  579. .map(sql_row_to_keyset)
  580. .transpose()
  581. }
  582. #[inline]
  583. async fn get_keys_inner<T>(executor: &T, id: &Id) -> Result<Option<Keys>, Error>
  584. where
  585. T: DatabaseExecutor,
  586. {
  587. query(
  588. r#"
  589. SELECT
  590. keys
  591. FROM key
  592. WHERE id = :id
  593. "#,
  594. )?
  595. .bind("id", id.to_string())
  596. .pluck(executor)
  597. .await?
  598. .map(|keys| {
  599. let keys = column_as_string!(keys);
  600. serde_json::from_str(&keys).map_err(Error::from)
  601. })
  602. .transpose()
  603. }
  604. #[inline]
  605. async fn get_mint_quote_inner<T>(
  606. executor: &T,
  607. quote_id: &str,
  608. for_update: bool,
  609. ) -> Result<Option<MintQuote>, Error>
  610. where
  611. T: DatabaseExecutor,
  612. {
  613. let for_update_clause = if for_update { "FOR UPDATE" } else { "" };
  614. let query_str = format!(
  615. r#"
  616. SELECT
  617. id,
  618. mint_url,
  619. amount,
  620. unit,
  621. request,
  622. state,
  623. expiry,
  624. secret_key,
  625. payment_method,
  626. amount_issued,
  627. amount_paid
  628. FROM
  629. mint_quote
  630. WHERE
  631. id = :id
  632. {for_update_clause}
  633. "#
  634. );
  635. query(&query_str)?
  636. .bind("id", quote_id.to_string())
  637. .fetch_one(executor)
  638. .await?
  639. .map(sql_row_to_mint_quote)
  640. .transpose()
  641. }
  642. #[inline]
  643. async fn get_melt_quote_inner<T>(
  644. executor: &T,
  645. quote_id: &str,
  646. for_update: bool,
  647. ) -> Result<Option<wallet::MeltQuote>, Error>
  648. where
  649. T: DatabaseExecutor,
  650. {
  651. let for_update_clause = if for_update { "FOR UPDATE" } else { "" };
  652. let query_str = format!(
  653. r#"
  654. SELECT
  655. id,
  656. unit,
  657. amount,
  658. request,
  659. fee_reserve,
  660. state,
  661. expiry,
  662. payment_preimage,
  663. payment_method
  664. FROM
  665. melt_quote
  666. WHERE
  667. id=:id
  668. {for_update_clause}
  669. "#
  670. );
  671. query(&query_str)?
  672. .bind("id", quote_id.to_owned())
  673. .fetch_one(executor)
  674. .await?
  675. .map(sql_row_to_melt_quote)
  676. .transpose()
  677. }
  678. #[inline]
  679. async fn get_proofs_inner<T>(
  680. executor: &T,
  681. mint_url: Option<MintUrl>,
  682. unit: Option<CurrencyUnit>,
  683. state: Option<Vec<State>>,
  684. spending_conditions: Option<Vec<SpendingConditions>>,
  685. for_update: bool,
  686. ) -> Result<Vec<ProofInfo>, Error>
  687. where
  688. T: DatabaseExecutor,
  689. {
  690. let for_update_clause = if for_update { "FOR UPDATE" } else { "" };
  691. let query_str = format!(
  692. r#"
  693. SELECT
  694. amount,
  695. unit,
  696. keyset_id,
  697. secret,
  698. c,
  699. witness,
  700. dleq_e,
  701. dleq_s,
  702. dleq_r,
  703. y,
  704. mint_url,
  705. state,
  706. spending_condition
  707. FROM proof
  708. {for_update_clause}
  709. "#
  710. );
  711. Ok(query(&query_str)?
  712. .fetch_all(executor)
  713. .await?
  714. .into_iter()
  715. .filter_map(|row| {
  716. let row = sql_row_to_proof_info(row).ok()?;
  717. if row.matches_conditions(&mint_url, &unit, &state, &spending_conditions) {
  718. Some(row)
  719. } else {
  720. None
  721. }
  722. })
  723. .collect::<Vec<_>>())
  724. }
  725. impl<RM> SQLWalletDatabase<RM>
  726. where
  727. RM: DatabasePool + 'static,
  728. {
  729. /// Creates a new instance
  730. pub async fn new<X>(db: X) -> Result<Self, Error>
  731. where
  732. X: Into<RM::Config>,
  733. {
  734. let pool = Pool::new(db.into());
  735. Self::migrate(pool.get().map_err(|e| Error::Database(Box::new(e)))?).await?;
  736. Ok(Self { pool })
  737. }
  738. /// Migrate [`WalletSqliteDatabase`]
  739. async fn migrate(conn: PooledResource<RM>) -> Result<(), Error> {
  740. let tx = ConnectionWithTransaction::new(conn).await?;
  741. migrate(&tx, RM::Connection::name(), migrations::MIGRATIONS).await?;
  742. // Update any existing keys with missing keyset_u32 values
  743. Self::add_keyset_u32(&tx).await?;
  744. tx.commit().await?;
  745. Ok(())
  746. }
  747. async fn add_keyset_u32<T>(conn: &T) -> Result<(), Error>
  748. where
  749. T: DatabaseExecutor,
  750. {
  751. // First get the keysets where keyset_u32 on key is null
  752. let keys_without_u32: Vec<Vec<Column>> = query(
  753. r#"
  754. SELECT
  755. id
  756. FROM key
  757. WHERE keyset_u32 IS NULL
  758. "#,
  759. )?
  760. .fetch_all(conn)
  761. .await?;
  762. for row in keys_without_u32 {
  763. unpack_into!(let (id) = row);
  764. let id = column_as_string!(id);
  765. if let Ok(id) = Id::from_str(&id) {
  766. query(
  767. r#"
  768. UPDATE
  769. key
  770. SET keyset_u32 = :u32_keyset
  771. WHERE id = :keyset_id
  772. "#,
  773. )?
  774. .bind("u32_keyset", u32::from(id))
  775. .bind("keyset_id", id.to_string())
  776. .execute(conn)
  777. .await?;
  778. }
  779. }
  780. // Also update keysets where keyset_u32 is null
  781. let keysets_without_u32: Vec<Vec<Column>> = query(
  782. r#"
  783. SELECT
  784. id
  785. FROM keyset
  786. WHERE keyset_u32 IS NULL
  787. "#,
  788. )?
  789. .fetch_all(conn)
  790. .await?;
  791. for row in keysets_without_u32 {
  792. unpack_into!(let (id) = row);
  793. let id = column_as_string!(id);
  794. if let Ok(id) = Id::from_str(&id) {
  795. query(
  796. r#"
  797. UPDATE
  798. keyset
  799. SET keyset_u32 = :u32_keyset
  800. WHERE id = :keyset_id
  801. "#,
  802. )?
  803. .bind("u32_keyset", u32::from(id))
  804. .bind("keyset_id", id.to_string())
  805. .execute(conn)
  806. .await?;
  807. }
  808. }
  809. Ok(())
  810. }
  811. }
  812. #[async_trait]
  813. impl<RM> WalletDatabase for SQLWalletDatabase<RM>
  814. where
  815. RM: DatabasePool + 'static,
  816. {
  817. type Err = database::Error;
  818. async fn begin_db_transaction(
  819. &self,
  820. ) -> Result<Box<dyn WalletDatabaseTransaction<Self::Err> + Send + Sync>, Self::Err> {
  821. Ok(Box::new(SQLWalletTransaction {
  822. inner: ConnectionWithTransaction::new(
  823. self.pool.get().map_err(|e| Error::Database(Box::new(e)))?,
  824. )
  825. .await?,
  826. }))
  827. }
  828. #[instrument(skip(self))]
  829. async fn get_melt_quotes(&self) -> Result<Vec<wallet::MeltQuote>, Self::Err> {
  830. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  831. Ok(query(
  832. r#"
  833. SELECT
  834. id,
  835. unit,
  836. amount,
  837. request,
  838. fee_reserve,
  839. state,
  840. expiry,
  841. payment_preimage,
  842. payment_method
  843. FROM
  844. melt_quote
  845. "#,
  846. )?
  847. .fetch_all(&*conn)
  848. .await?
  849. .into_iter()
  850. .map(sql_row_to_melt_quote)
  851. .collect::<Result<_, _>>()?)
  852. }
  853. #[instrument(skip(self))]
  854. async fn get_mint(&self, mint_url: MintUrl) -> Result<Option<MintInfo>, Self::Err> {
  855. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  856. Ok(query(
  857. r#"
  858. SELECT
  859. name,
  860. pubkey,
  861. version,
  862. description,
  863. description_long,
  864. contact,
  865. nuts,
  866. icon_url,
  867. motd,
  868. urls,
  869. mint_time,
  870. tos_url
  871. FROM
  872. mint
  873. WHERE mint_url = :mint_url
  874. "#,
  875. )?
  876. .bind("mint_url", mint_url.to_string())
  877. .fetch_one(&*conn)
  878. .await?
  879. .map(sql_row_to_mint_info)
  880. .transpose()?)
  881. }
  882. #[instrument(skip(self))]
  883. async fn get_mints(&self) -> Result<HashMap<MintUrl, Option<MintInfo>>, Self::Err> {
  884. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  885. Ok(query(
  886. r#"
  887. SELECT
  888. name,
  889. pubkey,
  890. version,
  891. description,
  892. description_long,
  893. contact,
  894. nuts,
  895. icon_url,
  896. motd,
  897. urls,
  898. mint_time,
  899. tos_url,
  900. mint_url
  901. FROM
  902. mint
  903. "#,
  904. )?
  905. .fetch_all(&*conn)
  906. .await?
  907. .into_iter()
  908. .map(|mut row| {
  909. let url = column_as_string!(
  910. row.pop().ok_or(ConversionError::MissingColumn(0, 1))?,
  911. MintUrl::from_str
  912. );
  913. Ok((url, sql_row_to_mint_info(row).ok()))
  914. })
  915. .collect::<Result<HashMap<_, _>, Error>>()?)
  916. }
  917. #[instrument(skip(self))]
  918. async fn get_mint_keysets(
  919. &self,
  920. mint_url: MintUrl,
  921. ) -> Result<Option<Vec<KeySetInfo>>, Self::Err> {
  922. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  923. let keysets = query(
  924. r#"
  925. SELECT
  926. id,
  927. unit,
  928. active,
  929. input_fee_ppk,
  930. final_expiry
  931. FROM
  932. keyset
  933. WHERE mint_url = :mint_url
  934. "#,
  935. )?
  936. .bind("mint_url", mint_url.to_string())
  937. .fetch_all(&*conn)
  938. .await?
  939. .into_iter()
  940. .map(sql_row_to_keyset)
  941. .collect::<Result<Vec<_>, Error>>()?;
  942. match keysets.is_empty() {
  943. false => Ok(Some(keysets)),
  944. true => Ok(None),
  945. }
  946. }
  947. #[instrument(skip(self), fields(keyset_id = %keyset_id))]
  948. async fn get_keyset_by_id(&self, keyset_id: &Id) -> Result<Option<KeySetInfo>, Self::Err> {
  949. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  950. get_keyset_by_id_inner(&*conn, keyset_id, false).await
  951. }
  952. #[instrument(skip(self))]
  953. async fn get_mint_quote(&self, quote_id: &str) -> Result<Option<MintQuote>, Self::Err> {
  954. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  955. get_mint_quote_inner(&*conn, quote_id, false).await
  956. }
  957. #[instrument(skip(self))]
  958. async fn get_mint_quotes(&self) -> Result<Vec<MintQuote>, Self::Err> {
  959. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  960. Ok(query(
  961. r#"
  962. SELECT
  963. id,
  964. mint_url,
  965. amount,
  966. unit,
  967. request,
  968. state,
  969. expiry,
  970. secret_key,
  971. payment_method,
  972. amount_issued,
  973. amount_paid
  974. FROM
  975. mint_quote
  976. "#,
  977. )?
  978. .fetch_all(&*conn)
  979. .await?
  980. .into_iter()
  981. .map(sql_row_to_mint_quote)
  982. .collect::<Result<_, _>>()?)
  983. }
  984. #[instrument(skip(self))]
  985. async fn get_unissued_mint_quotes(&self) -> Result<Vec<MintQuote>, Self::Err> {
  986. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  987. Ok(query(
  988. r#"
  989. SELECT
  990. id,
  991. mint_url,
  992. amount,
  993. unit,
  994. request,
  995. state,
  996. expiry,
  997. secret_key,
  998. payment_method,
  999. amount_issued,
  1000. amount_paid
  1001. FROM
  1002. mint_quote
  1003. WHERE
  1004. amount_issued = 0
  1005. OR
  1006. payment_method = 'bolt12'
  1007. "#,
  1008. )?
  1009. .fetch_all(&*conn)
  1010. .await?
  1011. .into_iter()
  1012. .map(sql_row_to_mint_quote)
  1013. .collect::<Result<_, _>>()?)
  1014. }
  1015. #[instrument(skip(self))]
  1016. async fn get_melt_quote(&self, quote_id: &str) -> Result<Option<wallet::MeltQuote>, Self::Err> {
  1017. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1018. get_melt_quote_inner(&*conn, quote_id, false).await
  1019. }
  1020. #[instrument(skip(self), fields(keyset_id = %keyset_id))]
  1021. async fn get_keys(&self, keyset_id: &Id) -> Result<Option<Keys>, Self::Err> {
  1022. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1023. get_keys_inner(&*conn, keyset_id).await
  1024. }
  1025. #[instrument(skip(self, state, spending_conditions))]
  1026. async fn get_proofs(
  1027. &self,
  1028. mint_url: Option<MintUrl>,
  1029. unit: Option<CurrencyUnit>,
  1030. state: Option<Vec<State>>,
  1031. spending_conditions: Option<Vec<SpendingConditions>>,
  1032. ) -> Result<Vec<ProofInfo>, Self::Err> {
  1033. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1034. get_proofs_inner(&*conn, mint_url, unit, state, spending_conditions, false).await
  1035. }
  1036. #[instrument(skip(self, ys))]
  1037. async fn get_proofs_by_ys(&self, ys: Vec<PublicKey>) -> Result<Vec<ProofInfo>, Self::Err> {
  1038. if ys.is_empty() {
  1039. return Ok(Vec::new());
  1040. }
  1041. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1042. Ok(query(
  1043. r#"
  1044. SELECT
  1045. amount,
  1046. unit,
  1047. keyset_id,
  1048. secret,
  1049. c,
  1050. witness,
  1051. dleq_e,
  1052. dleq_s,
  1053. dleq_r,
  1054. y,
  1055. mint_url,
  1056. state,
  1057. spending_condition
  1058. FROM proof
  1059. WHERE y IN (:ys)
  1060. "#,
  1061. )?
  1062. .bind_vec("ys", ys.iter().map(|y| y.to_bytes().to_vec()).collect())
  1063. .fetch_all(&*conn)
  1064. .await?
  1065. .into_iter()
  1066. .filter_map(|row| sql_row_to_proof_info(row).ok())
  1067. .collect::<Vec<_>>())
  1068. }
  1069. async fn get_balance(
  1070. &self,
  1071. mint_url: Option<MintUrl>,
  1072. unit: Option<CurrencyUnit>,
  1073. states: Option<Vec<State>>,
  1074. ) -> Result<u64, Self::Err> {
  1075. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1076. let mut query_str = "SELECT COALESCE(SUM(amount), 0) as total FROM proof".to_string();
  1077. let mut where_clauses = Vec::new();
  1078. let states = states
  1079. .unwrap_or_default()
  1080. .into_iter()
  1081. .map(|x| x.to_string())
  1082. .collect::<Vec<_>>();
  1083. if mint_url.is_some() {
  1084. where_clauses.push("mint_url = :mint_url");
  1085. }
  1086. if unit.is_some() {
  1087. where_clauses.push("unit = :unit");
  1088. }
  1089. if !states.is_empty() {
  1090. where_clauses.push("state IN (:states)");
  1091. }
  1092. if !where_clauses.is_empty() {
  1093. query_str.push_str(" WHERE ");
  1094. query_str.push_str(&where_clauses.join(" AND "));
  1095. }
  1096. let mut q = query(&query_str)?;
  1097. if let Some(ref mint_url) = mint_url {
  1098. q = q.bind("mint_url", mint_url.to_string());
  1099. }
  1100. if let Some(ref unit) = unit {
  1101. q = q.bind("unit", unit.to_string());
  1102. }
  1103. if !states.is_empty() {
  1104. q = q.bind_vec("states", states);
  1105. }
  1106. let balance = q
  1107. .pluck(&*conn)
  1108. .await?
  1109. .map(|n| {
  1110. // SQLite SUM returns INTEGER which we need to convert to u64
  1111. match n {
  1112. crate::stmt::Column::Integer(i) => Ok(i as u64),
  1113. crate::stmt::Column::Real(f) => Ok(f as u64),
  1114. _ => Err(Error::Database(Box::new(std::io::Error::new(
  1115. std::io::ErrorKind::InvalidData,
  1116. "Invalid balance type",
  1117. )))),
  1118. }
  1119. })
  1120. .transpose()?
  1121. .unwrap_or(0);
  1122. Ok(balance)
  1123. }
  1124. #[instrument(skip(self))]
  1125. async fn get_transaction(
  1126. &self,
  1127. transaction_id: TransactionId,
  1128. ) -> Result<Option<Transaction>, Self::Err> {
  1129. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1130. Ok(query(
  1131. r#"
  1132. SELECT
  1133. mint_url,
  1134. direction,
  1135. unit,
  1136. amount,
  1137. fee,
  1138. ys,
  1139. timestamp,
  1140. memo,
  1141. metadata,
  1142. quote_id,
  1143. payment_request,
  1144. payment_proof
  1145. FROM
  1146. transactions
  1147. WHERE
  1148. id = :id
  1149. "#,
  1150. )?
  1151. .bind("id", transaction_id.as_slice().to_vec())
  1152. .fetch_one(&*conn)
  1153. .await?
  1154. .map(sql_row_to_transaction)
  1155. .transpose()?)
  1156. }
  1157. #[instrument(skip(self))]
  1158. async fn list_transactions(
  1159. &self,
  1160. mint_url: Option<MintUrl>,
  1161. direction: Option<TransactionDirection>,
  1162. unit: Option<CurrencyUnit>,
  1163. ) -> Result<Vec<Transaction>, Self::Err> {
  1164. let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
  1165. Ok(query(
  1166. r#"
  1167. SELECT
  1168. mint_url,
  1169. direction,
  1170. unit,
  1171. amount,
  1172. fee,
  1173. ys,
  1174. timestamp,
  1175. memo,
  1176. metadata,
  1177. quote_id,
  1178. payment_request,
  1179. payment_proof
  1180. FROM
  1181. transactions
  1182. "#,
  1183. )?
  1184. .fetch_all(&*conn)
  1185. .await?
  1186. .into_iter()
  1187. .filter_map(|row| {
  1188. // TODO: Avoid a table scan by passing the heavy lifting of checking to the DB engine
  1189. let transaction = sql_row_to_transaction(row).ok()?;
  1190. if transaction.matches_conditions(&mint_url, &direction, &unit) {
  1191. Some(transaction)
  1192. } else {
  1193. None
  1194. }
  1195. })
  1196. .collect::<Vec<_>>())
  1197. }
  1198. }
  1199. fn sql_row_to_mint_info(row: Vec<Column>) -> Result<MintInfo, Error> {
  1200. unpack_into!(
  1201. let (
  1202. name,
  1203. pubkey,
  1204. version,
  1205. description,
  1206. description_long,
  1207. contact,
  1208. nuts,
  1209. icon_url,
  1210. motd,
  1211. urls,
  1212. mint_time,
  1213. tos_url
  1214. ) = row
  1215. );
  1216. Ok(MintInfo {
  1217. name: column_as_nullable_string!(&name),
  1218. pubkey: column_as_nullable_string!(&pubkey, |v| serde_json::from_str(v).ok(), |v| {
  1219. serde_json::from_slice(v).ok()
  1220. }),
  1221. version: column_as_nullable_string!(&version).and_then(|v| serde_json::from_str(&v).ok()),
  1222. description: column_as_nullable_string!(description),
  1223. description_long: column_as_nullable_string!(description_long),
  1224. contact: column_as_nullable_string!(contact, |v| serde_json::from_str(&v).ok()),
  1225. nuts: column_as_nullable_string!(nuts, |v| serde_json::from_str(&v).ok())
  1226. .unwrap_or_default(),
  1227. urls: column_as_nullable_string!(urls, |v| serde_json::from_str(&v).ok()),
  1228. icon_url: column_as_nullable_string!(icon_url),
  1229. motd: column_as_nullable_string!(motd),
  1230. time: column_as_nullable_number!(mint_time).map(|t| t),
  1231. tos_url: column_as_nullable_string!(tos_url),
  1232. })
  1233. }
  1234. #[instrument(skip_all)]
  1235. fn sql_row_to_keyset(row: Vec<Column>) -> Result<KeySetInfo, Error> {
  1236. unpack_into!(
  1237. let (
  1238. id,
  1239. unit,
  1240. active,
  1241. input_fee_ppk,
  1242. final_expiry
  1243. ) = row
  1244. );
  1245. Ok(KeySetInfo {
  1246. id: column_as_string!(id, Id::from_str, Id::from_bytes),
  1247. unit: column_as_string!(unit, CurrencyUnit::from_str),
  1248. active: matches!(active, Column::Integer(1)),
  1249. input_fee_ppk: column_as_nullable_number!(input_fee_ppk).unwrap_or_default(),
  1250. final_expiry: column_as_nullable_number!(final_expiry),
  1251. })
  1252. }
  1253. fn sql_row_to_mint_quote(row: Vec<Column>) -> Result<MintQuote, Error> {
  1254. unpack_into!(
  1255. let (
  1256. id,
  1257. mint_url,
  1258. amount,
  1259. unit,
  1260. request,
  1261. state,
  1262. expiry,
  1263. secret_key,
  1264. row_method,
  1265. row_amount_minted,
  1266. row_amount_paid
  1267. ) = row
  1268. );
  1269. let amount: Option<i64> = column_as_nullable_number!(amount);
  1270. let amount_paid: u64 = column_as_number!(row_amount_paid);
  1271. let amount_minted: u64 = column_as_number!(row_amount_minted);
  1272. let payment_method =
  1273. PaymentMethod::from_str(&column_as_string!(row_method)).map_err(Error::from)?;
  1274. Ok(MintQuote {
  1275. id: column_as_string!(id),
  1276. mint_url: column_as_string!(mint_url, MintUrl::from_str),
  1277. amount: amount.and_then(Amount::from_i64),
  1278. unit: column_as_string!(unit, CurrencyUnit::from_str),
  1279. request: column_as_string!(request),
  1280. state: column_as_string!(state, MintQuoteState::from_str),
  1281. expiry: column_as_number!(expiry),
  1282. secret_key: column_as_nullable_string!(secret_key)
  1283. .map(|v| SecretKey::from_str(&v))
  1284. .transpose()?,
  1285. payment_method,
  1286. amount_issued: amount_minted.into(),
  1287. amount_paid: amount_paid.into(),
  1288. })
  1289. }
  1290. fn sql_row_to_melt_quote(row: Vec<Column>) -> Result<wallet::MeltQuote, Error> {
  1291. unpack_into!(
  1292. let (
  1293. id,
  1294. unit,
  1295. amount,
  1296. request,
  1297. fee_reserve,
  1298. state,
  1299. expiry,
  1300. payment_preimage,
  1301. row_method
  1302. ) = row
  1303. );
  1304. let amount: u64 = column_as_number!(amount);
  1305. let fee_reserve: u64 = column_as_number!(fee_reserve);
  1306. let payment_method =
  1307. PaymentMethod::from_str(&column_as_string!(row_method)).map_err(Error::from)?;
  1308. Ok(wallet::MeltQuote {
  1309. id: column_as_string!(id),
  1310. amount: Amount::from(amount),
  1311. unit: column_as_string!(unit, CurrencyUnit::from_str),
  1312. request: column_as_string!(request),
  1313. fee_reserve: Amount::from(fee_reserve),
  1314. state: column_as_string!(state, MeltQuoteState::from_str),
  1315. expiry: column_as_number!(expiry),
  1316. payment_preimage: column_as_nullable_string!(payment_preimage),
  1317. payment_method,
  1318. })
  1319. }
  1320. fn sql_row_to_proof_info(row: Vec<Column>) -> Result<ProofInfo, Error> {
  1321. unpack_into!(
  1322. let (
  1323. amount,
  1324. unit,
  1325. keyset_id,
  1326. secret,
  1327. c,
  1328. witness,
  1329. dleq_e,
  1330. dleq_s,
  1331. dleq_r,
  1332. y,
  1333. mint_url,
  1334. state,
  1335. spending_condition
  1336. ) = row
  1337. );
  1338. let dleq = match (
  1339. column_as_nullable_binary!(dleq_e),
  1340. column_as_nullable_binary!(dleq_s),
  1341. column_as_nullable_binary!(dleq_r),
  1342. ) {
  1343. (Some(e), Some(s), Some(r)) => {
  1344. let e_key = SecretKey::from_slice(&e)?;
  1345. let s_key = SecretKey::from_slice(&s)?;
  1346. let r_key = SecretKey::from_slice(&r)?;
  1347. Some(ProofDleq::new(e_key, s_key, r_key))
  1348. }
  1349. _ => None,
  1350. };
  1351. let amount: u64 = column_as_number!(amount);
  1352. let proof = Proof {
  1353. amount: Amount::from(amount),
  1354. keyset_id: column_as_string!(keyset_id, Id::from_str),
  1355. secret: column_as_string!(secret, Secret::from_str),
  1356. witness: column_as_nullable_string!(witness, |v| { serde_json::from_str(&v).ok() }, |v| {
  1357. serde_json::from_slice(&v).ok()
  1358. }),
  1359. c: column_as_string!(c, PublicKey::from_str, PublicKey::from_slice),
  1360. dleq,
  1361. };
  1362. Ok(ProofInfo {
  1363. proof,
  1364. y: column_as_string!(y, PublicKey::from_str, PublicKey::from_slice),
  1365. mint_url: column_as_string!(mint_url, MintUrl::from_str),
  1366. state: column_as_string!(state, State::from_str),
  1367. spending_condition: column_as_nullable_string!(
  1368. spending_condition,
  1369. |r| { serde_json::from_str(&r).ok() },
  1370. |r| { serde_json::from_slice(&r).ok() }
  1371. ),
  1372. unit: column_as_string!(unit, CurrencyUnit::from_str),
  1373. })
  1374. }
  1375. fn sql_row_to_transaction(row: Vec<Column>) -> Result<Transaction, Error> {
  1376. unpack_into!(
  1377. let (
  1378. mint_url,
  1379. direction,
  1380. unit,
  1381. amount,
  1382. fee,
  1383. ys,
  1384. timestamp,
  1385. memo,
  1386. metadata,
  1387. quote_id,
  1388. payment_request,
  1389. payment_proof
  1390. ) = row
  1391. );
  1392. let amount: u64 = column_as_number!(amount);
  1393. let fee: u64 = column_as_number!(fee);
  1394. Ok(Transaction {
  1395. mint_url: column_as_string!(mint_url, MintUrl::from_str),
  1396. direction: column_as_string!(direction, TransactionDirection::from_str),
  1397. unit: column_as_string!(unit, CurrencyUnit::from_str),
  1398. amount: Amount::from(amount),
  1399. fee: Amount::from(fee),
  1400. ys: column_as_binary!(ys)
  1401. .chunks(33)
  1402. .map(PublicKey::from_slice)
  1403. .collect::<Result<Vec<_>, _>>()?,
  1404. timestamp: column_as_number!(timestamp),
  1405. memo: column_as_nullable_string!(memo),
  1406. metadata: column_as_nullable_string!(metadata, |v| serde_json::from_str(&v).ok(), |v| {
  1407. serde_json::from_slice(&v).ok()
  1408. })
  1409. .unwrap_or_default(),
  1410. quote_id: column_as_nullable_string!(quote_id),
  1411. payment_request: column_as_nullable_string!(payment_request),
  1412. payment_proof: column_as_nullable_string!(payment_proof),
  1413. })
  1414. }