1use 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
28const MINTS_TABLE: TableDefinition<&str, &str> = TableDefinition::new("mints_table");
30const MINT_KEYSETS_TABLE: MultimapTableDefinition<&str, &[u8]> =
32 MultimapTableDefinition::new("mint_keysets");
33const KEYSETS_TABLE: TableDefinition<&[u8], &str> = TableDefinition::new("keysets");
35const MINT_QUOTES_TABLE: TableDefinition<&str, &str> = TableDefinition::new("mint_quotes");
37const MELT_QUOTES_TABLE: TableDefinition<&str, &str> = TableDefinition::new("melt_quotes");
39const MINT_KEYS_TABLE: TableDefinition<&str, &str> = TableDefinition::new("mint_keys");
40const 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");
44const 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#[derive(Debug, Clone)]
53pub struct WalletRedbDatabase {
54 db: Arc<Database>,
55}
56
57impl WalletRedbDatabase {
58 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 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 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 {
252 let proofs = self
253 .get_proofs(Some(old_mint_url.clone()), None, None, None)
254 .await
255 .map_err(Error::from)?;
256
257 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 {
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 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("e).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("e).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}