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