mod.rs 38 KB

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