Skip to main content

fedimint_core/db/
mod.rs

1//! Core Fedimint database traits and types
2//!
3//! This module provides the core key-value database for Fedimint.
4//!
5//! # Usage
6//!
7//! To use the database, you typically follow these steps:
8//!
9//! 1. Create a `Database` instance
10//! 2. Begin a transaction
11//! 3. Perform operations within the transaction
12//! 4. Commit the transaction
13//!
14//! ## Example
15//!
16//! ```rust
17//! use fedimint_core::db::mem_impl::MemDatabase;
18//! use fedimint_core::db::{Database, DatabaseTransaction, IDatabaseTransactionOpsCoreTyped};
19//! use fedimint_core::encoding::{Decodable, Encodable};
20//! use fedimint_core::impl_db_record;
21//! use fedimint_core::module::registry::ModuleDecoderRegistry;
22//!
23//! #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Encodable, Decodable)]
24//! pub struct TestKey(pub u64);
25//! #[derive(Debug, Encodable, Decodable, Eq, PartialEq, PartialOrd, Ord)]
26//! pub struct TestVal(pub u64);
27//!
28//! #[repr(u8)]
29//! #[derive(Clone)]
30//! pub enum TestDbKeyPrefix {
31//!     Test = 0x42,
32//! }
33//!
34//! impl_db_record!(
35//!     key = TestKey,
36//!     value = TestVal,
37//!     db_prefix = TestDbKeyPrefix::Test,
38//! );
39//!
40//! # async fn example() {
41//! // Create a new in-memory database
42//! let db = Database::new(MemDatabase::new(), ModuleDecoderRegistry::default());
43//!
44//! // Begin a transaction
45//! let mut tx = db.begin_transaction().await;
46//!
47//! // Perform operations
48//! tx.insert_entry(&TestKey(1), &TestVal(100)).await;
49//! let value = tx.get_value(&TestKey(1)).await;
50//!
51//! // Commit the transaction
52//! tx.commit_tx().await;
53//!
54//! // For operations that may need to be retried due to conflicts, use the
55//! // `autocommit` function:
56//!
57//! db.autocommit(
58//!     |dbtx, _| {
59//!         Box::pin(async move {
60//!             dbtx.insert_entry(&TestKey(1), &TestVal(100)).await;
61//!             Ok::<(), std::convert::Infallible>(())
62//!         })
63//!     },
64//!     None,
65//! )
66//! .await
67//! .unwrap();
68//! # }
69//! ```
70//!
71//! # Isolation of database transactions
72//!
73//! Fedimint requires that the database implementation implement Snapshot
74//! Isolation. Snapshot Isolation is a database isolation level that guarantees
75//! consistent reads from the time that the snapshot was created (at transaction
76//! creation time). Transactions with Snapshot Isolation level will only commit
77//! if there has been no write to the modified keys since the snapshot (i.e.
78//! write-write conflicts are prevented).
79//!
80//! Specifically, Fedimint expects the database implementation to prevent the
81//! following anomalies:
82//!
83//! Non-Readable Write: TX1 writes (K1, V1) at time t but cannot read (K1, V1)
84//! at time (t + i)
85//!
86//! Dirty Read: TX1 is able to read TX2's uncommitted writes.
87//!
88//! Non-Repeatable Read: TX1 reads (K1, V1) at time t and retrieves (K1, V2) at
89//! time (t + i) where V1 != V2.
90//!
91//! Phantom Record: TX1 retrieves X number of records for a prefix at time t and
92//! retrieves Y number of records for the same prefix at time (t + i).
93//!
94//! Lost Writes: TX1 writes (K1, V1) at the same time as TX2 writes (K1, V2). V2
95//! overwrites V1 as the value for K1 (write-write conflict).
96//!
97//! | Type     | Non-Readable Write | Dirty Read | Non-Repeatable Read | Phantom
98//! Record | Lost Writes | | -------- | ------------------ | ---------- |
99//! ------------------- | -------------- | ----------- | | MemoryDB | Prevented
100//! | Prevented  | Prevented           | Prevented      | Possible    |
101//! | RocksDB  | Prevented          | Prevented  | Prevented           |
102//! Prevented      | Prevented   | | Sqlite   | Prevented          | Prevented
103//! | Prevented           | Prevented      | Prevented   |
104
105use std::any;
106use std::collections::{BTreeMap, BTreeSet};
107use std::error::Error;
108use std::fmt::{self, Debug};
109use std::marker::{self, PhantomData};
110use std::ops::{self, DerefMut, Range};
111use std::path::Path;
112use std::pin::Pin;
113use std::sync::Arc;
114use std::time::Duration;
115
116use bitcoin::hex::DisplayHex as _;
117use fedimint_core::util::BoxFuture;
118use fedimint_logging::LOG_DB;
119use fedimint_util_error::FmtCompact as _;
120use futures::{Stream, StreamExt};
121use macro_rules_attribute::apply;
122use rand::Rng;
123use serde::Serialize;
124use strum_macros::EnumIter;
125use thiserror::Error;
126use tracing::{debug, info, instrument, trace, warn};
127
128use crate::core::{ModuleInstanceId, ModuleKind};
129use crate::encoding::{Decodable, DecodeError, Encodable};
130use crate::fmt_utils::AbbreviateHexBytes;
131use crate::task::{MaybeSend, MaybeSync};
132use crate::{async_trait_maybe_send, maybe_add_send, maybe_add_send_sync, timing};
133
134pub mod mem_impl;
135pub mod notifications;
136
137pub use test_utils::*;
138
139use self::notifications::{Notifications, NotifyQueue};
140use crate::module::registry::{ModuleDecoderRegistry, ModuleRegistry};
141
142pub const MODULE_GLOBAL_PREFIX: u8 = 0xff;
143
144/// Result type for database operations
145pub type DatabaseResult<T> = std::result::Result<T, DatabaseError>;
146
147pub trait DatabaseKeyPrefix: Debug {
148    fn to_bytes(&self) -> Vec<u8>;
149}
150
151/// A key + value pair in the database with a unique prefix
152/// Extends `DatabaseKeyPrefix` to prepend the key's prefix.
153pub trait DatabaseRecord: DatabaseKeyPrefix {
154    const DB_PREFIX: u8;
155    const NOTIFY_ON_MODIFY: bool = false;
156    type Key: DatabaseKey + Debug;
157    type Value: DatabaseValue + Debug;
158}
159
160/// A key that can be used to query one or more `DatabaseRecord`
161/// Extends `DatabaseKeyPrefix` to prepend the key's prefix.
162pub trait DatabaseLookup: DatabaseKeyPrefix {
163    type Record: DatabaseRecord;
164}
165
166// Every `DatabaseRecord` is automatically a `DatabaseLookup`
167impl<Record> DatabaseLookup for Record
168where
169    Record: DatabaseRecord + Debug + Decodable + Encodable,
170{
171    type Record = Record;
172}
173
174/// `DatabaseKey` that represents the lookup structure for retrieving key/value
175/// pairs from the database.
176pub trait DatabaseKey: Sized {
177    /// Send a notification to tasks waiting to be notified if the value of
178    /// `DatabaseKey` is modified
179    ///
180    /// For instance, this can be used to be notified when a key in the
181    /// database is created. It is also possible to run a closure with the
182    /// value of the `DatabaseKey` as parameter to verify some changes to
183    /// that value.
184    const NOTIFY_ON_MODIFY: bool = false;
185    fn from_bytes(
186        data: &[u8],
187        modules: &ModuleDecoderRegistry,
188    ) -> std::result::Result<Self, DecodingError>;
189}
190
191/// Marker trait for `DatabaseKey`s where `NOTIFY` is true
192pub trait DatabaseKeyWithNotify {}
193
194/// `DatabaseValue` that represents the value structure of database records.
195pub trait DatabaseValue: Sized + Debug {
196    fn from_bytes(
197        data: &[u8],
198        modules: &ModuleDecoderRegistry,
199    ) -> std::result::Result<Self, DecodingError>;
200    fn to_bytes(&self) -> Vec<u8>;
201}
202
203pub type PrefixStream<'a> = Pin<Box<maybe_add_send!(dyn Stream<Item = (Vec<u8>, Vec<u8>)> + 'a)>>;
204
205/// Just ignore this type, it's only there to make compiler happy
206///
207/// See <https://users.rust-lang.org/t/argument-requires-that-is-borrowed-for-static/66503/2?u=yandros> for details.
208pub type PhantomBound<'big, 'small> = PhantomData<&'small &'big ()>;
209
210/// Error returned when the autocommit function fails
211#[derive(Debug, Error)]
212pub enum AutocommitError<E> {
213    /// Committing the transaction failed too many times, giving up
214    #[error("Commit Failed: {last_error}")]
215    CommitFailed {
216        /// Number of attempts
217        attempts: usize,
218        /// Last error on commit
219        last_error: DatabaseError,
220    },
221    /// Error returned by the closure provided to `autocommit`. If returned no
222    /// commit was attempted in that round
223    #[error("Closure error: {error}")]
224    ClosureError {
225        /// The attempt on which the closure returned an error
226        ///
227        /// Values other than 0 typically indicate a logic error since the
228        /// closure given to `autocommit` should not have side effects
229        /// and thus keep succeeding if it succeeded once.
230        attempts: usize,
231        /// Error returned by the closure
232        error: E,
233    },
234}
235
236pub trait AutocommitResultExt<T, E> {
237    /// Unwraps the "commit failed" error variant. Use this in cases where
238    /// autocommit is instructed to run indefinitely and commit will thus never
239    /// fail.
240    fn unwrap_autocommit(self) -> std::result::Result<T, E>;
241}
242
243impl<T, E> AutocommitResultExt<T, E> for std::result::Result<T, AutocommitError<E>> {
244    fn unwrap_autocommit(self) -> std::result::Result<T, E> {
245        match self {
246            Ok(value) => Ok(value),
247            Err(AutocommitError::CommitFailed { .. }) => {
248                panic!("`unwrap_autocommit` called on a autocommit result with finite retries");
249            }
250            Err(AutocommitError::ClosureError { error, .. }) => Err(error),
251        }
252    }
253}
254
255/// Raw database implementation
256///
257/// This and [`IRawDatabaseTransaction`] are meant to be implemented
258/// by crates like `fedimint-rocksdb` to provide a concrete implementation
259/// of a database to be used by Fedimint.
260///
261/// This is in contrast of [`IDatabase`] which includes extra
262/// functionality that Fedimint needs (and adds) on top of it.
263#[apply(async_trait_maybe_send!)]
264pub trait IRawDatabase: Debug + MaybeSend + MaybeSync + 'static {
265    /// A raw database transaction type
266    type Transaction<'a>: IRawDatabaseTransaction + Debug;
267
268    /// Start a database transaction
269    async fn begin_transaction<'a>(&'a self) -> Self::Transaction<'a>;
270
271    // Checkpoint the database to a backup directory
272    fn checkpoint(&self, backup_path: &Path) -> DatabaseResult<()>;
273}
274
275#[apply(async_trait_maybe_send!)]
276impl<T> IRawDatabase for Box<T>
277where
278    T: IRawDatabase,
279{
280    type Transaction<'a> = <T as IRawDatabase>::Transaction<'a>;
281
282    async fn begin_transaction<'a>(&'a self) -> Self::Transaction<'a> {
283        (**self).begin_transaction().await
284    }
285
286    fn checkpoint(&self, backup_path: &Path) -> DatabaseResult<()> {
287        (**self).checkpoint(backup_path)
288    }
289}
290
291/// An extension trait with convenience operations on [`IRawDatabase`]
292pub trait IRawDatabaseExt: IRawDatabase + Sized {
293    /// Convert to type implementing [`IRawDatabase`] into [`Database`].
294    ///
295    /// When type inference is not an issue, [`Into::into`] can be used instead.
296    fn into_database(self) -> Database {
297        Database::new(self, ModuleRegistry::default())
298    }
299}
300
301impl<T> IRawDatabaseExt for T where T: IRawDatabase {}
302
303impl<T> From<T> for Database
304where
305    T: IRawDatabase,
306{
307    fn from(raw: T) -> Self {
308        Self::new(raw, ModuleRegistry::default())
309    }
310}
311
312/// A database that on top of a raw database operation, implements
313/// key notification system.
314#[apply(async_trait_maybe_send!)]
315pub trait IDatabase: Debug + MaybeSend + MaybeSync + 'static {
316    /// Start a database transaction
317    async fn begin_transaction<'a>(&'a self) -> Box<dyn IDatabaseTransaction + 'a>;
318    /// Register for `key` updates, returning a future that waits for an update.
319    ///
320    /// Await registration before taking the database snapshot used to check a
321    /// condition. Once registration completes, updates must be remembered even
322    /// before the returned future is polled. The future may wake spuriously;
323    /// register again before checking a fresh snapshot after each wake.
324    ///
325    /// The returned future borrows the database, not the key. Dropping it
326    /// cancels the wait.
327    async fn register<'a>(&'a self, key: &[u8]) -> BoxFuture<'a, ()>;
328    /// Notify about `key` update (creation, modification, deletion)
329    async fn notify(&self, key: &[u8]);
330
331    /// The prefix len of this database refers to the global (as opposed to
332    /// module-isolated) key space
333    fn is_global(&self) -> bool;
334
335    /// Checkpoints the database to a backup directory
336    fn checkpoint(&self, backup_path: &Path) -> DatabaseResult<()>;
337}
338
339#[apply(async_trait_maybe_send!)]
340impl<T> IDatabase for Arc<T>
341where
342    T: IDatabase + ?Sized,
343{
344    async fn begin_transaction<'a>(&'a self) -> Box<dyn IDatabaseTransaction + 'a> {
345        (**self).begin_transaction().await
346    }
347    async fn register<'a>(&'a self, key: &[u8]) -> BoxFuture<'a, ()> {
348        (**self).register(key).await
349    }
350    async fn notify(&self, key: &[u8]) {
351        (**self).notify(key).await;
352    }
353
354    fn is_global(&self) -> bool {
355        (**self).is_global()
356    }
357
358    fn checkpoint(&self, backup_path: &Path) -> DatabaseResult<()> {
359        (**self).checkpoint(backup_path)
360    }
361}
362
363/// Base functionality around [`IRawDatabase`] to make it a [`IDatabase`]
364///
365/// Mostly notification system, but also run-time single-commit handling.
366struct BaseDatabase<RawDatabase> {
367    notifications: Arc<Notifications>,
368    raw: RawDatabase,
369}
370
371impl<RawDatabase> fmt::Debug for BaseDatabase<RawDatabase> {
372    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
373        f.write_str("BaseDatabase")
374    }
375}
376
377#[apply(async_trait_maybe_send!)]
378impl<RawDatabase: IRawDatabase + MaybeSend + 'static> IDatabase for BaseDatabase<RawDatabase> {
379    async fn begin_transaction<'a>(&'a self) -> Box<dyn IDatabaseTransaction + 'a> {
380        Box::new(BaseDatabaseTransaction::new(
381            self.raw.begin_transaction().await,
382            self.notifications.clone(),
383        ))
384    }
385    async fn register<'a>(&'a self, key: &[u8]) -> BoxFuture<'a, ()> {
386        Box::pin(self.notifications.register(key))
387    }
388    async fn notify(&self, key: &[u8]) {
389        self.notifications.notify(key);
390    }
391
392    fn is_global(&self) -> bool {
393        true
394    }
395
396    fn checkpoint(&self, backup_path: &Path) -> DatabaseResult<()> {
397        self.raw.checkpoint(backup_path)
398    }
399}
400
401/// A public-facing newtype over `IDatabase`
402///
403/// Notably carries set of module decoders (`ModuleDecoderRegistry`)
404/// and implements common utility function for auto-commits, db isolation,
405/// and other.
406#[derive(Clone, Debug)]
407pub struct Database {
408    inner: Arc<dyn IDatabase + 'static>,
409    module_decoders: ModuleDecoderRegistry,
410}
411
412impl Database {
413    pub fn strong_count(&self) -> usize {
414        Arc::strong_count(&self.inner)
415    }
416
417    pub fn into_inner(self) -> Arc<dyn IDatabase + 'static> {
418        self.inner
419    }
420}
421
422impl Database {
423    /// Creates a new Fedimint database from any object implementing
424    /// [`IDatabase`].
425    ///
426    /// See also [`Database::new_from_arc`].
427    pub fn new(raw: impl IRawDatabase + 'static, module_decoders: ModuleDecoderRegistry) -> Self {
428        let inner = BaseDatabase {
429            raw,
430            notifications: Arc::new(Notifications::new()),
431        };
432        Self::new_from_arc(
433            Arc::new(inner) as Arc<dyn IDatabase + 'static>,
434            module_decoders,
435        )
436    }
437
438    /// Create [`Database`] from an already typed-erased `IDatabase`.
439    pub fn new_from_arc(
440        inner: Arc<dyn IDatabase + 'static>,
441        module_decoders: ModuleDecoderRegistry,
442    ) -> Self {
443        Self {
444            inner,
445            module_decoders,
446        }
447    }
448
449    /// Create [`Database`] isolated to a partition with a given `prefix`
450    pub fn with_prefix(&self, prefix: Vec<u8>) -> Self {
451        Self {
452            inner: Arc::new(PrefixDatabase {
453                inner: self.inner.clone(),
454                global_dbtx_access_token: None,
455                prefix,
456            }),
457            module_decoders: self.module_decoders.clone(),
458        }
459    }
460
461    /// Create [`Database`] isolated to a partition with a prefix for a given
462    /// `module_instance_id`, allowing the module to access `global_dbtx` with
463    /// the right `access_token`
464    pub fn with_prefix_module_id(
465        &self,
466        module_instance_id: ModuleInstanceId,
467    ) -> (Self, GlobalDBTxAccessToken) {
468        let prefix = module_instance_id_to_byte_prefix(module_instance_id);
469        let global_dbtx_access_token = GlobalDBTxAccessToken::from_prefix(&prefix);
470        (
471            Self {
472                inner: Arc::new(PrefixDatabase {
473                    inner: self.inner.clone(),
474                    global_dbtx_access_token: Some(global_dbtx_access_token),
475                    prefix,
476                }),
477                module_decoders: self.module_decoders.clone(),
478            },
479            global_dbtx_access_token,
480        )
481    }
482
483    pub fn with_decoders(&self, module_decoders: ModuleDecoderRegistry) -> Self {
484        Self {
485            inner: self.inner.clone(),
486            module_decoders,
487        }
488    }
489
490    /// Is this `Database` a global, unpartitioned `Database`
491    pub fn is_global(&self) -> bool {
492        self.inner.is_global()
493    }
494
495    /// `Err` if [`Self::is_global`] is not true
496    pub fn ensure_global(&self) -> DatabaseResult<()> {
497        if !self.is_global() {
498            return Err(DatabaseError::NotGlobal);
499        }
500
501        Ok(())
502    }
503
504    /// `Err` if [`Self::is_global`] is true
505    pub fn ensure_isolated(&self) -> DatabaseResult<()> {
506        if self.is_global() {
507            return Err(DatabaseError::NotIsolated);
508        }
509
510        Ok(())
511    }
512
513    /// Begin a new committable database transaction
514    pub async fn begin_transaction<'s, 'tx>(&'s self) -> DatabaseTransaction<'tx, Committable>
515    where
516        's: 'tx,
517    {
518        DatabaseTransaction::<Committable>::new(
519            self.inner.begin_transaction().await,
520            self.module_decoders.clone(),
521        )
522    }
523
524    /// Begin a new non-committable database transaction
525    pub async fn begin_transaction_nc<'s, 'tx>(&'s self) -> DatabaseTransaction<'tx, NonCommittable>
526    where
527        's: 'tx,
528    {
529        self.begin_transaction().await.into_nc()
530    }
531
532    pub fn checkpoint(&self, backup_path: &Path) -> DatabaseResult<()> {
533        self.inner.checkpoint(backup_path)
534    }
535
536    /// Runs a closure with a reference to a database transaction and tries to
537    /// commit the transaction if the closure returns `Ok` and rolls it back
538    /// otherwise. If committing fails the closure is run for up to
539    /// `max_attempts` times. If `max_attempts` is `None` it will run
540    /// `usize::MAX` times which is close enough to infinite times.
541    ///
542    /// The closure `tx_fn` provided should not have side effects outside of the
543    /// database transaction provided, or if it does these should be
544    /// idempotent, since the closure might be run multiple times.
545    ///
546    /// # Lifetime Parameters
547    ///
548    /// The higher rank trait bound (HRTB) `'a` that is applied to the the
549    /// mutable reference to the database transaction ensures that the
550    /// reference lives as least as long as the returned future of the
551    /// closure.
552    ///
553    /// Further, the reference to self (`'s`) must outlive the
554    /// `DatabaseTransaction<'dt>`. In other words, the
555    /// `DatabaseTransaction` must live as least as long as `self` and that is
556    /// true as the `DatabaseTransaction` is only dropped at the end of the
557    /// `loop{}`.
558    ///
559    /// # Panics
560    ///
561    /// This function panics when the given number of maximum attempts is zero.
562    /// `max_attempts` must be greater or equal to one.
563    pub async fn autocommit<'s, 'dbtx, F, T, E>(
564        &'s self,
565        tx_fn: F,
566        max_attempts: Option<usize>,
567    ) -> std::result::Result<T, AutocommitError<E>>
568    where
569        's: 'dbtx,
570        for<'r, 'o> F: Fn(
571            &'r mut DatabaseTransaction<'o>,
572            PhantomBound<'dbtx, 'o>,
573        ) -> BoxFuture<'r, std::result::Result<T, E>>,
574    {
575        assert_ne!(max_attempts, Some(0));
576        let mut curr_attempts: usize = 0;
577
578        loop {
579            // The `checked_add()` function is used to catch the `usize` overflow.
580            // With `usize=32bit` and an assumed time of 1ms per iteration, this would crash
581            // after ~50 days. But if that's the case, something else must be wrong.
582            // With `usize=64bit` it would take much longer, obviously.
583            curr_attempts = curr_attempts
584                .checked_add(1)
585                .expect("db autocommit attempt counter overflowed");
586
587            let mut dbtx = self.begin_transaction().await;
588
589            let tx_fn_res = tx_fn(&mut dbtx.to_ref_nc(), PhantomData).await;
590            let val = match tx_fn_res {
591                Ok(val) => val,
592                Err(err) => {
593                    dbtx.ignore_uncommitted();
594                    return Err(AutocommitError::ClosureError {
595                        attempts: curr_attempts,
596                        error: err,
597                    });
598                }
599            };
600
601            let _timing /* logs on drop */ = timing::TimeReporter::new("autocommit - commit_tx");
602
603            match dbtx.commit_tx_result().await {
604                Ok(()) => {
605                    return Ok(val);
606                }
607                Err(err) => {
608                    if max_attempts.is_some_and(|max_att| max_att <= curr_attempts) {
609                        warn!(
610                            target: LOG_DB,
611                            curr_attempts,
612                            err = %err.fmt_compact(),
613                            "Database commit failed in an autocommit block - terminating"
614                        );
615                        return Err(AutocommitError::CommitFailed {
616                            attempts: curr_attempts,
617                            last_error: err,
618                        });
619                    }
620
621                    let delay = (2u64.pow(curr_attempts.min(7) as u32) * 10).min(1000);
622                    let delay = rand::thread_rng().gen_range(delay..(2 * delay));
623                    warn!(
624                        target: LOG_DB,
625                        curr_attempts,
626                        err = %err.fmt_compact(),
627                        delay_ms = %delay,
628                        "Database commit failed in an autocommit block - retrying"
629                    );
630                    crate::runtime::sleep(Duration::from_millis(delay)).await;
631                }
632            }
633        }
634    }
635
636    /// Waits for key to be notified.
637    ///
638    /// Calls the `checker` when value of the key may have changed.
639    /// Returns the value when `checker` returns a `Some(T)`.
640    pub async fn wait_key_check<'a, K, T>(
641        &'a self,
642        key: &K,
643        checker: impl Fn(Option<K::Value>) -> Option<T>,
644    ) -> (T, DatabaseTransaction<'a, Committable>)
645    where
646        K: DatabaseKey + DatabaseRecord + DatabaseKeyWithNotify,
647    {
648        let key_bytes = key.to_bytes();
649        loop {
650            // register for notification
651            let notify = self.inner.register(&key_bytes).await;
652
653            // check for value in db
654            let mut tx = self.inner.begin_transaction().await;
655
656            let maybe_value_bytes = tx
657                .raw_get_bytes(&key_bytes)
658                .await
659                .expect("Unrecoverable error when reading from database")
660                .map(|value_bytes| {
661                    decode_value_expect(&value_bytes, &self.module_decoders, &key_bytes)
662                });
663
664            if let Some(value) = checker(maybe_value_bytes) {
665                return (
666                    value,
667                    DatabaseTransaction::new(tx, self.module_decoders.clone()),
668                );
669            }
670
671            // key not found, try again
672            notify.await;
673            // if miss a notification between await and next register, it is
674            // fine. because we are going check the database
675        }
676    }
677
678    /// Waits for key to be present in database.
679    pub async fn wait_key_exists<K>(&self, key: &K) -> K::Value
680    where
681        K: DatabaseKey + DatabaseRecord + DatabaseKeyWithNotify,
682    {
683        self.wait_key_check(key, std::convert::identity).await.0
684    }
685}
686
687fn module_instance_id_to_byte_prefix(module_instance_id: u16) -> Vec<u8> {
688    let mut bytes = vec![MODULE_GLOBAL_PREFIX];
689    bytes.append(&mut module_instance_id.consensus_encode_to_vec());
690    bytes
691}
692
693/// A database that wraps an `inner` one and adds a prefix to all operations,
694/// effectively creating an isolated partition.
695#[derive(Clone, Debug)]
696struct PrefixDatabase<Inner>
697where
698    Inner: Debug,
699{
700    prefix: Vec<u8>,
701    global_dbtx_access_token: Option<GlobalDBTxAccessToken>,
702    inner: Inner,
703}
704
705impl<Inner> PrefixDatabase<Inner>
706where
707    Inner: Debug,
708{
709    // TODO: we should optimize these concatenations, maybe by having an internal
710    // `key: &[&[u8]]` that we flatten once, when passing to lowest layer, or
711    // something
712    fn get_full_key(&self, key: &[u8]) -> Vec<u8> {
713        let mut full_key = self.prefix.clone();
714        full_key.extend_from_slice(key);
715        full_key
716    }
717}
718
719#[apply(async_trait_maybe_send!)]
720impl<Inner> IDatabase for PrefixDatabase<Inner>
721where
722    Inner: Debug + MaybeSend + MaybeSync + 'static + IDatabase,
723{
724    async fn begin_transaction<'a>(&'a self) -> Box<dyn IDatabaseTransaction + 'a> {
725        Box::new(PrefixDatabaseTransaction {
726            inner: self.inner.begin_transaction().await,
727            global_dbtx_access_token: self.global_dbtx_access_token,
728            prefix: self.prefix.clone(),
729        })
730    }
731    async fn register<'a>(&'a self, key: &[u8]) -> BoxFuture<'a, ()> {
732        self.inner.register(&self.get_full_key(key)).await
733    }
734
735    async fn notify(&self, key: &[u8]) {
736        self.inner.notify(&self.get_full_key(key)).await;
737    }
738
739    fn is_global(&self) -> bool {
740        if self.global_dbtx_access_token.is_some() {
741            false
742        } else {
743            self.inner.is_global()
744        }
745    }
746
747    fn checkpoint(&self, backup_path: &Path) -> DatabaseResult<()> {
748        self.inner.checkpoint(backup_path)
749    }
750}
751
752/// A database transactions that wraps an `inner` one and adds a prefix to all
753/// operations, effectively creating an isolated partition.
754///
755/// Produced by [`PrefixDatabase`].
756#[derive(Debug)]
757struct PrefixDatabaseTransaction<Inner> {
758    inner: Inner,
759    global_dbtx_access_token: Option<GlobalDBTxAccessToken>,
760    prefix: Vec<u8>,
761}
762
763impl<Inner> PrefixDatabaseTransaction<Inner> {
764    // TODO: we should optimize these concatenations, maybe by having an internal
765    // `key: &[&[u8]]` that we flatten once, when passing to lowest layer, or
766    // something
767    fn get_full_key(&self, key: &[u8]) -> Vec<u8> {
768        let mut full_key = self.prefix.clone();
769        full_key.extend_from_slice(key);
770        full_key
771    }
772
773    fn get_full_range(&self, range: Range<&[u8]>) -> Range<Vec<u8>> {
774        Range {
775            start: self.get_full_key(range.start),
776            end: self.get_full_key(range.end),
777        }
778    }
779
780    fn adapt_prefix_stream(stream: PrefixStream<'_>, prefix_len: usize) -> PrefixStream<'_> {
781        Box::pin(stream.map(move |(k, v)| (k[prefix_len..].to_owned(), v)))
782    }
783}
784
785#[apply(async_trait_maybe_send!)]
786impl<Inner> IDatabaseTransaction for PrefixDatabaseTransaction<Inner>
787where
788    Inner: IDatabaseTransaction,
789{
790    async fn commit_tx(&mut self) -> DatabaseResult<()> {
791        self.inner.commit_tx().await
792    }
793
794    fn is_global(&self) -> bool {
795        if self.global_dbtx_access_token.is_some() {
796            false
797        } else {
798            self.inner.is_global()
799        }
800    }
801
802    fn global_dbtx(
803        &mut self,
804        access_token: GlobalDBTxAccessToken,
805    ) -> &mut dyn IDatabaseTransaction {
806        if let Some(self_global_dbtx_access_token) = self.global_dbtx_access_token {
807            assert_eq!(
808                access_token, self_global_dbtx_access_token,
809                "Invalid access key used to access global_dbtx"
810            );
811            &mut self.inner
812        } else {
813            self.inner.global_dbtx(access_token)
814        }
815    }
816}
817
818#[apply(async_trait_maybe_send!)]
819impl<Inner> IDatabaseTransactionOpsCore for PrefixDatabaseTransaction<Inner>
820where
821    Inner: IDatabaseTransactionOpsCore,
822{
823    async fn raw_insert_bytes(
824        &mut self,
825        key: &[u8],
826        value: &[u8],
827    ) -> DatabaseResult<Option<Vec<u8>>> {
828        let key = self.get_full_key(key);
829        self.inner.raw_insert_bytes(&key, value).await
830    }
831
832    async fn raw_get_bytes(&mut self, key: &[u8]) -> DatabaseResult<Option<Vec<u8>>> {
833        let key = self.get_full_key(key);
834        self.inner.raw_get_bytes(&key).await
835    }
836
837    async fn raw_remove_entry(&mut self, key: &[u8]) -> DatabaseResult<Option<Vec<u8>>> {
838        let key = self.get_full_key(key);
839        self.inner.raw_remove_entry(&key).await
840    }
841
842    async fn raw_find_by_prefix(&mut self, key_prefix: &[u8]) -> DatabaseResult<PrefixStream<'_>> {
843        let key = self.get_full_key(key_prefix);
844        let stream = self.inner.raw_find_by_prefix(&key).await?;
845        Ok(Self::adapt_prefix_stream(stream, self.prefix.len()))
846    }
847
848    async fn raw_find_by_prefix_sorted_descending(
849        &mut self,
850        key_prefix: &[u8],
851    ) -> DatabaseResult<PrefixStream<'_>> {
852        let key = self.get_full_key(key_prefix);
853        let stream = self
854            .inner
855            .raw_find_by_prefix_sorted_descending(&key)
856            .await?;
857        Ok(Self::adapt_prefix_stream(stream, self.prefix.len()))
858    }
859
860    async fn raw_find_by_range(&mut self, range: Range<&[u8]>) -> DatabaseResult<PrefixStream<'_>> {
861        let range = self.get_full_range(range);
862        let stream = self
863            .inner
864            .raw_find_by_range(Range {
865                start: &range.start,
866                end: &range.end,
867            })
868            .await?;
869        Ok(Self::adapt_prefix_stream(stream, self.prefix.len()))
870    }
871
872    async fn raw_remove_by_prefix(&mut self, key_prefix: &[u8]) -> DatabaseResult<()> {
873        let key = self.get_full_key(key_prefix);
874        self.inner.raw_remove_by_prefix(&key).await
875    }
876}
877
878impl<Inner> IDatabaseTransactionOps for PrefixDatabaseTransaction<Inner> where
879    Inner: IDatabaseTransactionOps
880{
881}
882
883/// Core raw a operations database transactions supports
884///
885/// Used to enforce the same signature on all types supporting it
886#[apply(async_trait_maybe_send!)]
887pub trait IDatabaseTransactionOpsCore: MaybeSend {
888    /// Insert entry
889    async fn raw_insert_bytes(
890        &mut self,
891        key: &[u8],
892        value: &[u8],
893    ) -> DatabaseResult<Option<Vec<u8>>>;
894
895    /// Get key value
896    async fn raw_get_bytes(&mut self, key: &[u8]) -> DatabaseResult<Option<Vec<u8>>>;
897
898    /// Remove entry by `key`
899    async fn raw_remove_entry(&mut self, key: &[u8]) -> DatabaseResult<Option<Vec<u8>>>;
900
901    /// Returns an stream of key-value pairs with keys that start with
902    /// `key_prefix`, sorted by key.
903    async fn raw_find_by_prefix(&mut self, key_prefix: &[u8]) -> DatabaseResult<PrefixStream<'_>>;
904
905    /// Same as [`Self::raw_find_by_prefix`] but the order is descending by key.
906    async fn raw_find_by_prefix_sorted_descending(
907        &mut self,
908        key_prefix: &[u8],
909    ) -> DatabaseResult<PrefixStream<'_>>;
910
911    /// Returns an stream of key-value pairs with keys within a `range`, sorted
912    /// by key. [`Range`] is an (half-open) range bounded inclusively below and
913    /// exclusively above.
914    async fn raw_find_by_range(&mut self, range: Range<&[u8]>) -> DatabaseResult<PrefixStream<'_>>;
915
916    /// Delete keys matching prefix
917    async fn raw_remove_by_prefix(&mut self, key_prefix: &[u8]) -> DatabaseResult<()>;
918}
919
920#[apply(async_trait_maybe_send!)]
921impl<T> IDatabaseTransactionOpsCore for Box<T>
922where
923    T: IDatabaseTransactionOpsCore + ?Sized,
924{
925    async fn raw_insert_bytes(
926        &mut self,
927        key: &[u8],
928        value: &[u8],
929    ) -> DatabaseResult<Option<Vec<u8>>> {
930        (**self).raw_insert_bytes(key, value).await
931    }
932
933    async fn raw_get_bytes(&mut self, key: &[u8]) -> DatabaseResult<Option<Vec<u8>>> {
934        (**self).raw_get_bytes(key).await
935    }
936
937    async fn raw_remove_entry(&mut self, key: &[u8]) -> DatabaseResult<Option<Vec<u8>>> {
938        (**self).raw_remove_entry(key).await
939    }
940
941    async fn raw_find_by_prefix(&mut self, key_prefix: &[u8]) -> DatabaseResult<PrefixStream<'_>> {
942        (**self).raw_find_by_prefix(key_prefix).await
943    }
944
945    async fn raw_find_by_prefix_sorted_descending(
946        &mut self,
947        key_prefix: &[u8],
948    ) -> DatabaseResult<PrefixStream<'_>> {
949        (**self)
950            .raw_find_by_prefix_sorted_descending(key_prefix)
951            .await
952    }
953
954    async fn raw_find_by_range(&mut self, range: Range<&[u8]>) -> DatabaseResult<PrefixStream<'_>> {
955        (**self).raw_find_by_range(range).await
956    }
957
958    async fn raw_remove_by_prefix(&mut self, key_prefix: &[u8]) -> DatabaseResult<()> {
959        (**self).raw_remove_by_prefix(key_prefix).await
960    }
961}
962
963#[apply(async_trait_maybe_send!)]
964impl<T> IDatabaseTransactionOpsCore for &mut T
965where
966    T: IDatabaseTransactionOpsCore + ?Sized,
967{
968    async fn raw_insert_bytes(
969        &mut self,
970        key: &[u8],
971        value: &[u8],
972    ) -> DatabaseResult<Option<Vec<u8>>> {
973        (**self).raw_insert_bytes(key, value).await
974    }
975
976    async fn raw_get_bytes(&mut self, key: &[u8]) -> DatabaseResult<Option<Vec<u8>>> {
977        (**self).raw_get_bytes(key).await
978    }
979
980    async fn raw_remove_entry(&mut self, key: &[u8]) -> DatabaseResult<Option<Vec<u8>>> {
981        (**self).raw_remove_entry(key).await
982    }
983
984    async fn raw_find_by_prefix(&mut self, key_prefix: &[u8]) -> DatabaseResult<PrefixStream<'_>> {
985        (**self).raw_find_by_prefix(key_prefix).await
986    }
987
988    async fn raw_find_by_prefix_sorted_descending(
989        &mut self,
990        key_prefix: &[u8],
991    ) -> DatabaseResult<PrefixStream<'_>> {
992        (**self)
993            .raw_find_by_prefix_sorted_descending(key_prefix)
994            .await
995    }
996
997    async fn raw_find_by_range(&mut self, range: Range<&[u8]>) -> DatabaseResult<PrefixStream<'_>> {
998        (**self).raw_find_by_range(range).await
999    }
1000
1001    async fn raw_remove_by_prefix(&mut self, key_prefix: &[u8]) -> DatabaseResult<()> {
1002        (**self).raw_remove_by_prefix(key_prefix).await
1003    }
1004}
1005
1006/// Additional operations (only some) database transactions expose, on top of
1007/// [`IDatabaseTransactionOpsCore`]
1008///
1009/// In certain contexts exposing these operations would be a problem, so they
1010/// are moved to a separate trait.
1011pub trait IDatabaseTransactionOps: IDatabaseTransactionOpsCore + MaybeSend {}
1012
1013impl<T> IDatabaseTransactionOps for Box<T> where T: IDatabaseTransactionOps + ?Sized {}
1014
1015impl<T> IDatabaseTransactionOps for &mut T where T: IDatabaseTransactionOps + ?Sized {}
1016
1017/// Like [`IDatabaseTransactionOpsCore`], but typed
1018///
1019/// Implemented via blanket impl for everything that implements
1020/// [`IDatabaseTransactionOpsCore`] that has decoders (implements
1021/// [`WithDecoders`]).
1022#[apply(async_trait_maybe_send!)]
1023pub trait IDatabaseTransactionOpsCoreTyped<'a> {
1024    async fn get_value<K>(&mut self, key: &K) -> Option<K::Value>
1025    where
1026        K: DatabaseKey + DatabaseRecord + MaybeSend + MaybeSync;
1027
1028    async fn insert_entry<K>(&mut self, key: &K, value: &K::Value) -> Option<K::Value>
1029    where
1030        K: DatabaseKey + DatabaseRecord + MaybeSend + MaybeSync,
1031        K::Value: MaybeSend + MaybeSync;
1032
1033    async fn insert_new_entry<K>(&mut self, key: &K, value: &K::Value)
1034    where
1035        K: DatabaseKey + DatabaseRecord + MaybeSend + MaybeSync,
1036        K::Value: MaybeSend + MaybeSync;
1037
1038    async fn find_by_range<K>(
1039        &mut self,
1040        key_range: Range<K>,
1041    ) -> Pin<Box<maybe_add_send!(dyn Stream<Item = (K, K::Value)> + '_)>>
1042    where
1043        K: DatabaseKey + DatabaseRecord + MaybeSend + MaybeSync,
1044        K::Value: MaybeSend + MaybeSync;
1045
1046    async fn find_by_prefix<KP>(
1047        &mut self,
1048        key_prefix: &KP,
1049    ) -> Pin<
1050        Box<
1051            maybe_add_send!(
1052                dyn Stream<
1053                        Item = (
1054                            KP::Record,
1055                            <<KP as DatabaseLookup>::Record as DatabaseRecord>::Value,
1056                        ),
1057                    > + '_
1058            ),
1059        >,
1060    >
1061    where
1062        KP: DatabaseLookup + MaybeSend + MaybeSync,
1063        KP::Record: DatabaseKey;
1064
1065    async fn find_by_prefix_sorted_descending<KP>(
1066        &mut self,
1067        key_prefix: &KP,
1068    ) -> Pin<
1069        Box<
1070            maybe_add_send!(
1071                dyn Stream<
1072                        Item = (
1073                            KP::Record,
1074                            <<KP as DatabaseLookup>::Record as DatabaseRecord>::Value,
1075                        ),
1076                    > + '_
1077            ),
1078        >,
1079    >
1080    where
1081        KP: DatabaseLookup + MaybeSend + MaybeSync,
1082        KP::Record: DatabaseKey;
1083
1084    async fn remove_entry<K>(&mut self, key: &K) -> Option<K::Value>
1085    where
1086        K: DatabaseKey + DatabaseRecord + MaybeSend + MaybeSync;
1087
1088    async fn remove_by_prefix<KP>(&mut self, key_prefix: &KP)
1089    where
1090        KP: DatabaseLookup + MaybeSend + MaybeSync;
1091}
1092
1093// blanket implementation of typed ops for anything that implements raw ops and
1094// has decoders
1095#[apply(async_trait_maybe_send!)]
1096impl<T> IDatabaseTransactionOpsCoreTyped<'_> for T
1097where
1098    T: IDatabaseTransactionOpsCore + WithDecoders,
1099{
1100    async fn get_value<K>(&mut self, key: &K) -> Option<K::Value>
1101    where
1102        K: DatabaseKey + DatabaseRecord + MaybeSend + MaybeSync,
1103    {
1104        let key_bytes = key.to_bytes();
1105        let raw = self
1106            .raw_get_bytes(&key_bytes)
1107            .await
1108            .expect("Unrecoverable error occurred while reading and entry from the database");
1109        raw.map(|value_bytes| {
1110            decode_value_expect::<K::Value>(&value_bytes, self.decoders(), &key_bytes)
1111        })
1112    }
1113
1114    async fn insert_entry<K>(&mut self, key: &K, value: &K::Value) -> Option<K::Value>
1115    where
1116        K: DatabaseKey + DatabaseRecord + MaybeSend + MaybeSync,
1117        K::Value: MaybeSend + MaybeSync,
1118    {
1119        let key_bytes = key.to_bytes();
1120        self.raw_insert_bytes(&key_bytes, &value.to_bytes())
1121            .await
1122            .expect("Unrecoverable error occurred while inserting entry into the database")
1123            .map(|value_bytes| {
1124                decode_value_expect::<K::Value>(&value_bytes, self.decoders(), &key_bytes)
1125            })
1126    }
1127
1128    async fn insert_new_entry<K>(&mut self, key: &K, value: &K::Value)
1129    where
1130        K: DatabaseKey + DatabaseRecord + MaybeSend + MaybeSync,
1131        K::Value: MaybeSend + MaybeSync,
1132    {
1133        if let Some(prev) = self.insert_entry(key, value).await {
1134            panic!(
1135                "Database overwriting element when expecting insertion of new entry. Key: {key:?} Prev Value: {prev:?}"
1136            );
1137        }
1138    }
1139
1140    async fn find_by_range<K>(
1141        &mut self,
1142        key_range: Range<K>,
1143    ) -> Pin<Box<maybe_add_send!(dyn Stream<Item = (K, K::Value)> + '_)>>
1144    where
1145        K: DatabaseKey + DatabaseRecord + MaybeSend + MaybeSync,
1146        K::Value: MaybeSend + MaybeSync,
1147    {
1148        let decoders = self.decoders().clone();
1149        Box::pin(
1150            self.raw_find_by_range(Range {
1151                start: &key_range.start.to_bytes(),
1152                end: &key_range.end.to_bytes(),
1153            })
1154            .await
1155            .expect("Unrecoverable error occurred while listing entries from the database")
1156            .map(move |(key_bytes, value_bytes)| {
1157                let key = decode_key_expect(&key_bytes, &decoders);
1158                let value = decode_value_expect(&value_bytes, &decoders, &key_bytes);
1159                (key, value)
1160            }),
1161        )
1162    }
1163
1164    async fn find_by_prefix<KP>(
1165        &mut self,
1166        key_prefix: &KP,
1167    ) -> Pin<
1168        Box<
1169            maybe_add_send!(
1170                dyn Stream<
1171                        Item = (
1172                            KP::Record,
1173                            <<KP as DatabaseLookup>::Record as DatabaseRecord>::Value,
1174                        ),
1175                    > + '_
1176            ),
1177        >,
1178    >
1179    where
1180        KP: DatabaseLookup + MaybeSend + MaybeSync,
1181        KP::Record: DatabaseKey,
1182    {
1183        let decoders = self.decoders().clone();
1184        Box::pin(
1185            self.raw_find_by_prefix(&key_prefix.to_bytes())
1186                .await
1187                .expect("Unrecoverable error occurred while listing entries from the database")
1188                .map(move |(key_bytes, value_bytes)| {
1189                    let key = decode_key_expect(&key_bytes, &decoders);
1190                    let value = decode_value_expect(&value_bytes, &decoders, &key_bytes);
1191                    (key, value)
1192                }),
1193        )
1194    }
1195
1196    async fn find_by_prefix_sorted_descending<KP>(
1197        &mut self,
1198        key_prefix: &KP,
1199    ) -> Pin<
1200        Box<
1201            maybe_add_send!(
1202                dyn Stream<
1203                        Item = (
1204                            KP::Record,
1205                            <<KP as DatabaseLookup>::Record as DatabaseRecord>::Value,
1206                        ),
1207                    > + '_
1208            ),
1209        >,
1210    >
1211    where
1212        KP: DatabaseLookup + MaybeSend + MaybeSync,
1213        KP::Record: DatabaseKey,
1214    {
1215        let decoders = self.decoders().clone();
1216        Box::pin(
1217            self.raw_find_by_prefix_sorted_descending(&key_prefix.to_bytes())
1218                .await
1219                .expect("Unrecoverable error occurred while listing entries from the database")
1220                .map(move |(key_bytes, value_bytes)| {
1221                    let key = decode_key_expect(&key_bytes, &decoders);
1222                    let value = decode_value_expect(&value_bytes, &decoders, &key_bytes);
1223                    (key, value)
1224                }),
1225        )
1226    }
1227    async fn remove_entry<K>(&mut self, key: &K) -> Option<K::Value>
1228    where
1229        K: DatabaseKey + DatabaseRecord + MaybeSend + MaybeSync,
1230    {
1231        let key_bytes = key.to_bytes();
1232        self.raw_remove_entry(&key_bytes)
1233            .await
1234            .expect("Unrecoverable error occurred while inserting removing entry from the database")
1235            .map(|value_bytes| {
1236                decode_value_expect::<K::Value>(&value_bytes, self.decoders(), &key_bytes)
1237            })
1238    }
1239    async fn remove_by_prefix<KP>(&mut self, key_prefix: &KP)
1240    where
1241        KP: DatabaseLookup + MaybeSend + MaybeSync,
1242    {
1243        self.raw_remove_by_prefix(&key_prefix.to_bytes())
1244            .await
1245            .expect("Unrecoverable error when removing entries from the database");
1246    }
1247}
1248
1249/// A database type that has decoders, which allows it to implement
1250/// [`IDatabaseTransactionOpsCoreTyped`]
1251pub trait WithDecoders {
1252    fn decoders(&self) -> &ModuleDecoderRegistry;
1253}
1254
1255/// Raw database transaction (e.g. rocksdb implementation)
1256#[apply(async_trait_maybe_send!)]
1257pub trait IRawDatabaseTransaction: MaybeSend + IDatabaseTransactionOps {
1258    async fn commit_tx(self) -> DatabaseResult<()>;
1259}
1260
1261/// Fedimint database transaction
1262///
1263/// See [`IDatabase`] for more info.
1264#[apply(async_trait_maybe_send!)]
1265pub trait IDatabaseTransaction: MaybeSend + IDatabaseTransactionOps + fmt::Debug {
1266    /// Commit the transaction
1267    async fn commit_tx(&mut self) -> DatabaseResult<()>;
1268
1269    /// Is global database
1270    fn is_global(&self) -> bool;
1271
1272    /// Get the global database tx from a module-prefixed database transaction
1273    ///
1274    /// Meant to be called only by core internals, and module developers should
1275    /// not call it directly.
1276    #[doc(hidden)]
1277    fn global_dbtx(&mut self, access_token: GlobalDBTxAccessToken)
1278    -> &mut dyn IDatabaseTransaction;
1279}
1280
1281#[apply(async_trait_maybe_send!)]
1282impl<T> IDatabaseTransaction for Box<T>
1283where
1284    T: IDatabaseTransaction + ?Sized,
1285{
1286    async fn commit_tx(&mut self) -> DatabaseResult<()> {
1287        (**self).commit_tx().await
1288    }
1289
1290    fn is_global(&self) -> bool {
1291        (**self).is_global()
1292    }
1293
1294    fn global_dbtx(
1295        &mut self,
1296        access_token: GlobalDBTxAccessToken,
1297    ) -> &mut dyn IDatabaseTransaction {
1298        (**self).global_dbtx(access_token)
1299    }
1300}
1301
1302#[apply(async_trait_maybe_send!)]
1303impl<'a, T> IDatabaseTransaction for &'a mut T
1304where
1305    T: IDatabaseTransaction + ?Sized,
1306{
1307    async fn commit_tx(&mut self) -> DatabaseResult<()> {
1308        (**self).commit_tx().await
1309    }
1310
1311    fn is_global(&self) -> bool {
1312        (**self).is_global()
1313    }
1314
1315    fn global_dbtx(&mut self, access_key: GlobalDBTxAccessToken) -> &mut dyn IDatabaseTransaction {
1316        (**self).global_dbtx(access_key)
1317    }
1318}
1319
1320/// Struct that implements `IRawDatabaseTransaction` and can be wrapped
1321/// easier in other structs since it does not consumed `self` by move.
1322struct BaseDatabaseTransaction<Tx> {
1323    // TODO: merge options
1324    raw: Option<Tx>,
1325    notify_queue: Option<NotifyQueue>,
1326    notifications: Arc<Notifications>,
1327}
1328
1329impl<Tx> fmt::Debug for BaseDatabaseTransaction<Tx>
1330where
1331    Tx: fmt::Debug,
1332{
1333    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1334        f.write_fmt(format_args!(
1335            "BaseDatabaseTransaction{{ raw={:?} }}",
1336            self.raw
1337        ))
1338    }
1339}
1340impl<Tx> BaseDatabaseTransaction<Tx>
1341where
1342    Tx: IRawDatabaseTransaction,
1343{
1344    fn new(dbtx: Tx, notifications: Arc<Notifications>) -> Self {
1345        Self {
1346            raw: Some(dbtx),
1347            notifications,
1348            notify_queue: Some(NotifyQueue::new()),
1349        }
1350    }
1351
1352    fn add_notification_key(&mut self, key: &[u8]) -> DatabaseResult<()> {
1353        self.notify_queue
1354            .as_mut()
1355            .ok_or(DatabaseError::TransactionConsumed)?
1356            .add(key);
1357        Ok(())
1358    }
1359}
1360
1361#[apply(async_trait_maybe_send!)]
1362impl<Tx: IRawDatabaseTransaction> IDatabaseTransactionOpsCore for BaseDatabaseTransaction<Tx> {
1363    async fn raw_insert_bytes(
1364        &mut self,
1365        key: &[u8],
1366        value: &[u8],
1367    ) -> DatabaseResult<Option<Vec<u8>>> {
1368        self.add_notification_key(key)?;
1369        self.raw
1370            .as_mut()
1371            .ok_or(DatabaseError::TransactionConsumed)?
1372            .raw_insert_bytes(key, value)
1373            .await
1374    }
1375
1376    async fn raw_get_bytes(&mut self, key: &[u8]) -> DatabaseResult<Option<Vec<u8>>> {
1377        self.raw
1378            .as_mut()
1379            .ok_or(DatabaseError::TransactionConsumed)?
1380            .raw_get_bytes(key)
1381            .await
1382    }
1383
1384    async fn raw_remove_entry(&mut self, key: &[u8]) -> DatabaseResult<Option<Vec<u8>>> {
1385        self.add_notification_key(key)?;
1386        self.raw
1387            .as_mut()
1388            .ok_or(DatabaseError::TransactionConsumed)?
1389            .raw_remove_entry(key)
1390            .await
1391    }
1392
1393    async fn raw_find_by_range(
1394        &mut self,
1395        key_range: Range<&[u8]>,
1396    ) -> DatabaseResult<PrefixStream<'_>> {
1397        self.raw
1398            .as_mut()
1399            .ok_or(DatabaseError::TransactionConsumed)?
1400            .raw_find_by_range(key_range)
1401            .await
1402    }
1403
1404    async fn raw_find_by_prefix(&mut self, key_prefix: &[u8]) -> DatabaseResult<PrefixStream<'_>> {
1405        self.raw
1406            .as_mut()
1407            .ok_or(DatabaseError::TransactionConsumed)?
1408            .raw_find_by_prefix(key_prefix)
1409            .await
1410    }
1411
1412    async fn raw_find_by_prefix_sorted_descending(
1413        &mut self,
1414        key_prefix: &[u8],
1415    ) -> DatabaseResult<PrefixStream<'_>> {
1416        self.raw
1417            .as_mut()
1418            .ok_or(DatabaseError::TransactionConsumed)?
1419            .raw_find_by_prefix_sorted_descending(key_prefix)
1420            .await
1421    }
1422
1423    async fn raw_remove_by_prefix(&mut self, key_prefix: &[u8]) -> DatabaseResult<()> {
1424        self.raw
1425            .as_mut()
1426            .ok_or(DatabaseError::TransactionConsumed)?
1427            .raw_remove_by_prefix(key_prefix)
1428            .await
1429    }
1430}
1431
1432impl<Tx: IRawDatabaseTransaction> IDatabaseTransactionOps for BaseDatabaseTransaction<Tx> {}
1433
1434#[apply(async_trait_maybe_send!)]
1435impl<Tx: IRawDatabaseTransaction + fmt::Debug> IDatabaseTransaction
1436    for BaseDatabaseTransaction<Tx>
1437{
1438    async fn commit_tx(&mut self) -> DatabaseResult<()> {
1439        self.raw
1440            .take()
1441            .ok_or(DatabaseError::TransactionConsumed)?
1442            .commit_tx()
1443            .await?;
1444        self.notifications.submit_queue(
1445            &self
1446                .notify_queue
1447                .take()
1448                .expect("commit must be called only once"),
1449        );
1450        Ok(())
1451    }
1452
1453    fn is_global(&self) -> bool {
1454        true
1455    }
1456
1457    fn global_dbtx(
1458        &mut self,
1459        _access_token: GlobalDBTxAccessToken,
1460    ) -> &mut dyn IDatabaseTransaction {
1461        panic!("Illegal to call global_dbtx on BaseDatabaseTransaction");
1462    }
1463}
1464
1465/// A helper for tracking and logging on `Drop` any instances of uncommitted
1466/// writes
1467#[derive(Clone)]
1468struct CommitTracker {
1469    /// Is the dbtx committed
1470    is_committed: bool,
1471    /// Does the dbtx have any writes
1472    has_writes: bool,
1473    /// Don't warn-log uncommitted writes
1474    ignore_uncommitted: bool,
1475}
1476
1477impl Drop for CommitTracker {
1478    fn drop(&mut self) {
1479        if self.has_writes && !self.is_committed {
1480            if self.ignore_uncommitted {
1481                trace!(
1482                    target: LOG_DB,
1483                    "DatabaseTransaction has writes and has not called commit, but that's expected."
1484                );
1485            } else {
1486                warn!(
1487                    target: LOG_DB,
1488                    location = ?backtrace::Backtrace::new(),
1489                    "DatabaseTransaction has writes and has not called commit."
1490                );
1491            }
1492        }
1493    }
1494}
1495
1496enum MaybeRef<'a, T> {
1497    Owned(T),
1498    Borrowed(&'a mut T),
1499}
1500
1501impl<T> ops::Deref for MaybeRef<'_, T> {
1502    type Target = T;
1503
1504    fn deref(&self) -> &Self::Target {
1505        match self {
1506            MaybeRef::Owned(o) => o,
1507            MaybeRef::Borrowed(r) => r,
1508        }
1509    }
1510}
1511
1512impl<T> ops::DerefMut for MaybeRef<'_, T> {
1513    fn deref_mut(&mut self) -> &mut Self::Target {
1514        match self {
1515            MaybeRef::Owned(o) => o,
1516            MaybeRef::Borrowed(r) => r,
1517        }
1518    }
1519}
1520
1521/// Session type for [`DatabaseTransaction`] that is allowed to commit
1522///
1523/// Opposite of [`NonCommittable`].
1524pub struct Committable;
1525
1526/// Session type for a [`DatabaseTransaction`] that is not allowed to commit
1527///
1528/// Opposite of [`Committable`].
1529pub struct NonCommittable;
1530
1531/// A high level database transaction handle
1532///
1533/// `Cap` is a session type
1534pub struct DatabaseTransaction<'tx, Cap = NonCommittable> {
1535    tx: Box<dyn IDatabaseTransaction + 'tx>,
1536    decoders: ModuleDecoderRegistry,
1537    commit_tracker: MaybeRef<'tx, CommitTracker>,
1538    on_commit_hooks: MaybeRef<'tx, Vec<Box<maybe_add_send!(dyn FnOnce())>>>,
1539    capability: marker::PhantomData<Cap>,
1540}
1541
1542impl<Cap> fmt::Debug for DatabaseTransaction<'_, Cap> {
1543    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1544        f.write_fmt(format_args!(
1545            "DatabaseTransaction {{ tx: {:?}, decoders={:?} }}",
1546            self.tx, self.decoders
1547        ))
1548    }
1549}
1550
1551impl<Cap> WithDecoders for DatabaseTransaction<'_, Cap> {
1552    fn decoders(&self) -> &ModuleDecoderRegistry {
1553        &self.decoders
1554    }
1555}
1556
1557#[instrument(target = LOG_DB, level = "trace", skip_all, fields(value_type = std::any::type_name::<V>()), err)]
1558fn decode_value<V: DatabaseValue>(
1559    value_bytes: &[u8],
1560    decoders: &ModuleDecoderRegistry,
1561) -> std::result::Result<V, DecodingError> {
1562    trace!(
1563        bytes = %AbbreviateHexBytes(value_bytes),
1564        "decoding value",
1565    );
1566    V::from_bytes(value_bytes, decoders)
1567}
1568
1569#[track_caller]
1570fn decode_value_expect<V: DatabaseValue>(
1571    value_bytes: &[u8],
1572    decoders: &ModuleDecoderRegistry,
1573    key_bytes: &[u8],
1574) -> V {
1575    decode_value(value_bytes, decoders).unwrap_or_else(|err| {
1576        panic!(
1577            "Unrecoverable decoding DatabaseValue as {}; err={}, key_bytes={}, val_bytes={}",
1578            any::type_name::<V>(),
1579            err.fmt_compact(),
1580            AbbreviateHexBytes(key_bytes),
1581            AbbreviateHexBytes(value_bytes),
1582        )
1583    })
1584}
1585
1586#[track_caller]
1587fn decode_key_expect<K: DatabaseKey>(key_bytes: &[u8], decoders: &ModuleDecoderRegistry) -> K {
1588    trace!(
1589        bytes = %AbbreviateHexBytes(key_bytes),
1590        "decoding key",
1591    );
1592    K::from_bytes(key_bytes, decoders).unwrap_or_else(|err| {
1593        panic!(
1594            "Unrecoverable decoding DatabaseKey as {}; err={}; bytes={}",
1595            any::type_name::<K>(),
1596            err.fmt_compact(),
1597            AbbreviateHexBytes(key_bytes)
1598        )
1599    })
1600}
1601
1602impl<'tx, Cap> DatabaseTransaction<'tx, Cap> {
1603    /// Convert into a non-committable version
1604    pub fn into_nc(self) -> DatabaseTransaction<'tx, NonCommittable> {
1605        DatabaseTransaction {
1606            tx: self.tx,
1607            decoders: self.decoders,
1608            commit_tracker: self.commit_tracker,
1609            on_commit_hooks: self.on_commit_hooks,
1610            capability: PhantomData::<NonCommittable>,
1611        }
1612    }
1613
1614    /// Get a reference to a non-committeable version
1615    pub fn to_ref_nc<'s, 'a>(&'s mut self) -> DatabaseTransaction<'a, NonCommittable>
1616    where
1617        's: 'a,
1618    {
1619        self.to_ref().into_nc()
1620    }
1621
1622    /// Get [`DatabaseTransaction`] isolated to a `prefix`
1623    pub fn with_prefix<'a: 'tx>(self, prefix: Vec<u8>) -> DatabaseTransaction<'a, Cap>
1624    where
1625        'tx: 'a,
1626    {
1627        DatabaseTransaction {
1628            tx: Box::new(PrefixDatabaseTransaction {
1629                inner: self.tx,
1630                global_dbtx_access_token: None,
1631                prefix,
1632            }),
1633            decoders: self.decoders,
1634            commit_tracker: self.commit_tracker,
1635            on_commit_hooks: self.on_commit_hooks,
1636            capability: self.capability,
1637        }
1638    }
1639
1640    /// Get [`DatabaseTransaction`] isolated to a prefix of a given
1641    /// `module_instance_id`, allowing the module to access global_dbtx
1642    /// with the right access token.
1643    pub fn with_prefix_module_id<'a: 'tx>(
1644        self,
1645        module_instance_id: ModuleInstanceId,
1646    ) -> (DatabaseTransaction<'a, Cap>, GlobalDBTxAccessToken)
1647    where
1648        'tx: 'a,
1649    {
1650        let prefix = module_instance_id_to_byte_prefix(module_instance_id);
1651        let global_dbtx_access_token = GlobalDBTxAccessToken::from_prefix(&prefix);
1652        (
1653            DatabaseTransaction {
1654                tx: Box::new(PrefixDatabaseTransaction {
1655                    inner: self.tx,
1656                    global_dbtx_access_token: Some(global_dbtx_access_token),
1657                    prefix,
1658                }),
1659                decoders: self.decoders,
1660                commit_tracker: self.commit_tracker,
1661                on_commit_hooks: self.on_commit_hooks,
1662                capability: self.capability,
1663            },
1664            global_dbtx_access_token,
1665        )
1666    }
1667
1668    /// Get [`DatabaseTransaction`] to `self`
1669    pub fn to_ref<'s, 'a>(&'s mut self) -> DatabaseTransaction<'a, Cap>
1670    where
1671        's: 'a,
1672    {
1673        let decoders = self.decoders.clone();
1674
1675        DatabaseTransaction {
1676            tx: Box::new(&mut self.tx),
1677            decoders,
1678            commit_tracker: match self.commit_tracker {
1679                MaybeRef::Owned(ref mut o) => MaybeRef::Borrowed(o),
1680                MaybeRef::Borrowed(ref mut b) => MaybeRef::Borrowed(b),
1681            },
1682            on_commit_hooks: match self.on_commit_hooks {
1683                MaybeRef::Owned(ref mut o) => MaybeRef::Borrowed(o),
1684                MaybeRef::Borrowed(ref mut b) => MaybeRef::Borrowed(b),
1685            },
1686            capability: self.capability,
1687        }
1688    }
1689
1690    /// Get [`DatabaseTransaction`] isolated to a `prefix` of `self`
1691    pub fn to_ref_with_prefix<'a>(&'a mut self, prefix: Vec<u8>) -> DatabaseTransaction<'a, Cap>
1692    where
1693        'tx: 'a,
1694    {
1695        DatabaseTransaction {
1696            tx: Box::new(PrefixDatabaseTransaction {
1697                inner: &mut self.tx,
1698                global_dbtx_access_token: None,
1699                prefix,
1700            }),
1701            decoders: self.decoders.clone(),
1702            commit_tracker: match self.commit_tracker {
1703                MaybeRef::Owned(ref mut o) => MaybeRef::Borrowed(o),
1704                MaybeRef::Borrowed(ref mut b) => MaybeRef::Borrowed(b),
1705            },
1706            on_commit_hooks: match self.on_commit_hooks {
1707                MaybeRef::Owned(ref mut o) => MaybeRef::Borrowed(o),
1708                MaybeRef::Borrowed(ref mut b) => MaybeRef::Borrowed(b),
1709            },
1710            capability: self.capability,
1711        }
1712    }
1713
1714    pub fn to_ref_with_prefix_module_id<'a>(
1715        &'a mut self,
1716        module_instance_id: ModuleInstanceId,
1717    ) -> (DatabaseTransaction<'a, Cap>, GlobalDBTxAccessToken)
1718    where
1719        'tx: 'a,
1720    {
1721        let prefix = module_instance_id_to_byte_prefix(module_instance_id);
1722        let global_dbtx_access_token = GlobalDBTxAccessToken::from_prefix(&prefix);
1723        (
1724            DatabaseTransaction {
1725                tx: Box::new(PrefixDatabaseTransaction {
1726                    inner: &mut self.tx,
1727                    global_dbtx_access_token: Some(global_dbtx_access_token),
1728                    prefix,
1729                }),
1730                decoders: self.decoders.clone(),
1731                commit_tracker: match self.commit_tracker {
1732                    MaybeRef::Owned(ref mut o) => MaybeRef::Borrowed(o),
1733                    MaybeRef::Borrowed(ref mut b) => MaybeRef::Borrowed(b),
1734                },
1735                on_commit_hooks: match self.on_commit_hooks {
1736                    MaybeRef::Owned(ref mut o) => MaybeRef::Borrowed(o),
1737                    MaybeRef::Borrowed(ref mut b) => MaybeRef::Borrowed(b),
1738                },
1739                capability: self.capability,
1740            },
1741            global_dbtx_access_token,
1742        )
1743    }
1744
1745    /// Is this `Database` a global, unpartitioned `Database`
1746    pub fn is_global(&self) -> bool {
1747        self.tx.is_global()
1748    }
1749
1750    /// `Err` if [`Self::is_global`] is not true
1751    pub fn ensure_global(&self) -> DatabaseResult<()> {
1752        if !self.is_global() {
1753            return Err(DatabaseError::NotGlobal);
1754        }
1755
1756        Ok(())
1757    }
1758
1759    /// `Err` if [`Self::is_global`] is true
1760    pub fn ensure_isolated(&self) -> DatabaseResult<()> {
1761        if self.is_global() {
1762            return Err(DatabaseError::NotIsolated);
1763        }
1764
1765        Ok(())
1766    }
1767
1768    /// Cancel the tx to avoid debugging warnings about uncommitted writes
1769    pub fn ignore_uncommitted(&mut self) -> &mut Self {
1770        self.commit_tracker.ignore_uncommitted = true;
1771        self
1772    }
1773
1774    /// Create warnings about uncommitted writes
1775    pub fn warn_uncommitted(&mut self) -> &mut Self {
1776        self.commit_tracker.ignore_uncommitted = false;
1777        self
1778    }
1779
1780    /// Register a hook that will be run after commit succeeds.
1781    #[instrument(target = LOG_DB, level = "trace", skip_all)]
1782    pub fn on_commit(&mut self, f: maybe_add_send!(impl FnOnce() + 'static)) {
1783        self.on_commit_hooks.push(Box::new(f));
1784    }
1785
1786    pub fn global_dbtx<'a>(
1787        &'a mut self,
1788        access_token: GlobalDBTxAccessToken,
1789    ) -> DatabaseTransaction<'a, Cap>
1790    where
1791        'tx: 'a,
1792    {
1793        let decoders = self.decoders.clone();
1794
1795        DatabaseTransaction {
1796            tx: Box::new(self.tx.global_dbtx(access_token)),
1797            decoders,
1798            commit_tracker: match self.commit_tracker {
1799                MaybeRef::Owned(ref mut o) => MaybeRef::Borrowed(o),
1800                MaybeRef::Borrowed(ref mut b) => MaybeRef::Borrowed(b),
1801            },
1802            on_commit_hooks: match self.on_commit_hooks {
1803                MaybeRef::Owned(ref mut o) => MaybeRef::Borrowed(o),
1804                MaybeRef::Borrowed(ref mut b) => MaybeRef::Borrowed(b),
1805            },
1806            capability: self.capability,
1807        }
1808    }
1809}
1810
1811/// Code used to access `global_dbtx`
1812#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1813pub struct GlobalDBTxAccessToken(u32);
1814
1815impl GlobalDBTxAccessToken {
1816    /// Calculate an access code for accessing global_dbtx from a prefixed
1817    /// database tx
1818    ///
1819    /// Since we need to do it at runtime, we want the user modules not to be
1820    /// able to call `global_dbtx` too easily. But at the same time we don't
1821    /// need to be paranoid.
1822    ///
1823    /// This must be deterministic during whole instance of the software running
1824    /// (because it's being rederived independently in multiple codepahs) , but
1825    /// it could be somewhat randomized between different runs and releases.
1826    fn from_prefix(prefix: &[u8]) -> Self {
1827        Self(prefix.iter().fold(0, |acc, b| acc + u32::from(*b)) + 513)
1828    }
1829}
1830
1831impl<'tx> DatabaseTransaction<'tx, Committable> {
1832    pub fn new(dbtx: Box<dyn IDatabaseTransaction + 'tx>, decoders: ModuleDecoderRegistry) -> Self {
1833        Self {
1834            tx: dbtx,
1835            decoders,
1836            commit_tracker: MaybeRef::Owned(CommitTracker {
1837                is_committed: false,
1838                has_writes: false,
1839                ignore_uncommitted: false,
1840            }),
1841            on_commit_hooks: MaybeRef::Owned(vec![]),
1842            capability: PhantomData,
1843        }
1844    }
1845
1846    pub async fn commit_tx_result(mut self) -> DatabaseResult<()> {
1847        self.commit_tracker.is_committed = true;
1848        let commit_result = self.tx.commit_tx().await;
1849
1850        // Run commit hooks in case commit was successful
1851        if commit_result.is_ok() {
1852            for hook in self.on_commit_hooks.deref_mut().drain(..) {
1853                hook();
1854            }
1855        }
1856
1857        commit_result
1858    }
1859
1860    pub async fn commit_tx(mut self) {
1861        self.commit_tracker.is_committed = true;
1862        self.commit_tx_result()
1863            .await
1864            .expect("Unrecoverable error occurred while committing to the database.");
1865    }
1866}
1867
1868#[apply(async_trait_maybe_send!)]
1869impl<Cap> IDatabaseTransactionOpsCore for DatabaseTransaction<'_, Cap>
1870where
1871    Cap: Send,
1872{
1873    async fn raw_insert_bytes(
1874        &mut self,
1875        key: &[u8],
1876        value: &[u8],
1877    ) -> DatabaseResult<Option<Vec<u8>>> {
1878        self.commit_tracker.has_writes = true;
1879        self.tx.raw_insert_bytes(key, value).await
1880    }
1881
1882    async fn raw_get_bytes(&mut self, key: &[u8]) -> DatabaseResult<Option<Vec<u8>>> {
1883        self.tx.raw_get_bytes(key).await
1884    }
1885
1886    async fn raw_remove_entry(&mut self, key: &[u8]) -> DatabaseResult<Option<Vec<u8>>> {
1887        self.tx.raw_remove_entry(key).await
1888    }
1889
1890    async fn raw_find_by_range(
1891        &mut self,
1892        key_range: Range<&[u8]>,
1893    ) -> DatabaseResult<PrefixStream<'_>> {
1894        self.tx.raw_find_by_range(key_range).await
1895    }
1896
1897    async fn raw_find_by_prefix(&mut self, key_prefix: &[u8]) -> DatabaseResult<PrefixStream<'_>> {
1898        self.tx.raw_find_by_prefix(key_prefix).await
1899    }
1900
1901    async fn raw_find_by_prefix_sorted_descending(
1902        &mut self,
1903        key_prefix: &[u8],
1904    ) -> DatabaseResult<PrefixStream<'_>> {
1905        self.tx
1906            .raw_find_by_prefix_sorted_descending(key_prefix)
1907            .await
1908    }
1909
1910    async fn raw_remove_by_prefix(&mut self, key_prefix: &[u8]) -> DatabaseResult<()> {
1911        self.commit_tracker.has_writes = true;
1912        self.tx.raw_remove_by_prefix(key_prefix).await
1913    }
1914}
1915impl IDatabaseTransactionOps for DatabaseTransaction<'_, Committable> {}
1916
1917impl<T> DatabaseKeyPrefix for T
1918where
1919    T: DatabaseLookup + crate::encoding::Encodable + Debug,
1920{
1921    fn to_bytes(&self) -> Vec<u8> {
1922        let mut data = vec![<Self as DatabaseLookup>::Record::DB_PREFIX];
1923        data.append(&mut self.consensus_encode_to_vec());
1924        data
1925    }
1926}
1927
1928impl<T> DatabaseKey for T
1929where
1930    // Note: key can only be `T` that can be decoded without modules (even if
1931    // module type is `()`)
1932    T: DatabaseRecord + crate::encoding::Decodable + Sized,
1933{
1934    const NOTIFY_ON_MODIFY: bool = <T as DatabaseRecord>::NOTIFY_ON_MODIFY;
1935    fn from_bytes(
1936        data: &[u8],
1937        modules: &ModuleDecoderRegistry,
1938    ) -> std::result::Result<Self, DecodingError> {
1939        if data.is_empty() {
1940            // TODO: build better coding errors, pretty useless right now
1941            return Err(DecodingError::wrong_length(1, 0));
1942        }
1943
1944        if data[0] != Self::DB_PREFIX {
1945            return Err(DecodingError::wrong_prefix(Self::DB_PREFIX, data[0]));
1946        }
1947
1948        <Self as crate::encoding::Decodable>::consensus_decode_whole(&data[1..], modules)
1949            .map_err(DecodingError::from)
1950    }
1951}
1952
1953impl<T> DatabaseValue for T
1954where
1955    T: Debug + Encodable + Decodable,
1956{
1957    fn from_bytes(
1958        data: &[u8],
1959        modules: &ModuleDecoderRegistry,
1960    ) -> std::result::Result<Self, DecodingError> {
1961        T::consensus_decode_whole(data, modules).map_err(DecodingError::from)
1962    }
1963
1964    fn to_bytes(&self) -> Vec<u8> {
1965        self.consensus_encode_to_vec()
1966    }
1967}
1968
1969/// This is a helper macro that generates the implementations of
1970/// `DatabaseRecord` necessary for reading/writing to the
1971/// database and fetching by prefix.
1972///
1973/// - `key`: This is the type of struct that will be used as the key into the
1974///   database
1975/// - `value`: This is the type of struct that will be used as the value into
1976///   the database
1977/// - `db_prefix`: Required enum expression that is represented as a `u8` and is
1978///   prepended to this key
1979/// - `query_prefix`: Optional type of struct that can be passed zero or more
1980///   times. Every query prefix can be used to query the database via
1981///   `find_by_prefix`
1982///
1983/// # Examples
1984///
1985/// ```
1986/// use fedimint_core::encoding::{Decodable, Encodable};
1987/// use fedimint_core::impl_db_record;
1988///
1989/// #[derive(Debug, Encodable, Decodable)]
1990/// struct MyKey;
1991///
1992/// #[derive(Debug, Encodable, Decodable)]
1993/// struct MyValue;
1994///
1995/// #[repr(u8)]
1996/// #[derive(Clone, Debug)]
1997/// pub enum DbKeyPrefix {
1998///     MyKey = 0x50,
1999/// }
2000///
2001/// impl_db_record!(key = MyKey, value = MyValue, db_prefix = DbKeyPrefix::MyKey);
2002/// ```
2003///
2004/// Use the required parameters and specify one `query_prefix`
2005///
2006/// ```
2007/// use fedimint_core::encoding::{Decodable, Encodable};
2008/// use fedimint_core::{impl_db_lookup, impl_db_record};
2009///
2010/// #[derive(Debug, Encodable, Decodable)]
2011/// struct MyKey;
2012///
2013/// #[derive(Debug, Encodable, Decodable)]
2014/// struct MyValue;
2015///
2016/// #[repr(u8)]
2017/// #[derive(Clone, Debug)]
2018/// pub enum DbKeyPrefix {
2019///     MyKey = 0x50,
2020/// }
2021///
2022/// #[derive(Debug, Encodable, Decodable)]
2023/// struct MyKeyPrefix;
2024///
2025/// impl_db_record!(key = MyKey, value = MyValue, db_prefix = DbKeyPrefix::MyKey,);
2026///
2027/// impl_db_lookup!(key = MyKey, query_prefix = MyKeyPrefix);
2028/// ```
2029#[macro_export]
2030macro_rules! impl_db_record {
2031    (key = $key:ty, value = $val:ty, db_prefix = $db_prefix:expr_2021 $(, notify_on_modify = $notify:tt)? $(,)?) => {
2032        impl $crate::db::DatabaseRecord for $key {
2033            const DB_PREFIX: u8 = $db_prefix as u8;
2034            $(const NOTIFY_ON_MODIFY: bool = $notify;)?
2035            type Key = Self;
2036            type Value = $val;
2037        }
2038        $(
2039            impl_db_record! {
2040                @impl_notify_marker key = $key, notify_on_modify = $notify
2041            }
2042        )?
2043    };
2044    // if notify is set to true
2045    (@impl_notify_marker key = $key:ty, notify_on_modify = true) => {
2046        impl $crate::db::DatabaseKeyWithNotify for $key {}
2047    };
2048    // if notify is set to false
2049    (@impl_notify_marker key = $key:ty, notify_on_modify = false) => {};
2050}
2051
2052#[macro_export]
2053macro_rules! impl_db_lookup{
2054    (key = $key:ty $(, query_prefix = $query_prefix:ty)* $(,)?) => {
2055        $(
2056            impl $crate::db::DatabaseLookup for $query_prefix {
2057                type Record = $key;
2058            }
2059        )*
2060    };
2061}
2062
2063/// Deprecated: Use `DatabaseVersionKey(ModuleInstanceId)` instead.
2064#[derive(Debug, Encodable, Decodable, Serialize)]
2065pub struct DatabaseVersionKeyV0;
2066
2067#[derive(Debug, Encodable, Decodable, Serialize)]
2068pub struct DatabaseVersionKey(pub ModuleInstanceId);
2069
2070#[derive(Debug, Encodable, Decodable, Serialize, Clone, PartialOrd, Ord, PartialEq, Eq, Copy)]
2071pub struct DatabaseVersion(pub u64);
2072
2073impl_db_record!(
2074    key = DatabaseVersionKeyV0,
2075    value = DatabaseVersion,
2076    db_prefix = DbKeyPrefix::DatabaseVersion
2077);
2078
2079impl_db_record!(
2080    key = DatabaseVersionKey,
2081    value = DatabaseVersion,
2082    db_prefix = DbKeyPrefix::DatabaseVersion
2083);
2084
2085impl std::fmt::Display for DatabaseVersion {
2086    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2087        write!(f, "{}", self.0)
2088    }
2089}
2090
2091impl DatabaseVersion {
2092    pub fn increment(&self) -> Self {
2093        Self(self.0 + 1)
2094    }
2095}
2096
2097impl std::fmt::Display for DbKeyPrefix {
2098    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2099        write!(f, "{self:?}")
2100    }
2101}
2102
2103#[repr(u8)]
2104#[derive(Clone, EnumIter, Debug)]
2105pub enum DbKeyPrefix {
2106    DatabaseVersion = 0x50,
2107    ClientBackup = 0x51,
2108}
2109
2110#[derive(Debug, Error)]
2111#[non_exhaustive]
2112pub enum DecodingError {
2113    #[error("Key had a wrong prefix, expected {expected} but got {found}")]
2114    WrongPrefix { expected: u8, found: u8 },
2115    #[error("Key had a wrong length, expected {expected} but got {found}")]
2116    WrongLength { expected: usize, found: usize },
2117    #[error("Other decoding error")]
2118    Other(#[source] Box<dyn Error + Send + Sync>),
2119    /// The bytes are not a valid consensus encoding of the record type.
2120    #[error("Invalid consensus encoding")]
2121    Decode(#[from] DecodeError),
2122}
2123
2124impl DecodingError {
2125    pub fn other<E: Error + Send + Sync + 'static>(error: E) -> Self {
2126        Self::Other(Box::new(error))
2127    }
2128
2129    pub fn wrong_prefix(expected: u8, found: u8) -> Self {
2130        Self::WrongPrefix { expected, found }
2131    }
2132
2133    pub fn wrong_length(expected: usize, found: usize) -> Self {
2134        Self::WrongLength { expected, found }
2135    }
2136}
2137
2138/// Error type for database operations
2139#[derive(Debug, Error)]
2140#[non_exhaustive]
2141pub enum DatabaseError {
2142    /// Write-write conflict during optimistic transaction commit.
2143    /// This occurs when two transactions attempt to modify the same key.
2144    #[error("Write-write conflict detected")]
2145    WriteConflict,
2146
2147    /// The backend could not tell whether this transaction conflicted with
2148    /// another one, because its snapshot is older than the write history the
2149    /// backend still keeps around for conflict detection.
2150    ///
2151    /// This is *not* a conflict: it says nothing about whether any other
2152    /// transaction touched the same keys, only that the transaction was open
2153    /// for too many intervening writes to check. Nothing was written, and
2154    /// rerunning the whole operation against a fresh transaction is the only
2155    /// supported recovery — the failed transaction cannot be committed again.
2156    ///
2157    /// The usual cause is a transaction held open across something slow.
2158    #[error("Transaction snapshot is older than the retained write history: {0}")]
2159    SnapshotTooOld(Box<dyn Error + Send + Sync>),
2160
2161    /// The transaction has already been consumed (committed or dropped).
2162    /// Operations cannot be performed on a consumed transaction.
2163    #[error("Transaction already consumed")]
2164    TransactionConsumed,
2165
2166    /// Error from the underlying database backend (e.g., RocksDB I/O errors).
2167    #[error("Database backend error: {0}")]
2168    DatabaseBackend(#[from] Box<dyn Error + Send + Sync>),
2169
2170    /// The operation requires a global (non module-isolated) database instance.
2171    #[error("Database instance is not global")]
2172    NotGlobal,
2173
2174    /// The operation requires a module-isolated database instance.
2175    #[error("Database instance is not isolated")]
2176    NotIsolated,
2177}
2178
2179impl DatabaseError {
2180    /// Create a DatabaseBackend error from any error type
2181    pub fn backend<E: Error + Send + Sync + 'static>(error: E) -> Self {
2182        Self::DatabaseBackend(Box::new(error))
2183    }
2184
2185    /// Create a `SnapshotTooOld` error, preserving the backend's own error
2186    pub fn snapshot_too_old<E: Error + Send + Sync + 'static>(error: E) -> Self {
2187        Self::SnapshotTooOld(Box::new(error))
2188    }
2189}
2190
2191#[macro_export]
2192macro_rules! push_db_pair_items {
2193    ($dbtx:ident, $prefix_type:expr_2021, $key_type:ty, $value_type:ty, $map:ident, $key_literal:literal) => {
2194        let db_items =
2195            $crate::db::IDatabaseTransactionOpsCoreTyped::find_by_prefix($dbtx, &$prefix_type)
2196                .await
2197                .map(|(key, val)| {
2198                    (
2199                        $crate::encoding::Encodable::consensus_encode_to_hex(&key),
2200                        val,
2201                    )
2202                })
2203                .collect::<BTreeMap<String, $value_type>>()
2204                .await;
2205
2206        $map.insert($key_literal.to_string(), Box::new(db_items));
2207    };
2208}
2209
2210#[macro_export]
2211macro_rules! push_db_key_items {
2212    ($dbtx:ident, $prefix_type:expr_2021, $key_type:ty, $map:ident, $key_literal:literal) => {
2213        let db_items =
2214            $crate::db::IDatabaseTransactionOpsCoreTyped::find_by_prefix($dbtx, &$prefix_type)
2215                .await
2216                .map(|(key, _)| key)
2217                .collect::<Vec<$key_type>>()
2218                .await;
2219
2220        $map.insert($key_literal.to_string(), Box::new(db_items));
2221    };
2222}
2223
2224/// Context passed to the db migration _functions_ (pay attention to `Fn` in the
2225/// name)
2226///
2227/// Typically should not be referred to directly, and instead by a type-alias,
2228/// where the inner-context is set.
2229///
2230/// Notably it has the (optional) module id (inaccessible to the modules
2231/// directly, but used internally) and an inner context `C` injected by the
2232/// outer-layer.
2233///
2234/// `C` is generic, as in different layers / scopes (server vs client, etc.) a
2235/// different (module-typed, type erased, server/client, etc.) contexts might be
2236/// needed, while the database migration logic is kind of generic over that.
2237pub struct DbMigrationFnContext<'tx, C> {
2238    dbtx: DatabaseTransaction<'tx>,
2239    module_instance_id: Option<ModuleInstanceId>,
2240    ctx: C,
2241    __please_use_constructor: (),
2242}
2243
2244impl<'tx, C> DbMigrationFnContext<'tx, C> {
2245    pub fn new(
2246        dbtx: DatabaseTransaction<'tx>,
2247        module_instance_id: Option<ModuleInstanceId>,
2248        ctx: C,
2249    ) -> Self {
2250        dbtx.ensure_global().expect("Must pass global dbtx");
2251        Self {
2252            dbtx,
2253            module_instance_id,
2254            ctx,
2255            // this is a constructor
2256            __please_use_constructor: (),
2257        }
2258    }
2259
2260    pub fn map<R>(self, f: impl FnOnce(C) -> R) -> DbMigrationFnContext<'tx, R> {
2261        DbMigrationFnContext::new(self.dbtx, self.module_instance_id, f(self.ctx))
2262    }
2263
2264    // TODO: this method is currently visible to the module itself, and it shouldn't
2265    #[doc(hidden)]
2266    pub fn split_dbtx_ctx<'s>(&'s mut self) -> (&'s mut DatabaseTransaction<'tx>, &'s C) {
2267        let Self { dbtx, ctx, .. } = self;
2268
2269        (dbtx, ctx)
2270    }
2271
2272    pub fn dbtx(&'_ mut self) -> DatabaseTransaction<'_> {
2273        if let Some(module_instance_id) = self.module_instance_id {
2274            self.dbtx.to_ref_with_prefix_module_id(module_instance_id).0
2275        } else {
2276            self.dbtx.to_ref_nc()
2277        }
2278    }
2279
2280    // TODO: this method is currently visible to the module itself, and it shouldn't
2281    #[doc(hidden)]
2282    pub fn module_instance_id(&self) -> Option<ModuleInstanceId> {
2283        self.module_instance_id
2284    }
2285}
2286
2287/// [`DbMigrationFn`] with no extra context (ATM gateway)
2288pub type GeneralDbMigrationFn = DbMigrationFn<()>;
2289pub type GeneralDbMigrationFnContext<'tx> = DbMigrationFnContext<'tx, ()>;
2290
2291/// [`DbMigrationFn`] used by core client
2292///
2293/// NOTE: client _module_ migrations are handled using separate structs due to
2294/// state machine migrations
2295pub type ClientCoreDbMigrationFn = DbMigrationFn<()>;
2296pub type ClientCoreDbMigrationFnContext<'tx> = DbMigrationFnContext<'tx, ()>;
2297
2298/// `CoreMigrationFn` that modules can implement to "migrate" the database
2299/// to the next database version.
2300///
2301/// It is parametrized over `C` (contents), which is extra data/type/interface
2302/// custom for different part of the codebase, e.g.:
2303///
2304/// * server core
2305/// * server modules
2306/// * client core
2307/// * gateway core
2308pub type DbMigrationFn<C> = Box<
2309    maybe_add_send_sync!(
2310        dyn for<'tx> Fn(
2311            DbMigrationFnContext<'tx, C>,
2312        ) -> Pin<
2313            Box<maybe_add_send!(dyn futures::Future<Output = Result<(), DbMigrationError>> + 'tx)>,
2314        >
2315    ),
2316>;
2317
2318/// Failure while applying database migrations.
2319#[derive(Debug, Error)]
2320#[non_exhaustive]
2321pub enum DbMigrationError {
2322    /// The database itself failed.
2323    #[error("Database error")]
2324    Database(#[from] DatabaseError),
2325    /// A stored value is not a valid consensus encoding.
2326    #[error("Failed to consensus-decode a database entry")]
2327    Decode(#[from] DecodeError),
2328    /// The database was written by newer code than the one applying migrations.
2329    #[error(
2330        "On disk database version {on_disk} for module {kind} is higher than the code \
2331         database version {target}"
2332    )]
2333    VersionTooHigh {
2334        kind: String,
2335        on_disk: DatabaseVersion,
2336        target: DatabaseVersion,
2337    },
2338    /// A migration failed for a reason specific to it.
2339    #[error("Migration failed")]
2340    Other(#[source] Box<dyn Error + Send + Sync>),
2341}
2342
2343impl DbMigrationError {
2344    /// Wraps a migration-specific error; accepts anything convertible into a
2345    /// boxed error, an `anyhow::Error` included.
2346    pub fn other<E>(error: E) -> Self
2347    where
2348        E: Into<Box<dyn Error + Send + Sync>>,
2349    {
2350        Self::Other(error.into())
2351    }
2352}
2353
2354/// Verifies that all database migrations are defined contiguously and returns
2355/// the "current" database version, which is one greater than the last key in
2356/// the map.
2357pub fn get_current_database_version<F>(
2358    migrations: &BTreeMap<DatabaseVersion, F>,
2359) -> DatabaseVersion {
2360    let versions = migrations.keys().copied().collect::<Vec<_>>();
2361
2362    // Verify that all database migrations are defined contiguously. If there is a
2363    // gap, this indicates a programming error and we should panic.
2364    if !versions
2365        .windows(2)
2366        .all(|window| window[0].increment() == window[1])
2367    {
2368        panic!("Database Migrations are not defined contiguously");
2369    }
2370
2371    versions
2372        .last()
2373        .map_or(DatabaseVersion(0), DatabaseVersion::increment)
2374}
2375
2376pub async fn apply_migrations<C>(
2377    db: &Database,
2378    ctx: C,
2379    kind: String,
2380    migrations: BTreeMap<DatabaseVersion, DbMigrationFn<C>>,
2381    module_instance_id: Option<ModuleInstanceId>,
2382    // When used in client side context, we can/should ignore keys that external app
2383    // is allowed to use, and but since this function is shared, we make it optional argument
2384    external_prefixes_above: Option<u8>,
2385) -> Result<(), DbMigrationError>
2386where
2387    C: Clone,
2388{
2389    let mut dbtx = db.begin_transaction().await;
2390    apply_migrations_dbtx(
2391        &mut dbtx.to_ref_nc(),
2392        ctx,
2393        kind,
2394        migrations,
2395        module_instance_id,
2396        external_prefixes_above,
2397    )
2398    .await?;
2399
2400    Ok(dbtx.commit_tx_result().await?)
2401}
2402/// `apply_migrations` iterates from the on disk database version for the
2403/// module.
2404///
2405/// `apply_migrations` iterates from the on disk database version for the module
2406/// up to `target_db_version` and executes all of the migrations that exist in
2407/// the migrations map. Each migration in migrations map updates the
2408/// database to have the correct on-disk structures that the code is expecting.
2409/// The entire migration process is atomic (i.e migration from 0->1 and 1->2
2410/// happen atomically). This function is called before the module is initialized
2411/// and as long as the correct migrations are supplied in the migrations map,
2412/// the module will be able to read and write from the database successfully.
2413pub async fn apply_migrations_dbtx<C>(
2414    global_dbtx: &mut DatabaseTransaction<'_>,
2415    ctx: C,
2416    kind: String,
2417    migrations: BTreeMap<DatabaseVersion, DbMigrationFn<C>>,
2418    module_instance_id: Option<ModuleInstanceId>,
2419    // When used in client side context, we can/should ignore keys that external app
2420    // is allowed to use, and but since this function is shared, we make it optional argument
2421    external_prefixes_above: Option<u8>,
2422) -> Result<(), DbMigrationError>
2423where
2424    C: Clone,
2425{
2426    // Newly created databases will not have any data since they have just been
2427    // instantiated.
2428    let is_new_db = global_dbtx
2429        .raw_find_by_prefix(&[])
2430        .await?
2431        .filter(|(key, _v)| {
2432            std::future::ready(
2433                external_prefixes_above.is_none_or(|external_prefixes_above| {
2434                    !key.is_empty() && key[0] < external_prefixes_above
2435                }),
2436            )
2437        })
2438        .next()
2439        .await
2440        .is_none();
2441
2442    let target_db_version = get_current_database_version(&migrations);
2443
2444    // First write the database version to disk if it does not exist.
2445    create_database_version_dbtx(
2446        global_dbtx,
2447        target_db_version,
2448        module_instance_id,
2449        kind.clone(),
2450        is_new_db,
2451    )
2452    .await;
2453
2454    let module_instance_id_key = module_instance_id_or_global(module_instance_id);
2455
2456    let disk_version = global_dbtx
2457        .get_value(&DatabaseVersionKey(module_instance_id_key))
2458        .await;
2459
2460    let db_version = if let Some(disk_version) = disk_version {
2461        let mut current_db_version = disk_version;
2462
2463        if current_db_version > target_db_version {
2464            return Err(DbMigrationError::VersionTooHigh {
2465                kind,
2466                on_disk: current_db_version,
2467                target: target_db_version,
2468            });
2469        }
2470
2471        while current_db_version < target_db_version {
2472            if let Some(migration) = migrations.get(&current_db_version) {
2473                info!(target: LOG_DB, ?kind, ?current_db_version, ?target_db_version, "Migrating module...");
2474                migration(DbMigrationFnContext::new(
2475                    global_dbtx.to_ref_nc(),
2476                    module_instance_id,
2477                    ctx.clone(),
2478                ))
2479                .await?;
2480            } else {
2481                warn!(target: LOG_DB, ?current_db_version, "Missing server db migration");
2482            }
2483
2484            current_db_version = current_db_version.increment();
2485
2486            global_dbtx
2487                .insert_entry(
2488                    &DatabaseVersionKey(module_instance_id_key),
2489                    &current_db_version,
2490                )
2491                .await;
2492        }
2493
2494        current_db_version
2495    } else {
2496        target_db_version
2497    };
2498
2499    debug!(target: LOG_DB, ?kind, ?db_version, "DB Version");
2500    Ok(())
2501}
2502
2503pub async fn create_database_version(
2504    db: &Database,
2505    target_db_version: DatabaseVersion,
2506    module_instance_id: Option<ModuleInstanceId>,
2507    kind: String,
2508    is_new_db: bool,
2509) -> Result<(), DbMigrationError> {
2510    let mut dbtx = db.begin_transaction().await;
2511
2512    create_database_version_dbtx(
2513        &mut dbtx.to_ref_nc(),
2514        target_db_version,
2515        module_instance_id,
2516        kind,
2517        is_new_db,
2518    )
2519    .await;
2520
2521    dbtx.commit_tx_result().await?;
2522    Ok(())
2523}
2524
2525/// Creates the `DatabaseVersion` inside the database if it does not exist. If
2526/// necessary, this function will migrate the legacy database version to the
2527/// expected `DatabaseVersionKey`.
2528pub async fn create_database_version_dbtx(
2529    global_dbtx: &mut DatabaseTransaction<'_>,
2530    target_db_version: DatabaseVersion,
2531    module_instance_id: Option<ModuleInstanceId>,
2532    kind: String,
2533    is_new_db: bool,
2534) {
2535    let key_module_instance_id = module_instance_id_or_global(module_instance_id);
2536
2537    // First check if the module has a `DatabaseVersion` written to
2538    // `DatabaseVersionKey`. If `DatabaseVersion` already exists, there is
2539    // nothing to do.
2540    if global_dbtx
2541        .get_value(&DatabaseVersionKey(key_module_instance_id))
2542        .await
2543        .is_none()
2544    {
2545        // If it exists, read and remove the legacy `DatabaseVersion`, which used to be
2546        // in the module's isolated namespace (but not for fedimint-server or
2547        // fedimint-client).
2548        //
2549        // Otherwise, if the previous database contains data and no legacy database
2550        // version, use `DatabaseVersion(0)` so that all database migrations are
2551        // run. Otherwise, this database can assumed to be new and can use
2552        // `target_db_version` to skip the database migrations.
2553        let current_version_in_module = if let Some(module_instance_id) = module_instance_id {
2554            remove_current_db_version_if_exists(
2555                &mut global_dbtx
2556                    .to_ref_with_prefix_module_id(module_instance_id)
2557                    .0
2558                    .into_nc(),
2559                is_new_db,
2560                target_db_version,
2561            )
2562            .await
2563        } else {
2564            remove_current_db_version_if_exists(
2565                &mut global_dbtx.to_ref().into_nc(),
2566                is_new_db,
2567                target_db_version,
2568            )
2569            .await
2570        };
2571
2572        // Write the previous `DatabaseVersion` to the new `DatabaseVersionKey`
2573        debug!(target: LOG_DB, ?kind, ?current_version_in_module, ?target_db_version, ?is_new_db, "Creating DatabaseVersionKey...");
2574        global_dbtx
2575            .insert_new_entry(
2576                &DatabaseVersionKey(key_module_instance_id),
2577                &current_version_in_module,
2578            )
2579            .await;
2580    }
2581}
2582
2583/// Removes `DatabaseVersion` from `DatabaseVersionKeyV0` if it exists and
2584/// returns the current database version. If the current version does not
2585/// exist, use `target_db_version` if the database is new. Otherwise, return
2586/// `DatabaseVersion(0)` to ensure all migrations are run.
2587async fn remove_current_db_version_if_exists(
2588    version_dbtx: &mut DatabaseTransaction<'_>,
2589    is_new_db: bool,
2590    target_db_version: DatabaseVersion,
2591) -> DatabaseVersion {
2592    // Remove the previous `DatabaseVersion` in the isolated database. If it doesn't
2593    // exist, just use the 0 for the version so that all of the migrations are
2594    // executed.
2595    let current_version_in_module = version_dbtx.remove_entry(&DatabaseVersionKeyV0).await;
2596    match current_version_in_module {
2597        Some(database_version) => database_version,
2598        None if is_new_db => target_db_version,
2599        None => DatabaseVersion(0),
2600    }
2601}
2602
2603/// Helper function to retrieve the `module_instance_id` for modules, otherwise
2604/// return 0xff for the global namespace.
2605fn module_instance_id_or_global(module_instance_id: Option<ModuleInstanceId>) -> ModuleInstanceId {
2606    // Use 0xff for fedimint-server and the `module_instance_id` for each module
2607    module_instance_id.unwrap_or_else(|| MODULE_GLOBAL_PREFIX.into())
2608}
2609#[allow(unused_imports)]
2610mod test_utils;
2611
2612pub async fn find_by_prefix_sorted_descending<'r, 'inner, KP>(
2613    tx: &'r mut (dyn IDatabaseTransaction + 'inner),
2614    decoders: ModuleDecoderRegistry,
2615    key_prefix: &KP,
2616) -> impl Stream<
2617    Item = (
2618        KP::Record,
2619        <<KP as DatabaseLookup>::Record as DatabaseRecord>::Value,
2620    ),
2621>
2622+ 'r
2623+ use<'r, KP>
2624where
2625    'inner: 'r,
2626    KP: DatabaseLookup,
2627    KP::Record: DatabaseKey,
2628{
2629    debug!(target: LOG_DB, "find by prefix sorted descending");
2630    let prefix_bytes = key_prefix.to_bytes();
2631    tx.raw_find_by_prefix_sorted_descending(&prefix_bytes)
2632        .await
2633        .expect("Error doing prefix search in database")
2634        .map(move |(key_bytes, value_bytes)| {
2635            let key = decode_key_expect(&key_bytes, &decoders);
2636            let value = decode_value_expect(&value_bytes, &decoders, &key_bytes);
2637            (key, value)
2638        })
2639}
2640
2641pub async fn verify_module_db_integrity_dbtx(
2642    dbtx: &mut DatabaseTransaction<'_>,
2643    module_id: ModuleInstanceId,
2644    module_kind: ModuleKind,
2645    prefixes: &BTreeSet<u8>,
2646) {
2647    let module_db_prefix = module_instance_id_to_byte_prefix(module_id);
2648    if module_id < 250 {
2649        assert_eq!(module_db_prefix.len(), 2);
2650    }
2651    let mut records = dbtx
2652        .raw_find_by_prefix(&module_db_prefix)
2653        .await
2654        .expect("DB fail");
2655    while let Some((k, v)) = records.next().await {
2656        assert!(
2657            prefixes.contains(&k[module_db_prefix.len()]),
2658            "Unexpected module {module_kind} {module_id} db record found: {}: {}",
2659            k.as_hex(),
2660            v.as_hex()
2661        );
2662    }
2663}
2664
2665#[cfg(test)]
2666mod tests;