commonware-storage 2026.9.0

Persist and retrieve data from an abstract store.
Documentation
//! Trait providing a unified test/benchmark interface across all Any database variants.

use crate::{
    merkle::{Family, Location, Proof},
    qmdb::{Error, operation::Key},
};
use commonware_codec::CodecShared;
use commonware_cryptography::Digest;
use commonware_runtime::Handle;
use core::num::NonZeroU64;
use std::{future::Future, ops::Range};

/// Unmerkleized batch of operations.
pub trait UnmerkleizedBatch<Db: ?Sized>: Sized {
    type Family: Family;
    type K;
    type V;
    type Metadata;
    type Merkleized: MerkleizedBatch;

    /// Record a mutation. Use `Some(value)` for update/create, `None` for delete.
    fn write(self, key: Self::K, value: Option<Self::V>) -> Self;

    /// Resolve mutations, compute the new root, and return a merkleized batch.
    fn merkleize(
        self,
        db: &Db,
        metadata: Option<Self::Metadata>,
    ) -> impl Future<Output = Result<Self::Merkleized, Error<Self::Family>>>;
}

/// Merkleized batch of operations.
pub trait MerkleizedBatch: Sized {
    type Digest: Digest;

    /// Return the committed root.
    fn root(&self) -> Self::Digest;
}

/// The result of applying a batch: the database and the range of written operations.
pub type ApplyBatchResult<D> =
    Result<(D, Range<Location<<D as BatchableDb>::Family>>), Error<<D as BatchableDb>::Family>>;

/// Db that supports updates through a batch API.
pub trait BatchableDb: Sized {
    type Family: Family;
    type K;
    type V;
    type Merkleized: MerkleizedBatch;
    type Batch: UnmerkleizedBatch<
            Self,
            Family = Self::Family,
            K = Self::K,
            V = Self::V,
            Metadata = Self::V,
            Merkleized = Self::Merkleized,
        >;

    /// Create a new speculative batch of operations with this database as its parent.
    fn new_batch(&self) -> Self::Batch;

    /// Apply a merkleized batch, returning the range of written operations.
    fn apply_batch(self, batch: Self::Merkleized) -> impl Future<Output = ApplyBatchResult<Self>>;
}

/// Unified trait for an authenticated database.
///
/// This trait provides access to authentication (root), pruning, persistence,
/// reads, and batch mutations.
pub trait DbAny<F: Family>:
    BatchableDb<Family = F, K = <Self as DbAny<F>>::Key, V = <Self as DbAny<F>>::Value> + Send + Sync
{
    /// The key type used to look up values.
    type Key: Key;

    /// The value type stored in the database.
    type Value: CodecShared + Clone;

    /// The digest type used for merkleization.
    type Digest: Digest;

    /// Get the value of a key.
    fn get<'a>(
        &'a self,
        key: &'a Self::Key,
    ) -> impl Future<Output = Result<Option<Self::Value>, Error<F>>> + Send + use<'a, F, Self>;

    /// Get the values of multiple keys, returned in the same order as the input keys.
    fn get_many<'a>(
        &'a self,
        keys: &'a [&'a Self::Key],
    ) -> impl Future<Output = Result<Vec<Option<Self::Value>>, Error<F>>> + Send + use<'a, F, Self>;

    /// Returns the root digest of the authenticated store.
    fn root(&self) -> Self::Digest;

    /// Return [start, end) where `start` and `end - 1` are the Locations of the oldest and newest
    /// retained operations respectively.
    fn bounds(&self) -> Range<Location<F>>;

    /// Return the Location of the next operation appended to this db.
    fn size(&self) -> Location<F> {
        self.bounds().end
    }

    /// Get the metadata associated with the last commit.
    fn get_metadata(
        &self,
    ) -> impl Future<Output = Result<Option<<Self as DbAny<F>>::Value>, Error<F>>> + Send;

    /// Prune historical operations prior to `loc`.
    fn prune(self, loc: Location<F>) -> impl Future<Output = Result<Self, Error<F>>> + Send;

    /// Begin durably persisting the database.
    ///
    /// Awaiting the returned [Handle] provides the same durability guarantee as [Self::commit]
    /// for the state applied before the call, plus a best-effort attempt to bound the recovery
    /// needed on startup. Use [Self::sync] to guarantee none is needed.
    fn start_sync(self) -> impl Future<Output = Result<(Self, Handle<()>), Error<F>>> + Send;

    /// Durably persist the database, guaranteeing the current state will survive a crash.
    ///
    /// For a stronger guarantee that eliminates potential recovery, use [Self::sync] instead.
    fn commit(self) -> impl Future<Output = Result<Self, Error<F>>> + Send;

    /// Durably persist the database, guaranteeing the current state will survive a crash, and that
    /// no recovery will be needed on startup.
    ///
    /// This provides a stronger guarantee than [Self::commit] but may be slower.
    fn sync(self) -> impl Future<Output = Result<Self, Error<F>>> + Send;

    /// Destroy the database, removing all data from disk.
    fn destroy(self) -> impl Future<Output = Result<(), Error<F>>> + Send
    where
        Self: Sized;

    /// The location before which all operations can be pruned.
    fn inactivity_floor_loc(&self) -> Location<F>;

    /// The maximum location that [`Self::prune`] accepts and the most recent location from which
    /// this database can be safely synced.
    fn sync_boundary(&self) -> Location<F>;
}

