//! A keyless authenticated database for variable-length data. //! //! For fixed-size values, use [super::fixed]. use crate::{ journal::{ authenticated, contiguous::variable::{self, Config as JournalConfig}, }, merkle::Family, qmdb::{ any::value::{VariableEncoding, VariableValue}, keyless::operation::Operation as BaseOperation, operation::Committable, Error, }, }; use commonware_codec::Read; use commonware_cryptography::Hasher; use commonware_runtime::{Clock, Metrics, Storage}; /// Keyless operation for variable-length values. pub type Operation = BaseOperation>; /// A keyless authenticated database for variable-length data. pub type Db = super::Keyless, variable::Journal>, H>; type Journal = authenticated::Journal>, H>; /// Configuration for a variable-size [keyless](super) authenticated db. pub type Config = super::Config>; impl Db { /// Returns a [Db] initialized from `cfg`. Any uncommitted operations will be /// discarded and the state of the db will be as of the last committed operation. pub async fn init( context: E, cfg: Config< as Read>::Cfg>, ) -> Result> { let journal: Journal = Journal::new(context, cfg.merkle, cfg.log, Operation::::is_commit).await?; Self::init_from_journal(journal).await } } #[cfg(test)] mod test { use super::*; use crate::{ merkle::{mmb, mmr}, qmdb::keyless::tests, }; use commonware_cryptography::Sha256; use commonware_macros::test_traced; use commonware_runtime::{ buffer::paged::CacheRef, deterministic, BufferPooler, Metrics, Runner as _, }; use commonware_utils::{NZUsize, NZU16, NZU64}; use std::num::{NonZeroU16, NonZeroUsize}; // Use some weird sizes here to test boundary conditions. const PAGE_SIZE: NonZeroU16 = NZU16!(101); const PAGE_CACHE_SIZE: NonZeroUsize = NZUsize!(11); fn db_config( suffix: &str, pooler: &impl BufferPooler, ) -> Config<(commonware_codec::RangeCfg, ())> { let page_cache = CacheRef::from_pooler(pooler, PAGE_SIZE, PAGE_CACHE_SIZE); Config { merkle: crate::merkle::journaled::Config { journal_partition: format!("journal-{suffix}"), metadata_partition: format!("metadata-{suffix}"), items_per_blob: NZU64!(11), write_buffer: NZUsize!(1024), thread_pool: None, page_cache: page_cache.clone(), }, log: JournalConfig { partition: format!("log-journal-{suffix}"), items_per_section: NZU64!(7), compression: None, codec_config: ((0..=10000).into(), ()), page_cache, write_buffer: NZUsize!(1024), }, } } type TestDb = Db, Sha256>; /// Return a [Db] database initialized with a fixed config. async fn open_db(context: deterministic::Context) -> TestDb { let cfg = db_config("partition", &context); TestDb::init(context, cfg).await.unwrap() } fn reopen() -> tests::Reopen> { Box::new(|ctx| Box::pin(open_db(ctx))) } #[test_traced("INFO")] fn test_keyless_db_empty() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db1")).await; tests::test_keyless_db_empty(ctx, db, reopen::()).await; }); } #[test_traced("WARN")] fn test_keyless_db_build_basic() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db1")).await; tests::test_keyless_db_build_basic(ctx, db, reopen::()).await; }); } #[test_traced("WARN")] fn test_keyless_db_recovery() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db1")).await; tests::test_keyless_db_recovery(ctx, db, reopen::()).await; }); } #[test_traced("WARN")] fn test_keyless_db_non_empty_recovery() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db1")).await; tests::test_keyless_db_non_empty_recovery(ctx, db, reopen::()).await; }); } #[test_traced("INFO")] fn test_keyless_db_proof() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.clone()).await; tests::test_keyless_db_proof(db).await; }); } #[test_traced("INFO")] fn test_keyless_db_proof_comprehensive() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.clone()).await; tests::test_keyless_db_proof_comprehensive(db).await; }); } #[test_traced("INFO")] fn test_keyless_db_proof_with_pruning() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db1")).await; tests::test_keyless_db_proof_with_pruning(ctx, db, reopen::()).await; }); } #[test_traced("WARN")] fn test_keyless_db_empty_db_recovery() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db1")).await; tests::test_keyless_db_empty_db_recovery(ctx, db, reopen::()).await; }); } #[test_traced("WARN")] fn test_keyless_db_replay_with_trailing_appends() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db1")).await; tests::test_keyless_db_replay_with_trailing_appends(ctx, db, reopen::()) .await; }); } #[test_traced("INFO")] fn test_keyless_db_get_out_of_bounds() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.clone()).await; tests::test_keyless_db_get_out_of_bounds(db).await; }); } #[test_traced("INFO")] fn test_keyless_db_metadata() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_db_metadata(db).await; }); } #[test_traced("INFO")] fn test_keyless_db_pruning() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_db_pruning(db).await; }); } #[test_traced("INFO")] fn test_keyless_batch_get() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_batch_get(db).await; }); } #[test_traced("INFO")] fn test_keyless_batch_stacked_get() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_batch_stacked_get(db).await; }); } #[test_traced("INFO")] fn test_keyless_batch_speculative_root() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_batch_speculative_root(db).await; }); } #[test_traced("INFO")] fn test_keyless_merkleized_batch_get() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_merkleized_batch_get(db).await; }); } #[test_traced("INFO")] fn test_keyless_batch_chained() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_batch_chained(db).await; }); } #[test_traced("INFO")] fn test_keyless_batch_chained_apply_sequential() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_batch_chained_apply_sequential(db).await; }); } #[test_traced("INFO")] fn test_keyless_batch_many_sequential() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_batch_many_sequential(db).await; }); } #[test_traced("INFO")] fn test_keyless_batch_empty() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_batch_empty(db).await; }); } #[test_traced("INFO")] fn test_keyless_batch_chained_merkleized_get() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_batch_chained_merkleized_get(db).await; }); } #[test_traced("INFO")] fn test_keyless_batch_large() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_batch_large(db).await; }); } #[test_traced] fn test_keyless_stale_batch() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_stale_batch(db).await; }); } #[test_traced] fn test_stale_batch_chained() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_stale_batch_chained(db).await; }); } #[test_traced] fn test_sequential_commit_parent_then_child() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_sequential_commit_parent_then_child(db).await; }); } #[test_traced] fn test_stale_batch_child_applied_before_parent() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_stale_batch_child_before_parent(db).await; }); } #[test_traced] fn test_partial_ancestor_commit() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_partial_ancestor_commit(db).await; }); } #[test_traced] fn test_keyless_to_batch() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_to_batch(db).await; }); } #[test_traced] fn test_keyless_child_root_matches_pending_and_committed() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_child_root_matches_pending_and_committed(db).await; }); } #[test_traced("INFO")] fn test_keyless_rewind_recovery() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_db_rewind_recovery(ctx, db, reopen::()).await; }); } #[test_traced("INFO")] fn test_keyless_rewind_pruned_target_errors() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_db_rewind_pruned_target_errors(db).await; }); } // mmb::Family variants #[test_traced("INFO")] fn test_keyless_db_empty_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db1")).await; tests::test_keyless_db_empty(ctx, db, reopen::()).await; }); } #[test_traced("WARN")] fn test_keyless_db_build_basic_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db1")).await; tests::test_keyless_db_build_basic(ctx, db, reopen::()).await; }); } #[test_traced("WARN")] fn test_keyless_db_recovery_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db1")).await; tests::test_keyless_db_recovery(ctx, db, reopen::()).await; }); } #[test_traced("WARN")] fn test_keyless_db_non_empty_recovery_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db1")).await; tests::test_keyless_db_non_empty_recovery(ctx, db, reopen::()).await; }); } #[test_traced("INFO")] fn test_keyless_db_proof_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.clone()).await; tests::test_keyless_db_proof(db).await; }); } #[test_traced("INFO")] fn test_keyless_db_proof_comprehensive_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.clone()).await; tests::test_keyless_db_proof_comprehensive(db).await; }); } #[test_traced("INFO")] fn test_keyless_db_proof_with_pruning_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db1")).await; tests::test_keyless_db_proof_with_pruning(ctx, db, reopen::()).await; }); } #[test_traced("WARN")] fn test_keyless_db_empty_db_recovery_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db1")).await; tests::test_keyless_db_empty_db_recovery(ctx, db, reopen::()).await; }); } #[test_traced("WARN")] fn test_keyless_db_replay_with_trailing_appends_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db1")).await; tests::test_keyless_db_replay_with_trailing_appends(ctx, db, reopen::()) .await; }); } #[test_traced("INFO")] fn test_keyless_db_get_out_of_bounds_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.clone()).await; tests::test_keyless_db_get_out_of_bounds(db).await; }); } #[test_traced("INFO")] fn test_keyless_db_metadata_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_db_metadata(db).await; }); } #[test_traced("INFO")] fn test_keyless_db_pruning_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_db_pruning(db).await; }); } #[test_traced("INFO")] fn test_keyless_batch_get_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_batch_get(db).await; }); } #[test_traced("INFO")] fn test_keyless_batch_stacked_get_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_batch_stacked_get(db).await; }); } #[test_traced("INFO")] fn test_keyless_batch_speculative_root_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_batch_speculative_root(db).await; }); } #[test_traced("INFO")] fn test_keyless_merkleized_batch_get_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_merkleized_batch_get(db).await; }); } #[test_traced("INFO")] fn test_keyless_batch_chained_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_batch_chained(db).await; }); } #[test_traced("INFO")] fn test_keyless_batch_chained_apply_sequential_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_batch_chained_apply_sequential(db).await; }); } #[test_traced("INFO")] fn test_keyless_batch_many_sequential_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_batch_many_sequential(db).await; }); } #[test_traced("INFO")] fn test_keyless_batch_empty_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_batch_empty(db).await; }); } #[test_traced("INFO")] fn test_keyless_batch_chained_merkleized_get_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_batch_chained_merkleized_get(db).await; }); } #[test_traced("INFO")] fn test_keyless_batch_large_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_batch_large(db).await; }); } #[test_traced] fn test_keyless_stale_batch_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_stale_batch(db).await; }); } #[test_traced] fn test_stale_batch_chained_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_stale_batch_chained(db).await; }); } #[test_traced] fn test_sequential_commit_parent_then_child_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_sequential_commit_parent_then_child(db).await; }); } #[test_traced] fn test_stale_batch_child_applied_before_parent_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_stale_batch_child_before_parent(db).await; }); } #[test_traced] fn test_keyless_to_batch_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_to_batch(db).await; }); } #[test_traced] fn test_keyless_child_root_matches_pending_and_committed_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_child_root_matches_pending_and_committed(db).await; }); } #[test_traced("INFO")] fn test_keyless_rewind_recovery_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_db_rewind_recovery(ctx, db, reopen::()).await; }); } #[test_traced("INFO")] fn test_keyless_rewind_pruned_target_errors_mmb() { deterministic::Runner::default().start(|ctx| async move { let db = open_db::(ctx.with_label("db")).await; tests::test_keyless_db_rewind_pruned_target_errors(db).await; }); } fn is_send(_: T) {} #[allow(dead_code)] fn assert_db_futures_are_send( db: &mut TestDb, loc: crate::merkle::Location, ) { is_send(db.get_metadata()); is_send(db.proof(loc, NZU64!(1))); is_send(db.sync()); is_send(db.get(loc)); is_send(db.rewind(loc)); } }