cdk_redb/mint/
mod.rs

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