/// Proof generation for Any database variants.
///
/// Only Any variants implement this trait. Current variants have a different
/// proof structure (grafted MMR + activity bitmap) that is incompatible with
/// the ops-level proofs returned here.
pub trait Provable<F: Family>: DbAny<F> {
    /// The operation type stored in the log.
    type Operation;

    /// Generate a proof of operations starting at `start_loc`.
    #[allow(clippy::type_complexity)]
    fn proof(
        &self,
        start_loc: Location<F>,
        max_ops: NonZeroU64,
    ) -> impl Future<Output = Result<(Proof<F, Self::Digest>, Vec<Self::Operation>), Error<F>>> + Send
    {
        async move {
            self.historical_proof(self.bounds().end, start_loc, max_ops)
                .await
        }
    }

    /// Generate a proof of operations starting at `start_loc` for a historical size.
    #[allow(clippy::type_complexity)]
    fn historical_proof(
        &self,
        historical_size: Location<F>,
        start_loc: Location<F>,
        max_ops: NonZeroU64,
    ) -> impl Future<Output = Result<(Proof<F, Self::Digest>, Vec<Self::Operation>), Error<F>>> + Send;
}

/// Implements [`DbAny`] by delegating each method to an identically-named inherent method.
///
/// # Syntax
///
/// ```ignore
/// impl_db_any! {
///     [$($generics)*] TypeName
///     where { $where_clause }
///     Family = .., Key = .., Value = .., Digest = ..
/// }
/// ```
macro_rules! impl_db_any {
    (
        [$($gen:tt)*] $ty:ty
        where { $($where_clause:tt)* }
        Family = $fam:ty, Key = $key:ty, Value = $val:ty, Digest = $dig:ty
    ) => {
        impl<$($gen)*> $crate::qmdb::any::traits::DbAny<$fam> for $ty
        where $($where_clause)*
        {
            type Key = $key;
            type Value = $val;
            type Digest = $dig;

            async fn get(&self, key: &$key) -> ::core::result::Result<Option<$val>, $crate::qmdb::Error<$fam>> {
                <$ty>::get(self, key).await
            }

            async fn get_many(&self, keys: &[&$key]) -> ::core::result::Result<Vec<Option<$val>>, $crate::qmdb::Error<$fam>> {
                <$ty>::get_many(self, keys).await
            }

            fn root(&self) -> $dig {
                <$ty>::root(self)
            }

            fn bounds(&self) -> ::std::ops::Range<$crate::merkle::Location<$fam>> {
                <$ty>::bounds(self)
            }

            async fn get_metadata(
                &self,
            ) -> ::core::result::Result<Option<$val>, $crate::qmdb::Error<$fam>> {
                <$ty>::get_metadata(self).await
            }

            async fn prune(
                self,
                loc: $crate::merkle::Location<$fam>,
            ) -> ::core::result::Result<Self, $crate::qmdb::Error<$fam>> {
                <$ty>::prune(self, loc).await
            }

            async fn start_sync(
                self,
            ) -> ::core::result::Result<
                (Self, ::commonware_runtime::Handle<()>),
                $crate::qmdb::Error<$fam>,
            > {
                <$ty>::start_sync(self).await
            }

            async fn commit(self) -> ::core::result::Result<Self, $crate::qmdb::Error<$fam>> {
                <$ty>::commit(self).await
            }

            async fn sync(self) -> ::core::result::Result<Self, $crate::qmdb::Error<$fam>> {
                <$ty>::sync(self).await
            }

            async fn destroy(self) -> ::core::result::Result<(), $crate::qmdb::Error<$fam>> {
                <$ty>::destroy(self).await
            }

            fn inactivity_floor_loc(&self) -> $crate::merkle::Location<$fam> {
                <$ty>::inactivity_floor_loc(self)
            }

            fn sync_boundary(&self) -> $crate::merkle::Location<$fam> {
                <$ty>::sync_boundary(self)
            }
        }
    };
}

pub(crate) use impl_db_any;

/// Implements [`Provable`] by delegating each method to an identically-named inherent method.
macro_rules! impl_provable {
    (
        [$($gen:tt)*] $ty:ty
        where { $($where_clause:tt)* }
        Family = $fam:ty, Operation = $op:ty
    ) => {
        impl<$($gen)*> $crate::qmdb::any::traits::Provable<$fam> for $ty
        where $($where_clause)*
        {
            type Operation = $op;

            async fn historical_proof(
                &self,
                historical_size: $crate::merkle::Location<$fam>,
                start_loc: $crate::merkle::Location<$fam>,
                max_ops: ::core::num::NonZeroU64,
            ) -> ::core::result::Result<
                ($crate::merkle::Proof<$fam, <Self as $crate::qmdb::any::traits::DbAny<$fam>>::Digest>, Vec<$op>),
                $crate::qmdb::Error<$fam>,
            > {
                <$ty>::historical_proof(self, historical_size, start_loc, max_ops)
                    .await
            }
        }
    };
}

pub(crate) use impl_provable;