cdk_redb/wallet/
mod.rs

1//! Redb Wallet
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::ProofInfo;
11use cdk_common::database::WalletDatabase;
12use cdk_common::mint_url::MintUrl;
13use cdk_common::util::unix_time;
14use cdk_common::wallet::{self, MintQuote, Transaction, TransactionDirection, TransactionId};
15use cdk_common::{
16    database, CurrencyUnit, Id, KeySet, KeySetInfo, Keys, MintInfo, PublicKey, SpendingConditions,
17    State,
18};
19use redb::{Database, MultimapTableDefinition, ReadableTable, TableDefinition};
20use tracing::instrument;
21
22use super::error::Error;
23use crate::migrations::migrate_00_to_01;
24use crate::wallet::migrations::{migrate_01_to_02, migrate_02_to_03, migrate_03_to_04};
25
26mod migrations;
27
28// <Mint_url, Info>
29const MINTS_TABLE: TableDefinition<&str, &str> = TableDefinition::new("mints_table");
30// <Mint_Url, Keyset_id>
31const MINT_KEYSETS_TABLE: MultimapTableDefinition<&str, &[u8]> =
32    MultimapTableDefinition::new("mint_keysets");
33// <Keyset_id, KeysetInfo>
34const KEYSETS_TABLE: TableDefinition<&[u8], &str> = TableDefinition::new("keysets");
35// <Quote_id, quote>
36const MINT_QUOTES_TABLE: TableDefinition<&str, &str> = TableDefinition::new("mint_quotes");
37// <Quote_id, quote>
38const MELT_QUOTES_TABLE: TableDefinition<&str, &str> = TableDefinition::new("melt_quotes");
39const MINT_KEYS_TABLE: TableDefinition<&str, &str> = TableDefinition::new("mint_keys");
40// <Y, Proof Info>
41const PROOFS_TABLE: TableDefinition<&[u8], &str> = TableDefinition::new("proofs");
42const CONFIG_TABLE: TableDefinition<&str, &str> = TableDefinition::new("config");
43const KEYSET_COUNTER: TableDefinition<&str, u32> = TableDefinition::new("keyset_counter");
44// <Transaction_id, Transaction>
45const TRANSACTIONS_TABLE: TableDefinition<&[u8], &str> = TableDefinition::new("transactions");
46
47const KEYSET_U32_MAPPING: TableDefinition<u32, &str> = TableDefinition::new("keyset_u32_mapping");
48
49const DATABASE_VERSION: u32 = 4;
50
51/// Wallet Redb Database
52#[derive(Debug, Clone)]
53pub struct WalletRedbDatabase {
54    db: Arc<Database>,
55}
56
57impl WalletRedbDatabase {
58    /// Create new [`WalletRedbDatabase`]
59    pub fn new(path: &Path) -> Result<Self, Error> {
60        {
61            let db = Arc::new(Database::create(path)?);
62
63            let db_version: Option<String>;
64            {
65                // Check database version
66                let read_txn = db.begin_read()?;
67                let table = read_txn.open_table(CONFIG_TABLE);
68
69                db_version = match table {
70                    Ok(table) => table.get("db_version")?.map(|v| v.value().to_string()),
71                    Err(_) => None,
72                };
73            }
74
75            match db_version {
76                Some(db_version) => {
77                    let mut current_file_version = u32::from_str(&db_version)?;
78                    tracing::info!("Current file version {}", current_file_version);
79
80                    match current_file_version.cmp(&DATABASE_VERSION) {
81                        Ordering::Less => {
82                            tracing::info!(
83                                "Database needs to be upgraded at {} current is {}",
84                                current_file_version,
85                                DATABASE_VERSION
86                            );
87                            if current_file_version == 0 {
88                                current_file_version = migrate_00_to_01(Arc::clone(&db))?;
89                            }
90
91                            if current_file_version == 1 {
92                                current_file_version = migrate_01_to_02(Arc::clone(&db))?;
93                            }
94
95                            if current_file_version == 2 {
96                                current_file_version = migrate_02_to_03(Arc::clone(&db))?;
97                            }
98
99                            if current_file_version == 3 {
100                                current_file_version = migrate_03_to_04(Arc::clone(&db))?;
101                            }
102
103                            if current_file_version != DATABASE_VERSION {
104                                tracing::warn!(
105                                    "Database upgrade did not complete at {} current is {}",
106                                    current_file_version,
107                                    DATABASE_VERSION
108                                );
109                                return Err(Error::UnknownDatabaseVersion);
110                            }
111
112                            let write_txn = db.begin_write()?;
113                            {
114                                let mut table = write_txn.open_table(CONFIG_TABLE)?;
115
116                                table
117                                    .insert("db_version", DATABASE_VERSION.to_string().as_str())?;
118                            }
119
120                            write_txn.commit()?;
121                        }
122                        Ordering::Equal => {
123                            tracing::info!("Database is at current version {}", DATABASE_VERSION);
124                        }
125                        Ordering::Greater => {
126                            tracing::warn!(
127                                "Database upgrade did not complete at {} current is {}",
128                                current_file_version,
129                                DATABASE_VERSION
130                            );
131                            return Err(Error::UnknownDatabaseVersion);
132                        }
133                    }
134                }
135                None => {
136                    let write_txn = db.begin_write()?;
137                    {
138                        let mut table = write_txn.open_table(CONFIG_TABLE)?;
139                        // Open all tables to init a new db
140                        let _ = write_txn.open_table(MINTS_TABLE)?;
141                        let _ = write_txn.open_multimap_table(MINT_KEYSETS_TABLE)?;
142                        let _ = write_txn.open_table(KEYSETS_TABLE)?;
143                        let _ = write_txn.open_table(MINT_QUOTES_TABLE)?;
144                        let _ = write_txn.open_table(MELT_QUOTES_TABLE)?;
145                        let _ = write_txn.open_table(MINT_KEYS_TABLE)?;
146                        let _ = write_txn.open_table(PROOFS_TABLE)?;
147                        let _ = write_txn.open_table(KEYSET_COUNTER)?;
148                        let _ = write_txn.open_table(TRANSACTIONS_TABLE)?;
149                        let _ = write_txn.open_table(KEYSET_U32_MAPPING)?;
150                        table.insert("db_version", DATABASE_VERSION.to_string().as_str())?;
151                    }
152
153                    write_txn.commit()?;
154                }
155            }
156            drop(db);
157        }
158
159        let mut db = Database::create(path)?;
160
161        db.upgrade()?;
162
163        Ok(Self { db: Arc::new(db) })
164    }
165}
166
167#[async_trait]
168impl WalletDatabase for WalletRedbDatabase {
169    type Err = database::Error;
170
171    #[instrument(skip(self))]
172    async fn add_mint(
173        &self,
174        mint_url: MintUrl,
175        mint_info: Option<MintInfo>,
176    ) -> Result<(), Self::Err> {
177        let write_txn = self.db.begin_write().map_err(Error::from)?;
178
179        {
180            let mut table = write_txn.open_table(MINTS_TABLE).map_err(Error::from)?;
181            table
182                .insert(
183                    mint_url.to_string().as_str(),
184                    serde_json::to_string(&mint_info)
185                        .map_err(Error::from)?
186                        .as_str(),
187                )
188                .map_err(Error::from)?;
189        }
190        write_txn.commit().map_err(Error::from)?;
191
192        Ok(())
193    }
194
195    #[instrument(skip(self))]
196    async fn remove_mint(&self, mint_url: MintUrl) -> Result<(), Self::Err> {
197        let write_txn = self.db.begin_write().map_err(Error::from)?;
198
199        {
200            let mut table = write_txn.open_table(MINTS_TABLE).map_err(Error::from)?;
201            table
202                .remove(mint_url.to_string().as_str())
203                .map_err(Error::from)?;
204        }
205        write_txn.commit().map_err(Error::from)?;
206
207        Ok(())
208    }
209
210    #[instrument(skip(self))]
211    async fn get_mint(&self, mint_url: MintUrl) -> Result<Option<MintInfo>, Self::Err> {
212        let read_txn = self.db.begin_read().map_err(Into::<Error>::into)?;
213        let table = read_txn.open_table(MINTS_TABLE).map_err(Error::from)?;
214
215        if let Some(mint_info) = table
216            .get(mint_url.to_string().as_str())
217            .map_err(Error::from)?
218        {
219            return Ok(serde_json::from_str(mint_info.value()).map_err(Error::from)?);
220        }
221
222        Ok(None)
223    }
224
225    #[instrument(skip(self))]
226    async fn get_mints(&self) -> Result<HashMap<MintUrl, Option<MintInfo>>, Self::Err> {
227        let read_txn = self.db.begin_read().map_err(Error::from)?;
228        let table = read_txn.open_table(MINTS_TABLE).map_err(Error::from)?;
229        let mints = table
230            .iter()
231            .map_err(Error::from)?
232            .flatten()
233            .map(|(mint, mint_info)| {
234                (
235                    MintUrl::from_str(mint.value()).unwrap(),
236                    serde_json::from_str(mint_info.value()).ok(),
237                )
238            })
239            .collect();
240
241        Ok(mints)
242    }
243
244    #[instrument(skip(self))]
245    async fn update_mint_url(
246        &self,
247        old_mint_url: MintUrl,
248        new_mint_url: MintUrl,
249    ) -> Result<(), Self::Err> {
250        // Update proofs table
251        {
252            let proofs = self
253                .get_proofs(Some(old_mint_url.clone()), None, None, None)
254                .await
255                .map_err(Error::from)?;
256
257            // Proofs with new url
258            let updated_proofs: Vec<ProofInfo> = proofs
259                .clone()
260                .into_iter()
261                .map(|mut p| {
262                    p.mint_url = new_mint_url.clone();
263                    p
264                })
265                .collect();
266
267            if !updated_proofs.is_empty() {
268                self.update_proofs(updated_proofs, vec![]).await?;
269            }
270        }
271
272        // Update mint quotes
273        {
274            let quotes = self.get_mint_quotes().await?;
275
276            let unix_time = unix_time();
277
278            let quotes: Vec<MintQuote> = quotes
279                .into_iter()
280                .filter_map(|mut q| {
281                    if q.expiry < unix_time {
282                        q.mint_url = new_mint_url.clone();
283                        Some(q)
284                    } else {
285                        None
286                    }
287                })
288                .collect();
289
290            for quote in quotes {
291                self.add_mint_quote(quote).await?;
292            }
293        }
294
295        Ok(())
296    }
297
298    #[instrument(skip(self))]
299    async fn add_mint_keysets(
300        &self,
301        mint_url: MintUrl,
302        keysets: Vec<KeySetInfo>,
303    ) -> Result<(), Self::Err> {
304        let write_txn = self.db.begin_write().map_err(Error::from)?;
305
306        let mut existing_u32 = false;
307
308        {
309            let mut table = write_txn
310                .open_multimap_table(MINT_KEYSETS_TABLE)
311                .map_err(Error::from)?;
312            let mut keysets_table = write_txn.open_table(KEYSETS_TABLE).map_err(Error::from)?;
313            let mut u32_table = write_txn
314                .open_table(KEYSET_U32_MAPPING)
315                .map_err(Error::from)?;
316
317            for keyset in keysets {
318                // Check if keyset already exists
319                let existing_keyset = {
320                    let existing_keyset = keysets_table
321                        .get(keyset.id.to_bytes().as_slice())
322                        .map_err(Error::from)?;
323
324                    existing_keyset.map(|r| r.value().to_string())
325                };
326
327                let existing = u32_table
328                    .insert(u32::from(keyset.id), keyset.id.to_string().as_str())
329                    .map_err(Error::from)?;
330
331                match existing {
332                    None => existing_u32 = false,
333                    Some(id) => {
334                        let id = Id::from_str(id.value())?;
335
336                        if id == keyset.id {
337                            existing_u32 = false;
338                        } else {
339                            println!("Breaking here");
340                            existing_u32 = true;
341                            break;
342                        }
343                    }
344                }
345
346                let keyset = if let Some(existing_keyset) = existing_keyset {
347                    let mut existing_keyset: KeySetInfo = serde_json::from_str(&existing_keyset)?;
348
349                    existing_keyset.active = keyset.active;
350                    existing_keyset.input_fee_ppk = keyset.input_fee_ppk;
351
352                    existing_keyset
353                } else {
354                    table
355                        .insert(
356                            mint_url.to_string().as_str(),
357                            keyset.id.to_bytes().as_slice(),
358                        )
359                        .map_err(Error::from)?;
360
361                    keyset
362                };
363
364                keysets_table
365                    .insert(
366                        keyset.id.to_bytes().as_slice(),
367                        serde_json::to_string(&keyset)
368                            .map_err(Error::from)?
369                            .as_str(),
370                    )
371                    .map_err(Error::from)?;
372            }
373        }
374
375        if existing_u32 {
376            tracing::warn!("Keyset already exists for keyset id");
377            write_txn.abort().map_err(Error::from)?;
378
379            return Err(database::Error::Duplicate);
380        }
381
382        write_txn.commit().map_err(Error::from)?;
383
384        Ok(())
385    }
386
387    #[instrument(skip(self))]
388    async fn get_mint_keysets(
389        &self,
390        mint_url: MintUrl,
391    ) -> Result<Option<Vec<KeySetInfo>>, Self::Err> {
392        let read_txn = self.db.begin_read().map_err(Into::<Error>::into)?;
393        let table = read_txn
394            .open_multimap_table(MINT_KEYSETS_TABLE)
395            .map_err(Error::from)?;
396
397        let keyset_ids = table
398            .get(mint_url.to_string().as_str())
399            .map_err(Error::from)?
400            .flatten()
401            .map(|k| Id::from_bytes(k.value()))
402            .collect::<Result<Vec<_>, _>>()?;
403
404        let mut keysets = vec![];
405
406        let keysets_t = read_txn.open_table(KEYSETS_TABLE).map_err(Error::from)?;
407
408        for keyset_id in keyset_ids {
409            if let Some(keyset) = keysets_t
410                .get(keyset_id.to_bytes().as_slice())
411                .map_err(Error::from)?
412            {
413                let keyset = serde_json::from_str(keyset.value()).map_err(Error::from)?;
414
415                keysets.push(keyset);
416            }
417        }
418
419        match keysets.is_empty() {
420            true => Ok(None),
421            false => Ok(Some(keysets)),
422        }
423    }
424
425    #[instrument(skip(self), fields(keyset_id = %keyset_id))]
426    async fn get_keyset_by_id(&self, keyset_id: &Id) -> Result<Option<KeySetInfo>, Self::Err> {
427        let read_txn = self.db.begin_read().map_err(Into::<Error>::into)?;
428        let table = read_txn.open_table(KEYSETS_TABLE).map_err(Error::from)?;
429
430        match table
431            .get(keyset_id.to_bytes().as_slice())
432            .map_err(Error::from)?
433        {
434            Some(keyset) => {
435                let keyset: KeySetInfo =
436                    serde_json::from_str(keyset.value()).map_err(Error::from)?;
437
438                Ok(Some(keyset))
439            }
440            None => Ok(None),
441        }
442    }
443
444    #[instrument(skip_all)]
445    async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), Self::Err> {
446        let write_txn = self.db.begin_write().map_err(Error::from)?;
447
448        {
449            let mut table = write_txn
450                .open_table(MINT_QUOTES_TABLE)
451                .map_err(Error::from)?;
452            table
453                .insert(
454                    quote.id.as_str(),
455                    serde_json::to_string(&quote).map_err(Error::from)?.as_str(),
456                )
457                .map_err(Error::from)?;
458        }
459
460        write_txn.commit().map_err(Error::from)?;
461
462        Ok(())
463    }
464
465    #[instrument(skip_all)]
466    async fn get_mint_quote(&self, quote_id: &str) -> Result<Option<MintQuote>, Self::Err> {
467        let read_txn = self.db.begin_read().map_err(Into::<Error>::into)?;
468        let table = read_txn
469            .open_table(MINT_QUOTES_TABLE)
470            .map_err(Error::from)?;
471
472        if let Some(mint_info) = table.get(quote_id).map_err(Error::from)? {
473            return Ok(serde_json::from_str(mint_info.value()).map_err(Error::from)?);
474        }
475
476        Ok(None)
477    }
478
479    #[instrument(skip_all)]
480    async fn get_mint_quotes(&self) -> Result<Vec<MintQuote>, Self::Err> {
481        let read_txn = self.db.begin_read().map_err(Into::<Error>::into)?;
482        let table = read_txn
483            .open_table(MINT_QUOTES_TABLE)
484            .map_err(Error::from)?;
485
486        Ok(table
487            .iter()
488            .map_err(Error::from)?
489            .flatten()
490            .flat_map(|(_id, quote)| serde_json::from_str(quote.value()))
491            .collect())
492    }
493
494    #[instrument(skip_all)]
495    async fn remove_mint_quote(&self, quote_id: &str) -> Result<(), Self::Err> {
496        let write_txn = self.db.begin_write().map_err(Error::from)?;
497
498        {
499            let mut table = write_txn
500                .open_table(MINT_QUOTES_TABLE)
501                .map_err(Error::from)?;
502            table.remove(quote_id).map_err(Error::from)?;
503        }
504
505        write_txn.commit().map_err(Error::from)?;
506
507        Ok(())
508    }
509
510    #[instrument(skip_all)]
511    async fn add_melt_quote(&self, quote: wallet::MeltQuote) -> Result<(), Self::Err> {
512        let write_txn = self.db.begin_write().map_err(Error::from)?;
513
514        {
515            let mut table = write_txn
516                .open_table(MELT_QUOTES_TABLE)
517                .map_err(Error::from)?;
518            table
519                .insert(
520                    quote.id.as_str(),
521                    serde_json::to_string(&quote).map_err(Error::from)?.as_str(),
522                )
523                .map_err(Error::from)?;
524        }
525
526        write_txn.commit().map_err(Error::from)?;
527
528        Ok(())
529    }
530
531    #[instrument(skip_all)]
532    async fn get_melt_quote(&self, quote_id: &str) -> Result<Option<wallet::MeltQuote>, Self::Err> {
533        let read_txn = self.db.begin_read().map_err(Error::from)?;
534        let table = read_txn
535            .open_table(MELT_QUOTES_TABLE)
536            .map_err(Error::from)?;
537
538        if let Some(mint_info) = table.get(quote_id).map_err(Error::from)? {
539            return Ok(serde_json::from_str(mint_info.value()).map_err(Error::from)?);
540        }
541
542        Ok(None)
543    }
544
545    #[instrument(skip_all)]
546    async fn get_melt_quotes(&self) -> Result<Vec<wallet::MeltQuote>, Self::Err> {
547        let read_txn = self.db.begin_read().map_err(Error::from)?;
548        let table = read_txn
549            .open_table(MELT_QUOTES_TABLE)
550            .map_err(Error::from)?;
551
552        Ok(table
553            .iter()
554            .map_err(Error::from)?
555            .flatten()
556            .flat_map(|(_id, quote)| serde_json::from_str(quote.value()))
557            .collect())
558    }
559
560    #[instrument(skip_all)]
561    async fn remove_melt_quote(&self, quote_id: &str) -> Result<(), Self::Err> {
562        let write_txn = self.db.begin_write().map_err(Error::from)?;
563
564        {
565            let mut table = write_txn
566                .open_table(MELT_QUOTES_TABLE)
567                .map_err(Error::from)?;
568            table.remove(quote_id).map_err(Error::from)?;
569        }
570
571        write_txn.commit().map_err(Error::from)?;
572
573        Ok(())
574    }
575
576    #[instrument(skip_all)]
577    async fn add_keys(&self, keyset: KeySet) -> Result<(), Self::Err> {
578        let write_txn = self.db.begin_write().map_err(Error::from)?;
579
580        keyset.verify_id()?;
581
582        let existing_keys;
583        let existing_u32;
584
585        {
586            let mut table = write_txn.open_table(MINT_KEYS_TABLE).map_err(Error::from)?;
587
588            existing_keys = table
589                .insert(
590                    keyset.id.to_string().as_str(),
591                    serde_json::to_string(&keyset.keys)
592                        .map_err(Error::from)?
593                        .as_str(),
594                )
595                .map_err(Error::from)?
596                .is_some();
597
598            let mut table = write_txn
599                .open_table(KEYSET_U32_MAPPING)
600                .map_err(Error::from)?;
601
602            let existing = table
603                .insert(u32::from(keyset.id), keyset.id.to_string().as_str())
604                .map_err(Error::from)?;
605
606            match existing {
607                None => existing_u32 = false,
608                Some(id) => {
609                    let id = Id::from_str(id.value())?;
610
611                    existing_u32 = id != keyset.id;
612                }
613            }
614        }
615
616        if existing_keys || existing_u32 {
617            tracing::warn!("Keys already exist for keyset id");
618            write_txn.abort().map_err(Error::from)?;
619
620            return Err(database::Error::Duplicate);
621        }
622
623        write_txn.commit().map_err(Error::from)?;
624
625        Ok(())
626    }
627
628    #[instrument(skip(self), fields(keyset_id = %keyset_id))]
629    async fn get_keys(&self, keyset_id: &Id) -> Result<Option<Keys>, Self::Err> {
630        let read_txn = self.db.begin_read().map_err(Error::from)?;
631        let table = read_txn.open_table(MINT_KEYS_TABLE).map_err(Error::from)?;
632
633        if let Some(mint_info) = table
634            .get(keyset_id.to_string().as_str())
635            .map_err(Error::from)?
636        {
637            return Ok(serde_json::from_str(mint_info.value()).map_err(Error::from)?);
638        }
639
640        Ok(None)
641    }
642
643    #[instrument(skip(self), fields(keyset_id = %keyset_id))]
644    async fn remove_keys(&self, keyset_id: &Id) -> Result<(), Self::Err> {
645        let write_txn = self.db.begin_write().map_err(Error::from)?;
646
647        {
648            let mut table = write_txn.open_table(MINT_KEYS_TABLE).map_err(Error::from)?;
649
650            table
651                .remove(keyset_id.to_string().as_str())
652                .map_err(Error::from)?;
653        }
654
655        write_txn.commit().map_err(Error::from)?;
656
657        Ok(())
658    }
659
660    #[instrument(skip(self, added, deleted_ys))]
661    async fn update_proofs(
662        &self,
663        added: Vec<ProofInfo>,
664        deleted_ys: Vec<PublicKey>,
665    ) -> Result<(), Self::Err> {
666        let write_txn = self.db.begin_write().map_err(Error::from)?;
667
668        {
669            let mut table = write_txn.open_table(PROOFS_TABLE).map_err(Error::from)?;
670
671            for proof_info in added.iter() {
672                table
673                    .insert(
674                        proof_info.y.to_bytes().as_slice(),
675                        serde_json::to_string(&proof_info)
676                            .map_err(Error::from)?
677                            .as_str(),
678                    )
679                    .map_err(Error::from)?;
680            }
681
682            for y in deleted_ys.iter() {
683                table.remove(y.to_bytes().as_slice()).map_err(Error::from)?;
684            }
685        }
686        write_txn.commit().map_err(Error::from)?;
687
688        Ok(())
689    }
690
691    #[instrument(skip_all)]
692    async fn get_proofs(
693        &self,
694        mint_url: Option<MintUrl>,
695        unit: Option<CurrencyUnit>,
696        state: Option<Vec<State>>,
697        spending_conditions: Option<Vec<SpendingConditions>>,
698    ) -> Result<Vec<ProofInfo>, Self::Err> {
699        let read_txn = self.db.begin_read().map_err(Error::from)?;
700
701        let table = read_txn.open_table(PROOFS_TABLE).map_err(Error::from)?;
702
703        let proofs: Vec<ProofInfo> = table
704            .iter()
705            .map_err(Error::from)?
706            .flatten()
707            .filter_map(|(_k, v)| {
708                let mut proof = None;
709
710                if let Ok(proof_info) = serde_json::from_str::<ProofInfo>(v.value()) {
711                    if proof_info.matches_conditions(&mint_url, &unit, &state, &spending_conditions)
712                    {
713                        proof = Some(proof_info)
714                    }
715                }
716
717                proof
718            })
719            .collect();
720
721        Ok(proofs)
722    }
723
724    async fn update_proofs_state(
725        &self,
726        ys: Vec<PublicKey>,
727        state: State,
728    ) -> Result<(), database::Error> {
729        let read_txn = self.db.begin_read().map_err(Error::from)?;
730        let table = read_txn.open_table(PROOFS_TABLE).map_err(Error::from)?;
731
732        let write_txn = self.db.begin_write().map_err(Error::from)?;
733
734        for y in ys {
735            let y_slice = y.to_bytes();
736            let proof = table
737                .get(y_slice.as_slice())
738                .map_err(Error::from)?
739                .ok_or(Error::UnknownY)?;
740
741            let mut proof_info =
742                serde_json::from_str::<ProofInfo>(proof.value()).map_err(Error::from)?;
743
744            proof_info.state = state;
745
746            {
747                let mut table = write_txn.open_table(PROOFS_TABLE).map_err(Error::from)?;
748                table
749                    .insert(
750                        y_slice.as_slice(),
751                        serde_json::to_string(&proof_info)
752                            .map_err(Error::from)?
753                            .as_str(),
754                    )
755                    .map_err(Error::from)?;
756            }
757        }
758
759        write_txn.commit().map_err(Error::from)?;
760
761        Ok(())
762    }
763
764    #[instrument(skip(self), fields(keyset_id = %keyset_id))]
765    async fn increment_keyset_counter(&self, keyset_id: &Id, count: u32) -> Result<u32, Self::Err> {
766        let write_txn = self.db.begin_write().map_err(Error::from)?;
767
768        let current_counter;
769        let new_counter;
770        {
771            let table = write_txn.open_table(KEYSET_COUNTER).map_err(Error::from)?;
772            let counter = table
773                .get(keyset_id.to_string().as_str())
774                .map_err(Error::from)?;
775
776            current_counter = match counter {
777                Some(c) => c.value(),
778                None => 0,
779            };
780
781            new_counter = current_counter + count;
782        }
783
784        {
785            let mut table = write_txn.open_table(KEYSET_COUNTER).map_err(Error::from)?;
786
787            table
788                .insert(keyset_id.to_string().as_str(), new_counter)
789                .map_err(Error::from)?;
790        }
791        write_txn.commit().map_err(Error::from)?;
792
793        Ok(new_counter)
794    }
795
796    #[instrument(skip(self))]
797    async fn add_transaction(&self, transaction: Transaction) -> Result<(), Self::Err> {
798        let write_txn = self.db.begin_write().map_err(Error::from)?;
799
800        {
801            let mut table = write_txn
802                .open_table(TRANSACTIONS_TABLE)
803                .map_err(Error::from)?;
804            table
805                .insert(
806                    transaction.id().as_slice(),
807                    serde_json::to_string(&transaction)
808                        .map_err(Error::from)?
809                        .as_str(),
810                )
811                .map_err(Error::from)?;
812        }
813
814        write_txn.commit().map_err(Error::from)?;
815
816        Ok(())
817    }
818
819    #[instrument(skip(self))]
820    async fn get_transaction(
821        &self,
822        transaction_id: TransactionId,
823    ) -> Result<Option<Transaction>, Self::Err> {
824        let read_txn = self.db.begin_read().map_err(Error::from)?;
825        let table = read_txn
826            .open_table(TRANSACTIONS_TABLE)
827            .map_err(Error::from)?;
828
829        if let Some(transaction) = table.get(transaction_id.as_slice()).map_err(Error::from)? {
830            return Ok(serde_json::from_str(transaction.value()).map_err(Error::from)?);
831        }
832
833        Ok(None)
834    }
835
836    #[instrument(skip(self))]
837    async fn list_transactions(
838        &self,
839        mint_url: Option<MintUrl>,
840        direction: Option<TransactionDirection>,
841        unit: Option<CurrencyUnit>,
842    ) -> Result<Vec<Transaction>, Self::Err> {
843        let read_txn = self.db.begin_read().map_err(Error::from)?;
844
845        let table = read_txn
846            .open_table(TRANSACTIONS_TABLE)
847            .map_err(Error::from)?;
848
849        let transactions: Vec<Transaction> = table
850            .iter()
851            .map_err(Error::from)?
852            .flatten()
853            .filter_map(|(_k, v)| {
854                let mut transaction = None;
855
856                if let Ok(tx) = serde_json::from_str::<Transaction>(v.value()) {
857                    if tx.matches_conditions(&mint_url, &direction, &unit) {
858                        transaction = Some(tx)
859                    }
860                }
861
862                transaction
863            })
864            .collect();
865
866        Ok(transactions)
867    }
868
869    #[instrument(skip(self))]
870    async fn remove_transaction(&self, transaction_id: TransactionId) -> Result<(), Self::Err> {
871        let write_txn = self.db.begin_write().map_err(Error::from)?;
872
873        {
874            let mut table = write_txn
875                .open_table(TRANSACTIONS_TABLE)
876                .map_err(Error::from)?;
877            table
878                .remove(transaction_id.as_slice())
879                .map_err(Error::from)?;
880        }
881
882        write_txn.commit().map_err(Error::from)?;
883
884        Ok(())
885    }
886}