mod.rs 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139
  1. //! SQLite Storage for CDK
  2. use std::cmp::Ordering;
  3. use std::collections::{HashMap, HashSet};
  4. use std::path::Path;
  5. use std::str::FromStr;
  6. use std::sync::Arc;
  7. use async_trait::async_trait;
  8. use cdk_common::common::{PaymentProcessorKey, QuoteTTL};
  9. use cdk_common::database::{
  10. self, MintDatabase, MintKeysDatabase, MintProofsDatabase, MintQuotesDatabase,
  11. MintSignaturesDatabase,
  12. };
  13. use cdk_common::dhke::hash_to_curve;
  14. use cdk_common::mint::{self, MintKeySetInfo, MintQuote};
  15. use cdk_common::nut00::ProofsMethods;
  16. use cdk_common::util::unix_time;
  17. use cdk_common::{
  18. BlindSignature, CurrencyUnit, Id, MeltBolt11Request, MeltQuoteState, MintInfo, MintQuoteState,
  19. Proof, Proofs, PublicKey, State,
  20. };
  21. use migrations::{migrate_01_to_02, migrate_04_to_05};
  22. use redb::{Database, MultimapTableDefinition, ReadableTable, TableDefinition};
  23. use uuid::Uuid;
  24. use super::error::Error;
  25. use crate::migrations::migrate_00_to_01;
  26. use crate::mint::migrations::{migrate_02_to_03, migrate_03_to_04};
  27. #[cfg(feature = "auth")]
  28. mod auth;
  29. mod migrations;
  30. #[cfg(feature = "auth")]
  31. pub use auth::MintRedbAuthDatabase;
  32. const ACTIVE_KEYSETS_TABLE: TableDefinition<&str, &str> = TableDefinition::new("active_keysets");
  33. const KEYSETS_TABLE: TableDefinition<&str, &str> = TableDefinition::new("keysets");
  34. const MINT_QUOTES_TABLE: TableDefinition<[u8; 16], &str> = TableDefinition::new("mint_quotes");
  35. const MELT_QUOTES_TABLE: TableDefinition<[u8; 16], &str> = TableDefinition::new("melt_quotes");
  36. const PROOFS_TABLE: TableDefinition<[u8; 33], &str> = TableDefinition::new("proofs");
  37. const PROOFS_STATE_TABLE: TableDefinition<[u8; 33], &str> = TableDefinition::new("proofs_state");
  38. const PROOF_CREATED_TIME: TableDefinition<[u8; 33], u64> =
  39. TableDefinition::new("proof_created_time");
  40. const CONFIG_TABLE: TableDefinition<&str, &str> = TableDefinition::new("config");
  41. // Key is hex blinded_message B_ value is blinded_signature
  42. const BLINDED_SIGNATURES: TableDefinition<[u8; 33], &str> =
  43. TableDefinition::new("blinded_signatures");
  44. const BLIND_SIGNATURE_CREATED_TIME: TableDefinition<[u8; 33], u64> =
  45. TableDefinition::new("blind_signature_created_time");
  46. const QUOTE_PROOFS_TABLE: MultimapTableDefinition<[u8; 16], [u8; 33]> =
  47. MultimapTableDefinition::new("quote_proofs");
  48. const QUOTE_SIGNATURES_TABLE: MultimapTableDefinition<[u8; 16], [u8; 33]> =
  49. MultimapTableDefinition::new("quote_signatures");
  50. const MELT_REQUESTS: TableDefinition<[u8; 16], (&str, &str)> =
  51. TableDefinition::new("melt_requests");
  52. const DATABASE_VERSION: u32 = 5;
  53. /// Mint Redbdatabase
  54. #[derive(Debug, Clone)]
  55. pub struct MintRedbDatabase {
  56. db: Arc<Database>,
  57. }
  58. impl MintRedbDatabase {
  59. /// Create new [`MintRedbDatabase`]
  60. pub fn new(path: &Path) -> Result<Self, Error> {
  61. {
  62. // Check database version
  63. let db = Arc::new(Database::create(path)?);
  64. // Check database version
  65. let read_txn = db.begin_read()?;
  66. let table = read_txn.open_table(CONFIG_TABLE);
  67. let db_version = match table {
  68. Ok(table) => table.get("db_version")?.map(|v| v.value().to_owned()),
  69. Err(_) => None,
  70. };
  71. match db_version {
  72. Some(db_version) => {
  73. let mut current_file_version = u32::from_str(&db_version)?;
  74. match current_file_version.cmp(&DATABASE_VERSION) {
  75. Ordering::Less => {
  76. tracing::info!(
  77. "Database needs to be upgraded at {} current is {}",
  78. current_file_version,
  79. DATABASE_VERSION
  80. );
  81. if current_file_version == 0 {
  82. current_file_version = migrate_00_to_01(Arc::clone(&db))?;
  83. }
  84. if current_file_version == 1 {
  85. current_file_version = migrate_01_to_02(Arc::clone(&db))?;
  86. }
  87. if current_file_version == 2 {
  88. current_file_version = migrate_02_to_03(Arc::clone(&db))?;
  89. }
  90. if current_file_version == 3 {
  91. current_file_version = migrate_03_to_04(Arc::clone(&db))?;
  92. }
  93. if current_file_version == 4 {
  94. current_file_version = migrate_04_to_05(Arc::clone(&db))?;
  95. }
  96. if current_file_version != DATABASE_VERSION {
  97. tracing::warn!(
  98. "Database upgrade did not complete at {} current is {}",
  99. current_file_version,
  100. DATABASE_VERSION
  101. );
  102. return Err(Error::UnknownDatabaseVersion);
  103. }
  104. let write_txn = db.begin_write()?;
  105. {
  106. let mut table = write_txn.open_table(CONFIG_TABLE)?;
  107. table
  108. .insert("db_version", DATABASE_VERSION.to_string().as_str())?;
  109. }
  110. write_txn.commit()?;
  111. }
  112. Ordering::Equal => {
  113. tracing::info!("Database is at current version {}", DATABASE_VERSION);
  114. }
  115. Ordering::Greater => {
  116. tracing::warn!(
  117. "Database upgrade did not complete at {} current is {}",
  118. current_file_version,
  119. DATABASE_VERSION
  120. );
  121. return Err(Error::UnknownDatabaseVersion);
  122. }
  123. }
  124. }
  125. None => {
  126. let write_txn = db.begin_write()?;
  127. {
  128. // Open all tables to init a new db
  129. let mut table = write_txn.open_table(CONFIG_TABLE)?;
  130. let _ = write_txn.open_table(ACTIVE_KEYSETS_TABLE)?;
  131. let _ = write_txn.open_table(KEYSETS_TABLE)?;
  132. let _ = write_txn.open_table(MINT_QUOTES_TABLE)?;
  133. let _ = write_txn.open_table(MELT_QUOTES_TABLE)?;
  134. let _ = write_txn.open_table(PROOFS_TABLE)?;
  135. let _ = write_txn.open_table(PROOFS_STATE_TABLE)?;
  136. let _ = write_txn.open_table(PROOF_CREATED_TIME)?;
  137. let _ = write_txn.open_table(BLINDED_SIGNATURES)?;
  138. let _ = write_txn.open_table(BLIND_SIGNATURE_CREATED_TIME)?;
  139. let _ = write_txn.open_multimap_table(QUOTE_PROOFS_TABLE)?;
  140. let _ = write_txn.open_multimap_table(QUOTE_SIGNATURES_TABLE)?;
  141. table.insert("db_version", DATABASE_VERSION.to_string().as_str())?;
  142. }
  143. write_txn.commit()?;
  144. }
  145. }
  146. drop(db);
  147. }
  148. let db = Database::create(path)?;
  149. Ok(Self { db: Arc::new(db) })
  150. }
  151. }
  152. #[async_trait]
  153. impl MintKeysDatabase for MintRedbDatabase {
  154. type Err = database::Error;
  155. async fn set_active_keyset(&self, unit: CurrencyUnit, id: Id) -> Result<(), Self::Err> {
  156. let write_txn = self.db.begin_write().map_err(Error::from)?;
  157. {
  158. let mut table = write_txn
  159. .open_table(ACTIVE_KEYSETS_TABLE)
  160. .map_err(Error::from)?;
  161. table
  162. .insert(unit.to_string().as_str(), id.to_string().as_str())
  163. .map_err(Error::from)?;
  164. }
  165. write_txn.commit().map_err(Error::from)?;
  166. Ok(())
  167. }
  168. async fn get_active_keyset_id(&self, unit: &CurrencyUnit) -> Result<Option<Id>, Self::Err> {
  169. let read_txn = self.db.begin_read().map_err(Error::from)?;
  170. let table = read_txn
  171. .open_table(ACTIVE_KEYSETS_TABLE)
  172. .map_err(Error::from)?;
  173. if let Some(id) = table.get(unit.to_string().as_str()).map_err(Error::from)? {
  174. return Ok(Some(Id::from_str(id.value()).map_err(Error::from)?));
  175. }
  176. Ok(None)
  177. }
  178. async fn get_active_keysets(&self) -> Result<HashMap<CurrencyUnit, Id>, Self::Err> {
  179. let read_txn = self.db.begin_read().map_err(Error::from)?;
  180. let table = read_txn
  181. .open_table(ACTIVE_KEYSETS_TABLE)
  182. .map_err(Error::from)?;
  183. let mut active_keysets = HashMap::new();
  184. for (unit, id) in (table.iter().map_err(Error::from)?).flatten() {
  185. let unit = CurrencyUnit::from_str(unit.value())?;
  186. let id = Id::from_str(id.value()).map_err(Error::from)?;
  187. active_keysets.insert(unit, id);
  188. }
  189. Ok(active_keysets)
  190. }
  191. async fn add_keyset_info(&self, keyset: MintKeySetInfo) -> Result<(), Self::Err> {
  192. let write_txn = self.db.begin_write().map_err(Error::from)?;
  193. {
  194. let mut table = write_txn.open_table(KEYSETS_TABLE).map_err(Error::from)?;
  195. table
  196. .insert(
  197. keyset.id.to_string().as_str(),
  198. serde_json::to_string(&keyset)
  199. .map_err(Error::from)?
  200. .as_str(),
  201. )
  202. .map_err(Error::from)?;
  203. }
  204. write_txn.commit().map_err(Error::from)?;
  205. Ok(())
  206. }
  207. async fn get_keyset_info(&self, keyset_id: &Id) -> Result<Option<MintKeySetInfo>, Self::Err> {
  208. let read_txn = self.db.begin_read().map_err(Error::from)?;
  209. let table = read_txn.open_table(KEYSETS_TABLE).map_err(Error::from)?;
  210. match table
  211. .get(keyset_id.to_string().as_str())
  212. .map_err(Error::from)?
  213. {
  214. Some(keyset) => Ok(serde_json::from_str(keyset.value()).map_err(Error::from)?),
  215. None => Ok(None),
  216. }
  217. }
  218. async fn get_keyset_infos(&self) -> Result<Vec<MintKeySetInfo>, Self::Err> {
  219. let read_txn = self.db.begin_read().map_err(Error::from)?;
  220. let table = read_txn.open_table(KEYSETS_TABLE).map_err(Error::from)?;
  221. let mut keysets = Vec::new();
  222. for (_id, keyset) in (table.iter().map_err(Error::from)?).flatten() {
  223. let keyset = serde_json::from_str(keyset.value()).map_err(Error::from)?;
  224. keysets.push(keyset)
  225. }
  226. Ok(keysets)
  227. }
  228. }
  229. #[async_trait]
  230. impl MintQuotesDatabase for MintRedbDatabase {
  231. type Err = database::Error;
  232. async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), Self::Err> {
  233. let write_txn = self.db.begin_write().map_err(Error::from)?;
  234. {
  235. let mut table = write_txn
  236. .open_table(MINT_QUOTES_TABLE)
  237. .map_err(Error::from)?;
  238. table
  239. .insert(
  240. quote.id.as_bytes(),
  241. serde_json::to_string(&quote).map_err(Error::from)?.as_str(),
  242. )
  243. .map_err(Error::from)?;
  244. }
  245. write_txn.commit().map_err(Error::from)?;
  246. Ok(())
  247. }
  248. async fn get_mint_quote(&self, quote_id: &Uuid) -> Result<Option<MintQuote>, Self::Err> {
  249. let read_txn = self.db.begin_read().map_err(Error::from)?;
  250. let table = read_txn
  251. .open_table(MINT_QUOTES_TABLE)
  252. .map_err(Error::from)?;
  253. match table.get(quote_id.as_bytes()).map_err(Error::from)? {
  254. Some(quote) => Ok(serde_json::from_str(quote.value()).map_err(Error::from)?),
  255. None => Ok(None),
  256. }
  257. }
  258. async fn update_mint_quote_state(
  259. &self,
  260. quote_id: &Uuid,
  261. state: MintQuoteState,
  262. ) -> Result<MintQuoteState, Self::Err> {
  263. let write_txn = self.db.begin_write().map_err(Error::from)?;
  264. let current_state;
  265. {
  266. let mut mint_quote: MintQuote;
  267. let mut table = write_txn
  268. .open_table(MINT_QUOTES_TABLE)
  269. .map_err(Error::from)?;
  270. {
  271. let quote_guard = table
  272. .get(quote_id.as_bytes())
  273. .map_err(Error::from)?
  274. .ok_or(Error::UnknownQuote)?;
  275. let quote = quote_guard.value();
  276. mint_quote = serde_json::from_str(quote).map_err(Error::from)?;
  277. }
  278. current_state = mint_quote.state;
  279. mint_quote.state = state;
  280. {
  281. table
  282. .insert(
  283. quote_id.as_bytes(),
  284. serde_json::to_string(&mint_quote)
  285. .map_err(Error::from)?
  286. .as_str(),
  287. )
  288. .map_err(Error::from)?;
  289. }
  290. }
  291. write_txn.commit().map_err(Error::from)?;
  292. Ok(current_state)
  293. }
  294. async fn get_mint_quote_by_request(
  295. &self,
  296. request: &str,
  297. ) -> Result<Option<MintQuote>, Self::Err> {
  298. let quotes = self.get_mint_quotes().await?;
  299. let quote = quotes
  300. .into_iter()
  301. .filter(|q| q.request.eq(request))
  302. .collect::<Vec<MintQuote>>()
  303. .first()
  304. .cloned();
  305. Ok(quote)
  306. }
  307. async fn get_mint_quote_by_request_lookup_id(
  308. &self,
  309. request_lookup_id: &str,
  310. ) -> Result<Option<MintQuote>, Self::Err> {
  311. let quotes = self.get_mint_quotes().await?;
  312. let quote = quotes
  313. .into_iter()
  314. .filter(|q| q.request_lookup_id.eq(request_lookup_id))
  315. .collect::<Vec<MintQuote>>()
  316. .first()
  317. .cloned();
  318. Ok(quote)
  319. }
  320. async fn get_mint_quotes(&self) -> Result<Vec<MintQuote>, Self::Err> {
  321. let read_txn = self.db.begin_read().map_err(Error::from)?;
  322. let table = read_txn
  323. .open_table(MINT_QUOTES_TABLE)
  324. .map_err(Error::from)?;
  325. let mut quotes = Vec::new();
  326. for (_id, quote) in (table.iter().map_err(Error::from)?).flatten() {
  327. let quote = serde_json::from_str(quote.value()).map_err(Error::from)?;
  328. quotes.push(quote)
  329. }
  330. Ok(quotes)
  331. }
  332. async fn get_mint_quotes_with_state(
  333. &self,
  334. state: MintQuoteState,
  335. ) -> Result<Vec<MintQuote>, Self::Err> {
  336. let read_txn = self.db.begin_read().map_err(Error::from)?;
  337. let table = read_txn
  338. .open_table(MINT_QUOTES_TABLE)
  339. .map_err(Error::from)?;
  340. let mut quotes = Vec::new();
  341. for (_id, quote) in (table.iter().map_err(Error::from)?).flatten() {
  342. let quote: MintQuote = serde_json::from_str(quote.value()).map_err(Error::from)?;
  343. if quote.state == state {
  344. quotes.push(quote)
  345. }
  346. }
  347. Ok(quotes)
  348. }
  349. async fn remove_mint_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err> {
  350. let write_txn = self.db.begin_write().map_err(Error::from)?;
  351. {
  352. let mut table = write_txn
  353. .open_table(MINT_QUOTES_TABLE)
  354. .map_err(Error::from)?;
  355. table.remove(quote_id.as_bytes()).map_err(Error::from)?;
  356. }
  357. write_txn.commit().map_err(Error::from)?;
  358. Ok(())
  359. }
  360. async fn add_melt_quote(&self, quote: mint::MeltQuote) -> Result<(), Self::Err> {
  361. let write_txn = self.db.begin_write().map_err(Error::from)?;
  362. {
  363. let mut table = write_txn
  364. .open_table(MELT_QUOTES_TABLE)
  365. .map_err(Error::from)?;
  366. table
  367. .insert(
  368. quote.id.as_bytes(),
  369. serde_json::to_string(&quote).map_err(Error::from)?.as_str(),
  370. )
  371. .map_err(Error::from)?;
  372. }
  373. write_txn.commit().map_err(Error::from)?;
  374. Ok(())
  375. }
  376. async fn get_melt_quote(&self, quote_id: &Uuid) -> Result<Option<mint::MeltQuote>, Self::Err> {
  377. let read_txn = self.db.begin_read().map_err(Error::from)?;
  378. let table = read_txn
  379. .open_table(MELT_QUOTES_TABLE)
  380. .map_err(Error::from)?;
  381. let quote = table.get(quote_id.as_bytes()).map_err(Error::from)?;
  382. Ok(quote.map(|q| serde_json::from_str(q.value()).unwrap()))
  383. }
  384. async fn update_melt_quote_state(
  385. &self,
  386. quote_id: &Uuid,
  387. state: MeltQuoteState,
  388. ) -> Result<MeltQuoteState, Self::Err> {
  389. let write_txn = self.db.begin_write().map_err(Error::from)?;
  390. let current_state;
  391. {
  392. let mut melt_quote: mint::MeltQuote;
  393. let mut table = write_txn
  394. .open_table(MELT_QUOTES_TABLE)
  395. .map_err(Error::from)?;
  396. {
  397. let quote_guard = table
  398. .get(quote_id.as_bytes())
  399. .map_err(Error::from)?
  400. .ok_or(Error::UnknownQuote)?;
  401. let quote = quote_guard.value();
  402. melt_quote = serde_json::from_str(quote).map_err(Error::from)?;
  403. }
  404. current_state = melt_quote.state;
  405. melt_quote.state = state;
  406. {
  407. table
  408. .insert(
  409. quote_id.as_bytes(),
  410. serde_json::to_string(&melt_quote)
  411. .map_err(Error::from)?
  412. .as_str(),
  413. )
  414. .map_err(Error::from)?;
  415. }
  416. }
  417. write_txn.commit().map_err(Error::from)?;
  418. Ok(current_state)
  419. }
  420. async fn get_melt_quotes(&self) -> Result<Vec<mint::MeltQuote>, Self::Err> {
  421. let read_txn = self.db.begin_read().map_err(Error::from)?;
  422. let table = read_txn
  423. .open_table(MELT_QUOTES_TABLE)
  424. .map_err(Error::from)?;
  425. let mut quotes = Vec::new();
  426. for (_id, quote) in (table.iter().map_err(Error::from)?).flatten() {
  427. let quote = serde_json::from_str(quote.value()).map_err(Error::from)?;
  428. quotes.push(quote)
  429. }
  430. Ok(quotes)
  431. }
  432. async fn remove_melt_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err> {
  433. let write_txn = self.db.begin_write().map_err(Error::from)?;
  434. {
  435. let mut table = write_txn
  436. .open_table(MELT_QUOTES_TABLE)
  437. .map_err(Error::from)?;
  438. table.remove(quote_id.as_bytes()).map_err(Error::from)?;
  439. }
  440. write_txn.commit().map_err(Error::from)?;
  441. Ok(())
  442. }
  443. /// Add melt request
  444. async fn add_melt_request(
  445. &self,
  446. melt_request: MeltBolt11Request<Uuid>,
  447. ln_key: PaymentProcessorKey,
  448. ) -> Result<(), Self::Err> {
  449. let write_txn = self.db.begin_write().map_err(Error::from)?;
  450. let mut table = write_txn.open_table(MELT_REQUESTS).map_err(Error::from)?;
  451. table
  452. .insert(
  453. melt_request.quote().as_bytes(),
  454. (
  455. serde_json::to_string(&melt_request)?.as_str(),
  456. serde_json::to_string(&ln_key)?.as_str(),
  457. ),
  458. )
  459. .map_err(Error::from)?;
  460. Ok(())
  461. }
  462. /// Get melt request
  463. async fn get_melt_request(
  464. &self,
  465. quote_id: &Uuid,
  466. ) -> Result<Option<(MeltBolt11Request<Uuid>, PaymentProcessorKey)>, Self::Err> {
  467. let read_txn = self.db.begin_read().map_err(Error::from)?;
  468. let table = read_txn.open_table(MELT_REQUESTS).map_err(Error::from)?;
  469. match table.get(quote_id.as_bytes()).map_err(Error::from)? {
  470. Some(melt_request) => {
  471. let (melt_request_str, ln_key_str) = melt_request.value();
  472. let melt_request = serde_json::from_str(melt_request_str)?;
  473. let ln_key = serde_json::from_str(ln_key_str)?;
  474. Ok(Some((melt_request, ln_key)))
  475. }
  476. None => Ok(None),
  477. }
  478. }
  479. }
  480. #[async_trait]
  481. impl MintProofsDatabase for MintRedbDatabase {
  482. type Err = database::Error;
  483. async fn add_proofs(&self, proofs: Proofs, quote_id: Option<Uuid>) -> Result<(), Self::Err> {
  484. let write_txn = self.db.begin_write().map_err(Error::from)?;
  485. {
  486. let mut table = write_txn.open_table(PROOFS_TABLE).map_err(Error::from)?;
  487. let mut time_table = write_txn
  488. .open_table(PROOF_CREATED_TIME)
  489. .map_err(Error::from)?;
  490. let mut quote_proofs_table = write_txn
  491. .open_multimap_table(QUOTE_PROOFS_TABLE)
  492. .map_err(Error::from)?;
  493. // Get current timestamp in seconds
  494. let current_time = unix_time();
  495. for proof in proofs {
  496. let y: PublicKey = hash_to_curve(&proof.secret.to_bytes()).map_err(Error::from)?;
  497. let y = y.to_bytes();
  498. if table.get(y).map_err(Error::from)?.is_none() {
  499. table
  500. .insert(
  501. y,
  502. serde_json::to_string(&proof).map_err(Error::from)?.as_str(),
  503. )
  504. .map_err(Error::from)?;
  505. // Store creation time
  506. time_table.insert(y, current_time).map_err(Error::from)?;
  507. }
  508. if let Some(quote_id) = &quote_id {
  509. quote_proofs_table
  510. .insert(quote_id.as_bytes(), y)
  511. .map_err(Error::from)?;
  512. }
  513. }
  514. }
  515. write_txn.commit().map_err(Error::from)?;
  516. Ok(())
  517. }
  518. async fn remove_proofs(
  519. &self,
  520. ys: &[PublicKey],
  521. quote_id: Option<Uuid>,
  522. ) -> Result<(), Self::Err> {
  523. let write_txn = self.db.begin_write().map_err(Error::from)?;
  524. let mut states: HashSet<State> = HashSet::new();
  525. {
  526. let mut proof_state_table = write_txn
  527. .open_table(PROOFS_STATE_TABLE)
  528. .map_err(Error::from)?;
  529. for y in ys {
  530. let state = proof_state_table
  531. .remove(&y.to_bytes())
  532. .map_err(Error::from)?;
  533. if let Some(state) = state {
  534. let state: State = serde_json::from_str(state.value()).map_err(Error::from)?;
  535. states.insert(state);
  536. }
  537. }
  538. }
  539. if states.contains(&State::Spent) {
  540. tracing::warn!("Db attempted to remove spent proof");
  541. write_txn.abort().map_err(Error::from)?;
  542. return Err(Self::Err::AttemptRemoveSpentProof);
  543. }
  544. {
  545. let mut proofs_table = write_txn.open_table(PROOFS_TABLE).map_err(Error::from)?;
  546. let mut time_table = write_txn
  547. .open_table(PROOF_CREATED_TIME)
  548. .map_err(Error::from)?;
  549. for y in ys {
  550. proofs_table.remove(&y.to_bytes()).map_err(Error::from)?;
  551. time_table.remove(&y.to_bytes()).map_err(Error::from)?;
  552. }
  553. }
  554. if let Some(quote_id) = quote_id {
  555. let mut quote_proofs_table = write_txn
  556. .open_multimap_table(QUOTE_PROOFS_TABLE)
  557. .map_err(Error::from)?;
  558. quote_proofs_table
  559. .remove_all(quote_id.as_bytes())
  560. .map_err(Error::from)?;
  561. }
  562. write_txn.commit().map_err(Error::from)?;
  563. Ok(())
  564. }
  565. async fn get_proofs_by_ys(&self, ys: &[PublicKey]) -> Result<Vec<Option<Proof>>, Self::Err> {
  566. let read_txn = self.db.begin_read().map_err(Error::from)?;
  567. let table = read_txn.open_table(PROOFS_TABLE).map_err(Error::from)?;
  568. let mut proofs = Vec::with_capacity(ys.len());
  569. for y in ys {
  570. match table.get(y.to_bytes()).map_err(Error::from)? {
  571. Some(proof) => proofs.push(Some(
  572. serde_json::from_str(proof.value()).map_err(Error::from)?,
  573. )),
  574. None => proofs.push(None),
  575. }
  576. }
  577. Ok(proofs)
  578. }
  579. async fn get_proof_ys_by_quote_id(&self, quote_id: &Uuid) -> Result<Vec<PublicKey>, Self::Err> {
  580. let read_txn = self.db.begin_read().map_err(Error::from)?;
  581. let table = read_txn
  582. .open_multimap_table(QUOTE_PROOFS_TABLE)
  583. .map_err(Error::from)?;
  584. let ys = table.get(quote_id.as_bytes()).map_err(Error::from)?;
  585. let proof_ys = ys.fold(Vec::new(), |mut acc, y| {
  586. if let Ok(y) = y {
  587. if let Ok(pubkey) = PublicKey::from_slice(&y.value()) {
  588. acc.push(pubkey);
  589. }
  590. }
  591. acc
  592. });
  593. Ok(proof_ys)
  594. }
  595. async fn get_proofs_states(&self, ys: &[PublicKey]) -> Result<Vec<Option<State>>, Self::Err> {
  596. let read_txn = self.db.begin_read().map_err(Error::from)?;
  597. let table = read_txn
  598. .open_table(PROOFS_STATE_TABLE)
  599. .map_err(Error::from)?;
  600. let mut states = Vec::with_capacity(ys.len());
  601. for y in ys {
  602. match table.get(y.to_bytes()).map_err(Error::from)? {
  603. Some(state) => states.push(Some(
  604. serde_json::from_str(state.value()).map_err(Error::from)?,
  605. )),
  606. None => states.push(None),
  607. }
  608. }
  609. Ok(states)
  610. }
  611. async fn get_proofs_by_keyset_id(
  612. &self,
  613. keyset_id: &Id,
  614. ) -> Result<(Proofs, Vec<Option<State>>), Self::Err> {
  615. let read_txn = self.db.begin_read().map_err(Error::from)?;
  616. let table = read_txn.open_table(PROOFS_TABLE).map_err(Error::from)?;
  617. let proofs_for_id = table
  618. .iter()
  619. .map_err(Error::from)?
  620. .flatten()
  621. .map(|(_, p)| serde_json::from_str::<Proof>(p.value()))
  622. .collect::<Result<Proofs, _>>()?
  623. .into_iter()
  624. .filter(|p| &p.keyset_id == keyset_id)
  625. .collect::<Proofs>();
  626. let proof_ys = proofs_for_id.ys()?;
  627. assert_eq!(proofs_for_id.len(), proof_ys.len());
  628. let states = self.get_proofs_states(&proof_ys).await?;
  629. Ok((proofs_for_id, states))
  630. }
  631. async fn update_proofs_states(
  632. &self,
  633. ys: &[PublicKey],
  634. proofs_state: State,
  635. ) -> Result<Vec<Option<State>>, Self::Err> {
  636. let write_txn = self.db.begin_write().map_err(Error::from)?;
  637. let mut states = Vec::with_capacity(ys.len());
  638. {
  639. let table = write_txn
  640. .open_table(PROOFS_STATE_TABLE)
  641. .map_err(Error::from)?;
  642. {
  643. // First collect current states
  644. for y in ys {
  645. let current_state = match table.get(y.to_bytes()).map_err(Error::from)? {
  646. Some(state) => {
  647. Some(serde_json::from_str(state.value()).map_err(Error::from)?)
  648. }
  649. None => None,
  650. };
  651. states.push(current_state);
  652. }
  653. }
  654. }
  655. // Check if any proofs are spent
  656. if states.contains(&Some(State::Spent)) {
  657. write_txn.abort().map_err(Error::from)?;
  658. return Err(database::Error::AttemptUpdateSpentProof);
  659. }
  660. {
  661. let mut table = write_txn
  662. .open_table(PROOFS_STATE_TABLE)
  663. .map_err(Error::from)?;
  664. {
  665. // If no proofs are spent, proceed with update
  666. let state_str = serde_json::to_string(&proofs_state).map_err(Error::from)?;
  667. for y in ys {
  668. table
  669. .insert(y.to_bytes(), state_str.as_str())
  670. .map_err(Error::from)?;
  671. }
  672. }
  673. }
  674. write_txn.commit().map_err(Error::from)?;
  675. Ok(states)
  676. }
  677. }
  678. #[async_trait]
  679. impl MintSignaturesDatabase for MintRedbDatabase {
  680. type Err = database::Error;
  681. async fn add_blind_signatures(
  682. &self,
  683. blinded_messages: &[PublicKey],
  684. blind_signatures: &[BlindSignature],
  685. quote_id: Option<Uuid>,
  686. ) -> Result<(), Self::Err> {
  687. let write_txn = self.db.begin_write().map_err(Error::from)?;
  688. {
  689. let mut table = write_txn
  690. .open_table(BLINDED_SIGNATURES)
  691. .map_err(Error::from)?;
  692. let mut time_table = write_txn
  693. .open_table(BLIND_SIGNATURE_CREATED_TIME)
  694. .map_err(Error::from)?;
  695. let mut quote_sigs_table = write_txn
  696. .open_multimap_table(QUOTE_SIGNATURES_TABLE)
  697. .map_err(Error::from)?;
  698. // Get current timestamp in seconds
  699. let current_time = unix_time();
  700. for (blinded_message, blind_signature) in blinded_messages.iter().zip(blind_signatures)
  701. {
  702. let blind_sig = serde_json::to_string(&blind_signature).map_err(Error::from)?;
  703. table
  704. .insert(blinded_message.to_bytes(), blind_sig.as_str())
  705. .map_err(Error::from)?;
  706. // Store creation time
  707. time_table
  708. .insert(blinded_message.to_bytes(), current_time)
  709. .map_err(Error::from)?;
  710. if let Some(quote_id) = &quote_id {
  711. quote_sigs_table
  712. .insert(quote_id.as_bytes(), blinded_message.to_bytes())
  713. .map_err(Error::from)?;
  714. }
  715. }
  716. }
  717. write_txn.commit().map_err(Error::from)?;
  718. Ok(())
  719. }
  720. async fn get_blind_signatures(
  721. &self,
  722. blinded_messages: &[PublicKey],
  723. ) -> Result<Vec<Option<BlindSignature>>, Self::Err> {
  724. let read_txn = self.db.begin_read().map_err(Error::from)?;
  725. let table = read_txn
  726. .open_table(BLINDED_SIGNATURES)
  727. .map_err(Error::from)?;
  728. let mut signatures = Vec::with_capacity(blinded_messages.len());
  729. for blinded_message in blinded_messages {
  730. match table.get(blinded_message.to_bytes()).map_err(Error::from)? {
  731. Some(blind_signature) => signatures.push(Some(
  732. serde_json::from_str(blind_signature.value()).map_err(Error::from)?,
  733. )),
  734. None => signatures.push(None),
  735. }
  736. }
  737. Ok(signatures)
  738. }
  739. async fn get_blind_signatures_for_keyset(
  740. &self,
  741. keyset_id: &Id,
  742. ) -> Result<Vec<BlindSignature>, Self::Err> {
  743. let read_txn = self.db.begin_read().map_err(Error::from)?;
  744. let table = read_txn
  745. .open_table(BLINDED_SIGNATURES)
  746. .map_err(Error::from)?;
  747. Ok(table
  748. .iter()
  749. .map_err(Error::from)?
  750. .flatten()
  751. .filter_map(|(_m, s)| {
  752. match serde_json::from_str::<BlindSignature>(s.value()).ok() {
  753. Some(signature) if &signature.keyset_id == keyset_id => Some(signature), // Filter by keyset_id
  754. _ => None, // Exclude non-matching entries
  755. }
  756. })
  757. .collect())
  758. }
  759. /// Get [`BlindSignature`]s for quote
  760. async fn get_blind_signatures_for_quote(
  761. &self,
  762. quote_id: &Uuid,
  763. ) -> Result<Vec<BlindSignature>, Self::Err> {
  764. let read_txn = self.db.begin_read().map_err(Error::from)?;
  765. let quote_proofs_table = read_txn
  766. .open_multimap_table(QUOTE_SIGNATURES_TABLE)
  767. .map_err(Error::from)?;
  768. let ys = quote_proofs_table.get(quote_id.as_bytes()).unwrap();
  769. let ys: Vec<[u8; 33]> = ys.into_iter().flatten().map(|v| v.value()).collect();
  770. let mut signatures = Vec::new();
  771. let signatures_table = read_txn
  772. .open_table(BLINDED_SIGNATURES)
  773. .map_err(Error::from)?;
  774. for y in ys {
  775. if let Some(sig) = signatures_table.get(y).map_err(Error::from)? {
  776. let sig = serde_json::from_str(sig.value())?;
  777. signatures.push(sig);
  778. }
  779. }
  780. Ok(signatures)
  781. }
  782. }
  783. #[async_trait]
  784. impl MintDatabase<database::Error> for MintRedbDatabase {
  785. async fn set_mint_info(&self, mint_info: MintInfo) -> Result<(), database::Error> {
  786. let write_txn = self.db.begin_write().map_err(Error::from)?;
  787. {
  788. let mut table = write_txn.open_table(CONFIG_TABLE).map_err(Error::from)?;
  789. table
  790. .insert("mint_info", serde_json::to_string(&mint_info)?.as_str())
  791. .map_err(Error::from)?;
  792. }
  793. write_txn.commit().map_err(Error::from)?;
  794. Ok(())
  795. }
  796. async fn get_mint_info(&self) -> Result<MintInfo, database::Error> {
  797. let read_txn = self.db.begin_read().map_err(Error::from)?;
  798. let table = read_txn.open_table(CONFIG_TABLE).map_err(Error::from)?;
  799. if let Some(mint_info) = table.get("mint_info").map_err(Error::from)? {
  800. let mint_info = serde_json::from_str(mint_info.value())?;
  801. return Ok(mint_info);
  802. }
  803. Err(Error::UnknownMintInfo.into())
  804. }
  805. async fn set_quote_ttl(&self, quote_ttl: QuoteTTL) -> Result<(), database::Error> {
  806. let write_txn = self.db.begin_write().map_err(Error::from)?;
  807. {
  808. let mut table = write_txn.open_table(CONFIG_TABLE).map_err(Error::from)?;
  809. table
  810. .insert("quote_ttl", serde_json::to_string(&quote_ttl)?.as_str())
  811. .map_err(Error::from)?;
  812. }
  813. write_txn.commit().map_err(Error::from)?;
  814. Ok(())
  815. }
  816. async fn get_quote_ttl(&self) -> Result<QuoteTTL, database::Error> {
  817. let read_txn = self.db.begin_read().map_err(Error::from)?;
  818. let table = read_txn.open_table(CONFIG_TABLE).map_err(Error::from)?;
  819. if let Some(quote_ttl) = table.get("quote_ttl").map_err(Error::from)? {
  820. let quote_ttl = serde_json::from_str(quote_ttl.value())?;
  821. return Ok(quote_ttl);
  822. }
  823. Err(Error::UnknownQuoteTTL.into())
  824. }
  825. }
  826. #[cfg(test)]
  827. mod tests {
  828. use cdk_common::secret::Secret;
  829. use cdk_common::{Amount, SecretKey};
  830. use tempfile::tempdir;
  831. use super::*;
  832. #[tokio::test]
  833. async fn test_remove_spent_proofs() {
  834. let tmp_dir = tempdir().unwrap();
  835. let db = MintRedbDatabase::new(&tmp_dir.path().join("mint.redb")).unwrap();
  836. // Create some test proofs
  837. let keyset_id = Id::from_str("00916bbf7ef91a36").unwrap();
  838. let proofs = vec![
  839. Proof {
  840. amount: Amount::from(100),
  841. keyset_id,
  842. secret: Secret::generate(),
  843. c: SecretKey::generate().public_key(),
  844. witness: None,
  845. dleq: None,
  846. },
  847. Proof {
  848. amount: Amount::from(200),
  849. keyset_id,
  850. secret: Secret::generate(),
  851. c: SecretKey::generate().public_key(),
  852. witness: None,
  853. dleq: None,
  854. },
  855. ];
  856. // Add proofs to database
  857. db.add_proofs(proofs.clone(), None).await.unwrap();
  858. // Mark one proof as spent
  859. db.update_proofs_states(&[proofs[0].y().unwrap()], State::Spent)
  860. .await
  861. .unwrap();
  862. db.update_proofs_states(&[proofs[1].y().unwrap()], State::Unspent)
  863. .await
  864. .unwrap();
  865. // Try to remove both proofs - should fail because one is spent
  866. let result = db
  867. .remove_proofs(&[proofs[0].y().unwrap(), proofs[1].y().unwrap()], None)
  868. .await;
  869. assert!(result.is_err());
  870. assert!(matches!(
  871. result.unwrap_err(),
  872. database::Error::AttemptRemoveSpentProof
  873. ));
  874. // Verify both proofs still exist
  875. let states = db
  876. .get_proofs_states(&[proofs[0].y().unwrap(), proofs[1].y().unwrap()])
  877. .await
  878. .unwrap();
  879. assert_eq!(states.len(), 2);
  880. assert_eq!(states[0], Some(State::Spent));
  881. assert_eq!(states[1], Some(State::Unspent));
  882. }
  883. #[tokio::test]
  884. async fn test_update_spent_proofs() {
  885. let tmp_dir = tempdir().unwrap();
  886. let db = MintRedbDatabase::new(&tmp_dir.path().join("mint.redb")).unwrap();
  887. // Create some test proofs
  888. let keyset_id = Id::from_str("00916bbf7ef91a36").unwrap();
  889. let proofs = vec![
  890. Proof {
  891. amount: Amount::from(100),
  892. keyset_id,
  893. secret: Secret::generate(),
  894. c: SecretKey::generate().public_key(),
  895. witness: None,
  896. dleq: None,
  897. },
  898. Proof {
  899. amount: Amount::from(200),
  900. keyset_id,
  901. secret: Secret::generate(),
  902. c: SecretKey::generate().public_key(),
  903. witness: None,
  904. dleq: None,
  905. },
  906. ];
  907. // Add proofs to database
  908. db.add_proofs(proofs.clone(), None).await.unwrap();
  909. // Mark one proof as spent
  910. db.update_proofs_states(&[proofs[0].y().unwrap()], State::Spent)
  911. .await
  912. .unwrap();
  913. db.update_proofs_states(&[proofs[1].y().unwrap()], State::Unspent)
  914. .await
  915. .unwrap();
  916. // Mark one proof as spent
  917. let result = db
  918. .update_proofs_states(
  919. &[proofs[0].y().unwrap(), proofs[1].y().unwrap()],
  920. State::Unspent,
  921. )
  922. .await;
  923. assert!(result.is_err());
  924. assert!(matches!(
  925. result.unwrap_err(),
  926. database::Error::AttemptUpdateSpentProof
  927. ));
  928. // Verify both proofs still exist
  929. let states = db
  930. .get_proofs_states(&[proofs[0].y().unwrap(), proofs[1].y().unwrap()])
  931. .await
  932. .unwrap();
  933. assert_eq!(states.len(), 2);
  934. assert_eq!(states[0], Some(State::Spent));
  935. assert_eq!(states[1], Some(State::Unspent));
  936. }
  937. }