Skip to main content

commonware_storage/qmdb/current/
mod.rs

1//! A _Current_ authenticated database provides succinct proofs of _any_ value ever associated with
2//! a key, and also whether that value is the _current_ value associated with it.
3//!
4//! # Examples
5//!
6//! See [`crate::qmdb::any`] for batch API examples (forking, sequential commit, staleness). The
7//! Current layer uses the same batch API.
8//!
9//! # Batch validity
10//!
11//! Reads through a batch succeed only while the DB is at a state of the batch's chain: its
12//! recorded database boundary, an ancestor's tip, or the batch's own tip. Once the DB moves to any
13//! other state, for example because a sibling or one of the batch's own descendants was applied,
14//! the batch is stale, as is each of its descendants whose chain does not include the new state.
15//! Reads through a stale batch return `StaleRead`, and merkleizing or applying it returns
16//! `StaleBatch` (see [`crate::qmdb::chain`]).
17//!
18//! For example:
19//! - If `A` is applied and `B` is then built from `A`, `B` reads and merkleizes normally.
20//! - If siblings `B1` and `B2` are built and `B1` is applied, `B2.get()` returns `StaleRead`, and
21//!   `apply_batch(B2)` and merkleizing a child of `B2` return `StaleBatch`.
22//! - If `view = db.to_batch()` is held while another batch is applied, reads through `view`
23//!   return `StaleRead`.
24//!
25//! # Motivation
26//!
27//! An [crate::qmdb::any] ("Any") database can prove that a key had a particular value at some
28//! point, but it cannot prove that the value is still current. Some later operation may have
29//! updated or deleted it. A Current database adds exactly this capability by maintaining a bitmap
30//! that tracks which operations are _active_ (i.e. represent the current state of their key).
31//!
32//! To make this useful, a verifier needs both the operation and its activity status authenticated
33//! under a single root. We achieve this by _grafting_ bitmap chunks onto the operations tree.
34//!
35//! # Data structures
36//!
37//! A Current database ([db::Db]) wraps an Any database and adds:
38//!
39//! - **Status bitmap** ([BitMap]): One bit per operation in the log. Bit _i_ is 1 if operation _i_
40//!   is active, 0 otherwise. The bitmap is divided into fixed-size chunks of `N` bytes (i.e. `N *
41//!   8` bits each). `N` must be a power of two.
42//!
43//!   One exception by convention: the *current* last commit carries bit = 1 even though a
44//!   CommitFloor is not an active update. Earlier (intermediate) CommitFloors carry bit = 0.
45//!   Maintaining this makes the chunk containing the latest commit deterministic across init and
46//!   `apply_batch`.
47//!
48//!   The bitmap lives on the inner `any::Db.bitmap`; `current::Db` reads through it for
49//!   grafted-tree leaves and proofs.
50//!
51//! - **Grafted tree**: An in-memory Merkle structure of digests at and above the _grafting height_
52//!   in the ops tree. This is the core of how bitmap and ops state are combined into a single
53//!   authenticated structure (see below).
54//!
55//! - **Graft roots**: The ops-tree root at the grafting height of each graftable bitmap chunk not
56//!   wholly below the inactivity floor, held in memory by chunk index. They survive journal
57//!   flushes, so merkleization recomputes a dirty chunk's grafted leaf without reading the ops
58//!   tree from storage. Startup and state sync rebuild them with the grafted tree, and a root is
59//!   released once the inactivity floor passes its chunk. With 32-byte chunks and 32-byte digests,
60//!   that is 32 bytes per 256 operations.
61//!
62//! - **Bitmap metadata** (`Metadata`): Persists the pruning boundary and "pinned" digests needed to
63//!   restore the grafted tree after pruning old bitmap chunks.
64//!
65//! # Grafting: combining the activity status bitmap and the ops tree
66//!
67//! ## The problem
68//!
69//! Naively authenticating the bitmap and ops tree as two independent Merkle structures would
70//! require two separate proofs per operation: one for the operation's value, one for its activity
71//! status. This doubles proof sizes.
72//!
73//! ## The solution
74//!
75//! We combine ("graft") the two structures at a specific height in the ops tree called the
76//! _grafting height_. The grafting height `h = log2(N * 8)` is chosen so that each subtree of
77//! height `h` in the ops tree covers exactly one bitmap chunk's worth of operations.
78//!
79//! At the grafting height, instead of using the ops tree's own subtree root, we replace it with a
80//! _grafted leaf_ digest that incorporates both the bitmap chunk and the ops subtree root:
81//!
82//! ```text
83//! grafted_leaf = hash(bitmap_chunk || ops_subtree_root)   // non-zero chunk
84//! grafted_leaf = ops_subtree_root                         // all-zero chunk (identity)
85//! ```
86//!
87//! The all-zero identity means that for pruned regions (where every operation is inactive), the
88//! grafted tree is structurally identical to the ops tree at and above the grafting height.
89//!
90//! Above the grafting height, internal nodes use standard hashing over the grafted leaves. Below
91//! the grafting height, the ops tree is unchanged.
92//!
93//! Not every complete chunk is graftable. A chunk is _graftable_ when its height-`h` ancestor
94//! exists as a single node in the ops tree. In MMR every complete chunk is immediately graftable.
95//! In MMB, delayed merges can leave a chunk bit-complete before its height-`h` ancestor is born;
96//! that chunk is _pending_ and its digest is hashed directly into the canonical root until the
97//! merge happens, at which point it migrates into the grafted tree. See [Pending and partial
98//! chunks](#pending-and-partial-chunks).
99//!
100//! ## Example
101//!
102//! Consider 8 operations with `N = 1` (8-bit chunks, so `h = log2(8) = 3`). But to illustrate the
103//! structure more clearly, let's use a smaller example: 8 operations with chunk size 4 bits (`h =
104//! 2`), yielding 2 complete bitmap chunks:
105//!
106//! ```text
107//! Ops tree positions (8 leaves):
108//!
109//!   Height
110//!     3              14                    <-- peak: digest commits to ops tree and bitmap chunks
111//!                  /    \
112//!                 /      \
113//!                /        \
114//!     2  [G]    6          13    [G]       <-- grafting height: grafted leaves
115//!             /   \      /    \
116//!     1      2     5    9     12           <-- below grafting height: pure ops tree nodes
117//!           / \   / \  / \   /  \
118//!     0    0   1 3   4 7  8 10  11
119//!          ^           ^
120//!          |           |
121//!      ops 0-3     ops 4-7
122//!      chunk 0     chunk 1
123//! ```
124//!
125//! Positions 6 and 13 are at the grafting height. Their digests are:
126//! - `pos 6: hash(chunk_0 || ops_subtree_root(pos 6))`
127//! - `pos 13: hash(chunk_1 || ops_subtree_root(pos 13))`
128//!
129//! Position 14 (above grafting height) is a standard internal node:
130//! - `pos 14: hash(14 || digest(pos 6) || digest(pos 13))`
131//!
132//! The grafted tree stores positions 6, 13, and 14. The ops tree stores everything below (positions
133//! 0-5 and 7-12). Together they form a single virtual Merkle structure whose root authenticates
134//! both the operations and their activity status.
135//!
136//! ## Proof generation and verification
137//!
138//! To prove that operation _i_ is active, we provide:
139//! 1. An inclusion proof for the operation's leaf, using the virtual (grafted) storage.
140//! 2. The bitmap chunk containing bit _i_.
141//!
142//! The verifier (see `grafting::Verifier`) walks the proof from leaf to root. Below the grafting
143//! height, it uses standard hashing. At the grafting height, it detects the boundary and
144//! reconstructs the grafted leaf from the chunk and the ops subtree root. For non-zero chunks the
145//! grafted leaf is `hash(chunk || ops_subtree_root)`, and for all-zero chunks it is the ops subtree
146//! root itself (the identity optimization in `grafting::Verifier::node`). Above the grafting
147//! height, it resumes standard hashing. If the reconstructed root matches the expected root and bit
148//! _i_ is set in the chunk, the operation is proven active.
149//!
150//! This is a single proof path, not two independent ones. The bitmap chunk is embedded in the
151//! proof verification at the grafting boundary.
152//!
153//! Range proofs over a window that includes the trailing pending or partial chunk additionally
154//! carry that chunk's digest in the proof and have the verifier re-derive it from the supplied
155//! chunk bytes. Graftable-chunk reconstruction is always single-peak: the height-`gh` ancestor
156//! exists in the ops tree and yields a single grafted leaf to fold against the bitmap chunk.
157//!
158//! ## Pending and partial chunks
159//!
160//! Two kinds of bitmap chunk are not grafted into the overlay at height `gh`; instead, their
161//! digests are hashed into the canonical root directly alongside `ops_root` and `grafted_root`.
162//!
163//! - **Partial chunk.** The trailing bitmap chunk is usually incomplete (fewer than `N * 8` bits).
164//!   It has no grafted leaf because no corresponding subtree exists in the ops tree.
165//!
166//! - **Pending chunk.** A chunk whose bits are complete but whose height-`gh` ancestor has not yet
167//!   been born in the ops tree. Its leaves are split across multiple sub-`gh` peaks, so there is no
168//!   single ops node to graft onto. The chunk is _deferred_ until the merge happens, at which point
169//!   it migrates into the grafted tree.
170//!
171//! Pending chunks only arise in families with delayed merges (MMB). Define `birth_chunk_0` as the
172//! value of `ops_leaves` at which chunk 0's height-`gh` ancestor is first born in the ops tree.
173//! Then chunk `i` is graftable when `ops_leaves >= i * 2^gh + birth_chunk_0`.
174//!
175//! - In MMR, `birth_chunk_0 = 2^gh`, so a chunk is graftable the moment it is bit-complete and
176//!   pending chunks never exist.
177//! - In MMB, `birth_chunk_0 = 3 * 2^(gh-1) - 1`, which exceeds the chunk size `2^gh` by `2^(gh-1) -
178//!   1`. That gap is the **pending window** for chunk `i`: ops_leaves values for which chunk `i` is
179//!   bit-complete but not yet graftable.
180//!
181//! The pending window is strictly narrower than one chunk stride, so **at most one chunk is pending
182//! at any time**. The pending and partial chunks are independent: at `gh >= 3` both can be present;
183//! at `gh == 1` the pending window is empty and only a partial chunk is ever present.
184//!
185//! ### Chunk lifecycle (MMB)
186//!
187//! ```text
188//!                       ops_leaves N grows --->
189//!
190//!   chunk i state:    incomplete    pending          graftable
191//!                    (bits being   (bits set;       (h=gh ancestor born;
192//!                     appended)    ancestor not      chunk grafted onto
193//!                                  yet born)         that ops node)
194//!
195//!   N reaches:                    (i+1)*2^gh     i*2^gh + birth_chunk_0
196//!                                 ^                  ^
197//!                                 |                  |
198//!                                 chunk completes    chunk becomes graftable
199//!
200//!   Where chunk i's bytes live:
201//!     incomplete -> canonical root, as partial_chunk_digest
202//!     pending    -> canonical root, as pending_chunk_digest
203//!     graftable  -> grafted tree
204//!
205//!   At any moment, the canonical root may include zero, one, or both of {pending, partial};
206//!   the grafted tree commits to every chunk in [pruned_chunks, graftable_chunks).
207//! ```
208//!
209//! ### Worked example: pending + partial coexistence (MMB, `gh = 3`)
210//!
211//! For `gh = 3` MMB, `birth_chunk_0 = 3 * 2^(gh-1) - 1 = 11`. Chunk 0 fills at `N = 8` but
212//! does not activate until `N = 11`. The pending window is `N` in `[8, 10]`.
213//!
214//! ```text
215//!   Snapshot at N = 10 (chunk 0 bit-complete; chunk 1 has 2 of 8 bits):
216//!
217//!     chunks (8 bits each)     chunk 0       chunk 1
218//!     ops index range          0..7          8..15
219//!     bits set                 11111111      11
220//!     state                    pending       incomplete
221//!
222//!     graftable_chunks = 0      (chunk 0's h=3 ancestor is not yet born)
223//!     grafted_root           no graftable substitutions; equals ops-tree bagged root
224//!     pending_chunk_digest   = H(chunk_0_bytes)
225//!     partial_chunk_digest   = (next_bit = 2, H(chunk_1_bytes))
226//!     canonical_root         = hash(ops_root || grafted_root || pending
227//!                                                            || next_bit || partial)
228//!
229//!   At N = 11, chunk 0's h=3 ancestor is born:
230//!     graftable_chunks becomes 1, the pending slot is dropped, chunk 0 migrates
231//!     into the grafted tree.
232//! ```
233//!
234//! See [Root structure](#root-structure) below for the canonical root layout.
235//!
236//! ## Incremental updates
237//!
238//! When operations are added or bits change (e.g. an operation becomes inactive during floor
239//! raising), only the affected chunks are marked "dirty". During `merkleize`, only dirty grafted
240//! leaves are recomputed and their ancestors are propagated upward through the cache. This avoids
241//! recomputing the entire grafted tree.
242//!
243//! ## Pruning
244//!
245//! Old bitmap chunks (below the inactivity floor) can be pruned. Before pruning, the grafted tree's
246//! peak digests covering the pruned region are persisted to metadata as "pinned nodes". On
247//! recovery, these pinned nodes are loaded and serve as opaque siblings during upward propagation,
248//! allowing the grafted tree to be rebuilt without the pruned chunks.
249//!
250//! ### Delayed-merge settlement
251//!
252//! For families with delayed merges (e.g. MMB), pruning is slightly more conservative than the
253//! inactivity floor alone would allow.
254//!
255//! The grafted root is computed by iterating the _ops tree's_ peaks and looking up the
256//! corresponding nodes in the grafted tree. After pruning, only the grafted tree's pinned peaks are
257//! available in the pruned region; interior nodes (including individual grafted leaves) are
258//! discarded. If the ops tree has a peak that maps to a discarded grafted node, root computation
259//! fails.
260//!
261//! In an MMR the ops tree's peaks within the pruned region always coincide with the grafted tree's
262//! pinned peaks, so this is never a problem. In an MMB, delayed merges cause the ops tree's peak
263//! structure to lag behind: a chunk pair's parent node at height `gh+1` is not created until some
264//! number of leaves after the pair's last leaf. Until that merge happens, the ops tree still has
265//! individual height-`gh` peaks for each chunk in the pair, and those map to grafted _leaves_
266//! (height 0 in the grafted tree), which are not pinned peaks.
267//!
268//! To avoid this, [`Db::prune`](db::Db::prune) defers bitmap pruning for chunks whose chunk-pair
269//! parent has not yet been born in the ops tree (see `Db::sync_boundary`). Once the parent is born,
270//! every ops peak within the pruned region is at height `gh+1` or above, and maps to a pinned peak
271//! or an ancestor of pinned peaks that can be reconstructed by hashing children (see
272//! `grafting::Storage::reconstruct_grafted_node`).
273//!
274//! The same birth threshold also defines a recovery floor. Selecting a size where the chunk-pair
275//! parent has not been born would re-expose the individual ops peaks and break reconstruction.
276//! Bounded initialization rejects sizes below this floor. The floor is a pure function of the
277//! pruned chunk count and the family geometry, so it does not need to be persisted. It is
278//! recomputed on startup from the pruned chunk count stored in metadata.
279//!
280//! The pruning lag is small: at most `2^(gh+1) - 1` ops beyond the chunk boundary (just under 2
281//! chunks for the default chunk size).
282//!
283//! # Root structure
284//!
285//! The canonical root of a `current` database is:
286//!
287//! ```text
288//! root = hash(
289//!     ops_root
290//!     || grafted_root
291//!     [|| pending_chunk_digest]
292//!     [|| next_bit || partial_chunk_digest]
293//! )
294//! ```
295//!
296//! Components:
297//!
298//! - **Ops root**: The root of the raw operations tree (the inner [crate::qmdb::any] database's
299//!   root). Used for state sync, where a client downloads operations and verifies each batch
300//!   against this root using ops-tree range proofs.
301//!
302//! - **Grafted root**: The bagged root of the virtual overlay (see `grafting::Storage`) that shares
303//!   the ops-tree topology but substitutes graftable chunks at height `gh`. When no chunks are
304//!   graftable, `grafted_root` still reflects the ops-tree peak structure. Used for proofs about
305//!   operation values and their activity status. See [RangeProof](proof::RangeProof) and
306//!   [OperationProof](proof::constant::OperationProof).
307//!
308//! - **Pending chunk digest** (optional): `H(pending_chunk_bytes)` when a chunk's bits are complete
309//!   but its height-`gh` ancestor has not yet been born in the ops tree. Absent in MMR and in the
310//!   steady state of MMB.
311//!
312//! - **Partial chunk** (optional): When the bitmap length is not chunk-aligned, the trailing
313//!   incomplete chunk's digest and bit count are folded in.
314//!
315//! Pending and partial slots are independent. When both are present, pending hashes in before
316//! partial.
317//!
318//! The canonical root is returned by [Db](db::Db)`::`[root()](db::Db::root). The ops root is
319//! returned by the `sync::Database` trait's `root()` method, since the sync engine verifies batches
320//! against the ops root, not the canonical root.
321//!
322//! For state sync, the sync engine targets the ops root and verifies each batch against it. Callers
323//! verifying ops proofs directly should use [`crate::qmdb::verify_proof`]. After sync, the bitmap
324//! and grafted tree are reconstructed deterministically from the operations, and the canonical root
325//! is computed. [proof::OpsRootWitness] can be used to validate that a particular ops root is
326//! committed by a trusted canonical root; the sync engine does not perform this check itself.
327
328use crate::{
329    Context,
330    index::Factory as IndexFactory,
331    journal::{
332        authenticated,
333        contiguous::{fixed::Config as FConfig, variable::Config as VConfig},
334    },
335    merkle::{self, Location, full::Config as MerkleConfig},
336    qmdb::{
337        any::{
338            self, Config as AnyConfig,
339            operation::{Operation, Update},
340        },
341        bitmap::Shared,
342    },
343    translator::Translator,
344};
345use commonware_codec::{Codec, FixedSize};
346use commonware_cryptography::Hasher;
347use commonware_macros::boxed;
348use commonware_parallel::Strategy;
349use commonware_runtime::Spawner;
350use commonware_utils::bitmap::Prunable as BitMap;
351use core::num::NonZeroUsize;
352use std::sync::Arc;
353
354pub mod batch;
355pub mod db;
356pub mod grafting;
357
358pub mod ordered;
359pub mod proof;
360pub(crate) mod sync;
361pub mod unordered;
362
363use self::db::Metrics;
364
365/// Configuration for a `Current` authenticated db.
366#[derive(Clone)]
367pub struct Config<T: Translator, J, S: Strategy, B = ()> {
368    /// Configuration for the Merkle structure backing the authenticated journal.
369    pub merkle_config: MerkleConfig<S>,
370
371    /// Configuration for the operations log journal.
372    pub journal_config: J,
373
374    /// The name of the storage partition used for grafted tree metadata.
375    pub grafted_metadata_partition: String,
376
377    /// The translator used by the compressed index. Ordered databases require an
378    /// [OrderedTranslator](crate::translator::OrderedTranslator).
379    pub translator: T,
380
381    /// Maximum number of entries in the `(location -> key)` cache used during init to resolve
382    /// snapshot collisions without re-reading the log; `None` disables it.
383    pub init_cache: Option<NonZeroUsize>,
384
385    /// Size (in bytes) of the read buffer used to replay the log during init.
386    pub init_buffer: NonZeroUsize,
387
388    /// The index's snapshot-build concurrency (see [crate::qmdb::SnapshotBuild::Concurrency]): `()`
389    /// for index types that build serially, and the number of build tasks for index types that
390    /// build in parallel. A value of `1` builds the index entirely on the init task. Values of
391    /// `2` and `3` decode on the init task and insert on one or two workers. Larger values split
392    /// between spawned decode and insert tasks while the init task merely forwards batches.
393    pub init_concurrency: B,
394}
395
396impl<T: Translator, J, S: Strategy, B> From<Config<T, J, S, B>> for AnyConfig<T, J, S, B> {
397    fn from(cfg: Config<T, J, S, B>) -> Self {
398        Self {
399            merkle_config: cfg.merkle_config,
400            journal_config: cfg.journal_config,
401            translator: cfg.translator,
402            init_cache: cfg.init_cache,
403            init_buffer: cfg.init_buffer,
404            init_concurrency: cfg.init_concurrency,
405        }
406    }
407}
408
409/// Configuration for a `Current` authenticated db with fixed-size values.
410pub type FixedConfig<T, S, B = ()> = Config<T, FConfig, S, B>;
411
412/// Configuration for a `Current` authenticated db with variable-sized values.
413pub type VariableConfig<T, C, S, B = ()> = Config<T, VConfig<C>, S, B>;
414
415/// Initialize a `Current` authenticated db from the given config.
416#[boxed]
417pub(super) async fn init<F, E, U, H, I, J, const N: usize, S>(
418    context: E,
419    config: Config<I::Translator, J::Config, S, <I as crate::qmdb::SnapshotBuild<F>>::Concurrency>,
420    max_size: Option<Location<F>>,
421) -> Result<db::Db<F, E, J, I, H, U, N, S>, crate::qmdb::Error<F>>
422where
423    F: merkle::Graftable,
424    E: Context + Spawner,
425    U: Update,
426    H: Hasher,
427    I: IndexFactory<Value = Location<F>> + crate::qmdb::SnapshotBuild<F>,
428    J: authenticated::Backing<E, Item = Operation<F, U>> + 'static,
429    S: Strategy,
430    Operation<F, U>: Codec,
431{
432    // TODO: Re-evaluate assertion placement after `generic_const_exprs` is stable.
433    const {
434        // A compile-time assertion that the chunk size is some multiple of digest size. A multiple
435        // of 1 is optimal with respect to proof size, but a higher multiple allows for a smaller
436        // (RAM resident) merkle tree over the structure.
437        assert!(
438            N.is_multiple_of(H::Digest::SIZE),
439            "chunk size must be some multiple of the digest size",
440        );
441        // A compile-time assertion that chunk size is a power of 2, which is necessary to allow
442        // the status bitmap tree to be aligned with the underlying operations MMR.
443        assert!(N.is_power_of_two(), "chunk size must be a power of 2");
444    }
445
446    let strategy = config.merkle_config.strategy.clone();
447    let metadata_partition = config.grafted_metadata_partition.clone();
448
449    // Load bitmap metadata (pruned_chunks + pinned nodes for the grafted tree).
450    let (metadata, pruned_chunks, pinned_nodes) =
451        db::init_metadata(context.child("metadata"), &metadata_partition).await?;
452
453    // Pre-build the activity-status bitmap with the known pruned-chunk count from grafted metadata.
454    let bitmap = BitMap::<N>::new_with_pruned_chunks(pruned_chunks)
455        .map_err(|_| crate::qmdb::Error::<F>::DataCorrupted("pruned chunks overflow"))?;
456    let bitmap = Arc::new(Shared::<N>::new(bitmap));
457
458    // The persisted bitmap boundary and delayed-pair absorption threshold constrain which retained
459    // commit can be reconstructed. The underlying `any` database takes sole ownership of the
460    // bitmap, validates both constraints before publishing the selected journal prefix, then
461    // populates the bitmap during snapshot rebuild.
462    let any = any::init_with_bitmap(
463        context.child("any"),
464        config.into(),
465        Some(bitmap),
466        max_size,
467        db::pair_absorption_threshold::<F, N>(pruned_chunks as u64),
468    )
469    .await?;
470
471    // Rebuild the grafted tree, graft roots, and canonical root from the persisted pins and
472    // selected operation tree.
473    let (grafted_tree, graft_roots, root) = db::rebuild_grafted_tree::<F, H, S, N>(
474        any.bitmap.as_ref(),
475        &pinned_nodes,
476        &any.log.merkle,
477        any.inactivity_floor_loc,
478        any.root(),
479        &strategy,
480    )
481    .await?;
482
483    let metrics = Metrics::new(context);
484    let db = db::Db {
485        any,
486        grafted_tree: Arc::new(grafted_tree),
487        graft_roots,
488        metadata,
489        strategy,
490        root,
491        metrics,
492        #[cfg(test)]
493        halt_before_prune_log: false,
494    };
495    db.update_metrics();
496    Ok(db)
497}
498
499/// Extension trait for Current QMDB types that exposes bitmap information for testing.
500#[cfg(any(test, feature = "test-traits"))]
501pub trait BitmapPrunedBits {
502    /// Returns the number of bits that have been pruned from the bitmap.
503    fn pruned_bits(&self) -> u64;
504
505    /// Returns the value of the bit at the given index.
506    fn get_bit(&self, index: u64) -> bool;
507
508    /// Returns the position of the oldest retained bit.
509    fn oldest_retained(&self) -> u64;
510}
511
512#[cfg(test)]
513pub mod tests {
514    //! Shared test utilities for Current QMDB variants.
515
516    pub use super::BitmapPrunedBits;
517    use super::{
518        Codec, FConfig, FixedConfig, MerkleConfig, Operation, Strategy, Update, VConfig,
519        VariableConfig, batch, db, grafting, ordered, unordered,
520    };
521    use crate::{
522        index::{Ordered as OrderedIndex, Unordered as UnorderedIndex},
523        journal::contiguous::Mutable,
524        merkle::{self, mmb, mmr, storage::Storage as _},
525        qmdb::{
526            any::{
527                ValueEncoding,
528                batch::Slot,
529                operation::update,
530                test::{
531                    Changes, Choice, Inspect, Links, Neighbors, Script, assert_bits, assert_exact,
532                    build, colliding_digest, counter, hold, hold_batch, live, replay,
533                    test_any_activity_depths, test_any_ordered_policy_eviction_matrix,
534                    test_any_ordered_policy_repair_across_ancestors,
535                    test_any_policy_ancestor_twins, test_any_policy_decisions_match_writes,
536                    test_any_policy_evicts_parent_created_key, test_any_policy_hold,
537                    test_any_policy_keep_evict_and_recover,
538                    test_any_policy_limits_after_colliding_writes,
539                    test_any_policy_limits_with_writes, test_any_policy_matches_proportional,
540                    test_any_policy_own_writes, test_any_proportional_bound,
541                    test_any_proportional_one_batch,
542                },
543                traits::{DbAny, MerkleizedBatch as _, UnmerkleizedBatch as _},
544            },
545            chain::Bounds,
546            floor::{Bounded, Hold, Proportional},
547            store::tests::{TestKey, TestValue},
548            verify_proof,
549        },
550        translator::Translator,
551    };
552    use commonware_parallel::Sequential;
553    use commonware_runtime::{
554        Runner as _, Supervisor as _,
555        buffer::paged::CacheRef,
556        deterministic::{self, Context},
557    };
558    use commonware_utils::{NZU16, NZU64, NZUsize, TestRng, bitmap::Readable};
559    use core::future::Future;
560    use ordered::tests::test_build_small_close_reopen as test_ordered_build_small_close_reopen;
561    use rand::Rng;
562    use std::{
563        collections::BTreeMap,
564        num::{NonZeroU16, NonZeroUsize},
565        ops::Range,
566        pin::Pin,
567        sync::Arc,
568    };
569    use tracing::warn;
570    use unordered::tests::test_build_small_close_reopen as test_unordered_build_small_close_reopen;
571
572    type Error<F> = crate::qmdb::Error<F>;
573    type Location<F> = merkle::Location<F>;
574    type WriteVec<F, C> = Vec<(<C as DbAny<F>>::Key, Option<<C as DbAny<F>>::Value>)>;
575
576    // Janky page & cache sizes to exercise boundary conditions.
577    const PAGE_SIZE: NonZeroU16 = NZU16!(88);
578    const PAGE_CACHE_SIZE: NonZeroUsize = NZUsize!(8);
579
580    /// Instantiate the staged-merkleize parity test for one current DB kind, with `$open_db` as
581    /// the kind's test DB constructor.
582    ///
583    /// The staged path (`stage` + `Staged::merkleize`) must produce a root byte-identical to an
584    /// explicit `get_many` + `write` + `merkleize` over the current layer, across updates, deletes
585    /// (which, for the ordered kind, rewrite predecessors via a snapshot-bucket scan), unstaged
586    /// writes, duplicate read slots, missing keys, and prefix-then-suffix expansion, rooted at the
587    /// DB (D=0) and through one or two unapplied ancestors (D=1/D=2). This guards the current-layer
588    /// threading of `bitmap_parent`/`grafted_parent`, global slot assignment across `expand`, and
589    /// `compute_current_layer` for non-empty staged updates. Collision-prone translators in
590    /// `$open_db` (e.g. `OneCap`) stress predecessor rewrites.
591    macro_rules! staged_merkleize_parity_test {
592        ($name:ident, $open_db:path) => {
593            #[test_traced("WARN")]
594            pub fn $name() {
595                fn key(i: u64) -> Digest {
596                    Sha256::hash(&[&i.to_be_bytes()])
597                }
598                fn val(i: u64) -> Digest {
599                    Sha256::hash(&[&(i + 10000).to_be_bytes()])
600                }
601
602                deterministic::Runner::default().start(|ctx| async move {
603                    let db = $open_db(ctx.child("current"), "staged-parity".to_string()).await;
604
605                    let mut seed = db.new_batch();
606                    for i in 0..2000u64 {
607                        seed = seed.write(key(i), Some(val(i)));
608                    }
609                    let seed = seed.merkleize(&db, None, &mut Proportional).await.unwrap();
610                    let db = db.apply_batch(seed).await.unwrap().commit().await.unwrap();
611
612                    for depth in [0u8, 1u8, 2u8] {
613                        // Hold every unapplied ancestor until the child is merkleized.
614                        // Speculative batch Merkle lookups walk weak parent links for in-memory
615                        // ancestor nodes.
616                        let mut stack = Vec::new();
617                        match depth {
618                            0 => {}
619                            1 => {
620                                let mut p = db.new_batch();
621                                for i in 0..50u64 {
622                                    p = p.write(key(i), Some(val(i + 1_000)));
623                                }
624                                for i in 100..110u64 {
625                                    p = p.write(key(i), None);
626                                }
627                                stack.push(
628                                    p.merkleize(&db, None, &mut Proportional)
629                                        .await
630                                        .unwrap(),
631                                );
632                            }
633                            2 => {
634                                let mut grandparent = db.new_batch();
635                                for i in 0..10u64 {
636                                    grandparent = grandparent.write(key(i), Some(val(i + 1_000)));
637                                }
638                                for i in 100..110u64 {
639                                    grandparent = grandparent.write(key(i), None);
640                                }
641                                let grandparent = grandparent
642                                    .merkleize(&db, None, &mut Proportional)
643                                    .await
644                                    .unwrap();
645
646                                let mut p = grandparent.new_batch::<Sha256>();
647                                for i in 20..30u64 {
648                                    p = p.write(key(i), Some(val(i + 2_000)));
649                                }
650                                let p = p.merkleize(&db, None, &mut Proportional).await.unwrap();
651                                stack.push(grandparent);
652                                stack.push(p);
653                            }
654                            _ => unreachable!("covered depths"),
655                        };
656                        let new_batch = || {
657                            stack
658                                .last()
659                                .map_or_else(|| db.new_batch(), |p| p.new_batch::<Sha256>())
660                        };
661
662                        // key(60) is untouched by the depth-1/2 ancestors, so its staged read
663                        // stays database-resolved and exercises staged cached-location reuse
664                        // behind stacked batches.
665                        let read_keys = [
666                            key(5),
667                            key(6),
668                            key(9000),
669                            key(5),
670                            key(0),
671                            key(20),
672                            key(60),
673                            key(105),
674                        ];
675                        let keys: Vec<&Digest> = read_keys.iter().collect();
676                        let slots = vec![
677                            Slot::Put(val(5_000)),
678                            Slot::Keep,
679                            Slot::Put(val(5_001)),
680                            Slot::Put(val(5_002)),
681                            Slot::Put(val(5_003)),
682                            Slot::Delete,
683                            Slot::Put(val(5_004)),
684                            Slot::Put(val(5_005)),
685                        ];
686                        let unstaged = vec![
687                            (key(7000), Some(val(6_000))),
688                            (key(30), Some(val(6_001))),
689                            (key(5), Some(val(6_002))),
690                            (key(31), None),
691                        ];
692
693                        let mut explicit = new_batch();
694                        let explicit_values = explicit.get_many(&keys, &db).await.unwrap();
695                        for (key, slot) in read_keys.iter().zip(&slots) {
696                            match slot {
697                                Slot::Keep => {}
698                                Slot::Put(value) => explicit = explicit.write(*key, Some(*value)),
699                                Slot::Delete => explicit = explicit.write(*key, None),
700                            }
701                        }
702                        for (k, v) in &unstaged {
703                            explicit = explicit.write(*k, *v);
704                        }
705                        let explicit_root = explicit
706                            .merkleize(&db, None, &mut Proportional)
707                            .await
708                            .unwrap()
709                            .root();
710
711                        let (staged_values, staged) = new_batch().stage(&keys, &db).await.unwrap();
712                        let staged_root = staged
713                            .merkleize(
714                                slots.clone(),
715                                unstaged.clone(),
716                                None,
717                                &db,
718                                &mut Proportional,
719                            )
720                            .await
721                            .unwrap()
722                            .root();
723
724                        assert_eq!(
725                            explicit_values, staged_values,
726                            "value mismatch at depth={depth}"
727                        );
728                        assert_eq!(explicit_root, staged_root, "root mismatch at depth={depth}");
729
730                        let split = 3;
731                        let (mut expanded_values, staged) =
732                            new_batch().stage(&keys[..split], &db).await.unwrap();
733                        let (range, suffix_values, staged) =
734                            staged.expand(&keys[split..], &db).await.unwrap();
735                        assert_eq!(range, split..keys.len());
736                        assert_eq!(staged.len(), keys.len());
737                        assert!(!staged.is_empty());
738                        expanded_values.extend(suffix_values);
739                        let expanded_root = staged
740                            .merkleize(
741                                slots.clone(),
742                                unstaged.clone(),
743                                None,
744                                &db,
745                                &mut Proportional,
746                            )
747                            .await
748                            .unwrap()
749                            .root();
750
751                        assert_eq!(
752                            explicit_values, expanded_values,
753                            "expanded value mismatch at depth={depth}"
754                        );
755                        assert_eq!(
756                            explicit_root, expanded_root,
757                            "expanded root mismatch at depth={depth}"
758                        );
759
760                        let planned = val(7_000);
761                        let duplicate_update = val(7_001);
762                        let (first_values, staged) =
763                            new_batch().stage(&keys[..1], &db).await.unwrap();
764                        let (duplicate_range, duplicate_values, staged) =
765                            staged.expand(&keys[..1], &db).await.unwrap();
766                        assert_eq!(duplicate_range, 1..2);
767                        assert_eq!(
768                            first_values[0], duplicate_values[0],
769                            "duplicate expansion must assign a new slot without changing the base read"
770                        );
771                        assert_ne!(
772                            duplicate_values[0],
773                            Some(planned),
774                            "expand must not observe values computed for earlier staged slots"
775                        );
776
777                        let duplicate_root = staged
778                            .merkleize(
779                                vec![Slot::Put(planned), Slot::Put(duplicate_update)],
780                                Vec::new(),
781                                None,
782                                &db,
783                                &mut Proportional,
784                            )
785                            .await
786                            .unwrap()
787                            .root();
788                        let expected_duplicate_root = new_batch()
789                            .write(read_keys[0], Some(planned))
790                            .write(read_keys[0], Some(duplicate_update))
791                            .merkleize(&db, None, &mut Proportional)
792                            .await
793                            .unwrap()
794                            .root();
795                        assert_eq!(
796                            expected_duplicate_root, duplicate_root,
797                            "duplicate expanded slots should use normal update-order semantics"
798                        );
799                    }
800                });
801            }
802        };
803    }
804    pub(crate) use staged_merkleize_parity_test;
805
806    /// Shared config factory for fixed-value Current QMDB tests.
807    pub(crate) fn fixed_config<T: Translator + Default>(
808        partition_prefix: &str,
809    ) -> FixedConfig<T, Sequential> {
810        fixed_config_full(partition_prefix, ())
811    }
812
813    /// Shared config construction for every fixed-value flavor, generic over the index's
814    /// snapshot-build concurrency.
815    pub(crate) fn fixed_config_full<T: Translator + Default, B>(
816        partition_prefix: &str,
817        init_concurrency: B,
818    ) -> FixedConfig<T, Sequential, B> {
819        let page_cache = CacheRef::new(PAGE_SIZE, PAGE_CACHE_SIZE);
820        FixedConfig {
821            merkle_config: MerkleConfig {
822                journal_partition: format!("{partition_prefix}-journal-partition"),
823                metadata_partition: format!("{partition_prefix}-metadata-partition"),
824                items_per_blob: NZU64!(11),
825                write_buffer: NZUsize!(1024),
826                replay_buffer: NZUsize!(1024),
827                strategy: Sequential,
828                page_cache: page_cache.clone(),
829            },
830            journal_config: FConfig {
831                partition: format!("{partition_prefix}-partition-prefix"),
832                items_per_blob: NZU64!(7),
833                page_cache,
834                write_buffer: NZUsize!(1024),
835                replay_buffer: NZUsize!(1024),
836            },
837            grafted_metadata_partition: format!("{partition_prefix}-grafted-metadata-partition"),
838            translator: T::default(),
839            init_cache: Some(NZUsize!(1024)),
840            init_buffer: NZUsize!(1 << 21),
841            init_concurrency,
842        }
843    }
844
845    /// Shared config factory for variable-value Current QMDB tests with unit codec config.
846    pub(crate) fn variable_config<T: Translator + Default>(
847        partition_prefix: &str,
848    ) -> VariableConfig<T, ((), ()), Sequential> {
849        variable_config_full(partition_prefix, ())
850    }
851
852    /// Shared config construction for every variable-value flavor, generic over the index's
853    /// snapshot-build concurrency.
854    pub(crate) fn variable_config_full<T: Translator + Default, B>(
855        partition_prefix: &str,
856        init_concurrency: B,
857    ) -> VariableConfig<T, ((), ()), Sequential, B> {
858        let page_cache = CacheRef::new(PAGE_SIZE, PAGE_CACHE_SIZE);
859        VariableConfig {
860            merkle_config: MerkleConfig {
861                journal_partition: format!("{partition_prefix}-journal-partition"),
862                metadata_partition: format!("{partition_prefix}-metadata-partition"),
863                items_per_blob: NZU64!(11),
864                write_buffer: NZUsize!(1024),
865                replay_buffer: NZUsize!(1024),
866                strategy: Sequential,
867                page_cache: page_cache.clone(),
868            },
869            journal_config: VConfig {
870                partition: format!("{partition_prefix}-partition-prefix"),
871                items_per_section: NZU64!(7),
872                compression: None,
873                codec_config: ((), ()),
874                page_cache,
875                write_buffer: NZUsize!(1024),
876                replay_buffer: NZUsize!(1024),
877            },
878            grafted_metadata_partition: format!("{partition_prefix}-grafted-metadata-partition"),
879            translator: T::default(),
880            init_cache: Some(NZUsize!(1024)),
881            init_buffer: NZUsize!(1 << 21),
882            init_concurrency,
883        }
884    }
885
886    /// Like [fixed_config], typed for a partitioned index at the serial concurrency.
887    pub(crate) fn fixed_config_partitioned<T: Translator + Default>(
888        partition_prefix: &str,
889    ) -> FixedConfig<T, Sequential, NonZeroUsize> {
890        fixed_config_full(partition_prefix, NZUsize!(1))
891    }
892
893    /// Like [variable_config], typed for a partitioned index at the serial concurrency.
894    pub(crate) fn variable_config_partitioned<T: Translator + Default>(
895        partition_prefix: &str,
896    ) -> VariableConfig<T, ((), ()), Sequential, NonZeroUsize> {
897        variable_config_full(partition_prefix, NZUsize!(1))
898    }
899
900    /// Commit a set of writes as a single batch.
901    ///
902    /// Returns a boxed future so the merkleize/apply/commit chain does not inflate the futures
903    /// (and poll frames) of every test composing commits, which overflow the stack on platforms
904    /// with small defaults.
905    fn commit_writes<'a, F: merkle::Graftable, C: DbAny<F> + 'a>(
906        db: C,
907        writes: impl IntoIterator<Item = (C::Key, Option<<C as DbAny<F>>::Value>)> + 'a,
908    ) -> std::pin::Pin<Box<dyn Future<Output = Result<C, Error<F>>> + 'a>> {
909        Box::pin(async move {
910            let mut batch = db.new_batch();
911            for (k, v) in writes {
912                batch = batch.write(k, v);
913            }
914            let merkleized = batch.merkleize(&db, None, &mut Proportional).await?;
915            let db = db.apply_batch(merkleized).await?;
916            db.commit().await
917        })
918    }
919
920    /// Apply random operations to the given db, committing them (randomly and at the end) only if
921    /// `commit_changes` is true. Returns the db; callers should commit if needed.
922    #[boxed]
923    pub async fn apply_random_ops<F, C>(
924        num_elements: u64,
925        commit_changes: bool,
926        rng_seed: u64,
927        mut db: C,
928    ) -> Result<C, Error<F>>
929    where
930        F: merkle::Graftable,
931        C: DbAny<F>,
932        C::Key: TestKey,
933        <C as DbAny<F>>::Value: TestValue,
934    {
935        // Log the seed with high visibility to make failures reproducible.
936        warn!("rng_seed={}", rng_seed);
937        let mut rng = TestRng::new(rng_seed);
938
939        // First loop: all initial writes in one batch.
940        let writes: Vec<_> = (0u64..num_elements)
941            .map(|i| {
942                let k = TestKey::from_seed(i);
943                let v = TestValue::from_seed(rng.next_u64());
944                (k, Some(v))
945            })
946            .collect();
947        if commit_changes {
948            db = commit_writes(db, writes).await?;
949        }
950
951        // Randomly update / delete them. We use a delete frequency that is 1/7th of the update
952        // frequency. Accumulate writes and commit periodically.
953        let mut pending: WriteVec<F, C> = Vec::new();
954        for _ in 0u64..num_elements * 10 {
955            let rand_key = TestKey::from_seed(rng.next_u64() % num_elements);
956            if rng.next_u32().is_multiple_of(7) {
957                pending.push((rand_key, None));
958                continue;
959            }
960            let v = TestValue::from_seed(rng.next_u64());
961            pending.push((rand_key, Some(v)));
962            if commit_changes && rng.next_u32().is_multiple_of(20) {
963                db = commit_writes(db, pending.drain(..)).await?;
964            }
965        }
966        if commit_changes {
967            db = commit_writes(db, pending).await?;
968        }
969        Ok(db)
970    }
971
972    /// Build a random database, close and reopen it, and return the auditor state.
973    #[boxed]
974    async fn build_random_close_reopen_round<M, C, F, Fut>(
975        mut context: Context,
976        mut open_db: F,
977    ) -> String
978    where
979        M: merkle::Graftable + 'static,
980        C: DbAny<M> + 'static,
981        C::Key: TestKey,
982        <C as DbAny<M>>::Value: TestValue,
983        F: FnMut(Context, String) -> Fut,
984        Fut: Future<Output = C>,
985    {
986        const ELEMENTS: u64 = 1000;
987
988        let partition = "build-random".to_string();
989        let rng_seed = context.next_u64();
990        let db: C = open_db(context.child("first"), partition.clone()).await;
991        let db = apply_random_ops::<M, C>(ELEMENTS, true, rng_seed, db)
992            .await
993            .unwrap();
994        let merkleized = db
995            .new_batch()
996            .merkleize(&db, None, &mut Proportional)
997            .await
998            .unwrap();
999        let db = db.apply_batch(merkleized).await.unwrap();
1000        let db = db.sync().await.unwrap();
1001
1002        // Drop and reopen the db
1003        let root = db.root();
1004        drop(db);
1005        let db: C = open_db(context.child("second"), partition).await;
1006
1007        // Ensure the root matches
1008        assert_eq!(db.root(), root);
1009
1010        db.destroy().await.unwrap();
1011        context.auditor().state()
1012    }
1013
1014    /// Run `test_build_random_close_reopen` against a database factory.
1015    ///
1016    /// The factory should return a database when given a context and partition name.
1017    /// The factory will be called multiple times to test reopening.
1018    pub async fn test_build_random_close_reopen<M, C, F, Fut>(context: Context, open_db: F)
1019    where
1020        M: merkle::Graftable,
1021        C: DbAny<M> + 'static,
1022        C::Key: TestKey,
1023        <C as DbAny<M>>::Value: TestValue,
1024        F: FnMut(Context, String) -> Fut + Clone,
1025        Fut: Future<Output = C>,
1026    {
1027        // Run on the provided runner.
1028        let state1 =
1029            build_random_close_reopen_round::<M, C, F, Fut>(context, open_db.clone()).await;
1030
1031        // Run again on a fresh runner to verify determinism.
1032        let executor = deterministic::Runner::default();
1033        let state2 = executor
1034            .start(|context| build_random_close_reopen_round::<M, C, F, Fut>(context, open_db));
1035
1036        assert_eq!(state1, state2);
1037    }
1038
1039    /// Run `test_commit_after_sync_recovery` against a database factory.
1040    pub async fn test_commit_after_sync_recovery<M, C, F, Fut>(context: Context, mut open_db: F)
1041    where
1042        M: merkle::Graftable,
1043        C: DbAny<M> + 'static,
1044        C::Key: TestKey,
1045        <C as DbAny<M>>::Value: TestValue,
1046        F: FnMut(Context, String) -> Fut + Clone,
1047        Fut: Future<Output = C>,
1048    {
1049        let mut open_db_clone = open_db.clone();
1050        let partition = "commit-after-sync".to_string();
1051        let db: C = Box::pin(open_db_clone(context.child("first"), partition.clone())).await;
1052        let key0 = <<C as DbAny<M>>::Key as TestKey>::from_seed(0);
1053        let key1 = <<C as DbAny<M>>::Key as TestKey>::from_seed(1);
1054        let value0 = <C as DbAny<M>>::Value::from_seed(100);
1055        let value1 = <C as DbAny<M>>::Value::from_seed(200);
1056
1057        // Establish a synced baseline so metadata and journal recovery start from it.
1058        let db = commit_writes::<M, C>(db, [(key0, Some(value0.clone()))])
1059            .await
1060            .unwrap();
1061        let db = db.sync().await.unwrap();
1062
1063        // Commit a later batch without syncing metadata; reopen must rebuild it from the log.
1064        let db = commit_writes::<M, C>(db, [(key1, Some(value1.clone()))])
1065            .await
1066            .unwrap();
1067        let committed_root = db.root();
1068        let committed_size = db.size();
1069        drop(db);
1070
1071        let db: C = Box::pin(open_db(context.child("second"), partition)).await;
1072        assert_eq!(db.root(), committed_root);
1073        assert_eq!(db.size(), committed_size);
1074        assert_eq!(db.get(&key0).await.unwrap(), Some(value0));
1075        assert_eq!(db.get(&key1).await.unwrap(), Some(value1));
1076
1077        db.destroy().await.unwrap();
1078    }
1079
1080    /// Run `test_simulate_write_failures` against a database factory.
1081    ///
1082    /// This test builds a random database and simulates recovery from different types of
1083    /// failure scenarios.
1084    pub async fn test_simulate_write_failures<M, C, F, Fut>(mut context: Context, mut open_db: F)
1085    where
1086        M: merkle::Graftable,
1087        C: DbAny<M> + 'static,
1088        C::Key: TestKey,
1089        <C as DbAny<M>>::Value: TestValue,
1090        F: FnMut(Context, String) -> Fut + Clone,
1091        Fut: Future<Output = C>,
1092    {
1093        const ELEMENTS: u64 = 1000;
1094
1095        let partition = "build-random-fail-commit".to_string();
1096        let rng_seed = context.next_u64();
1097        let db: C = Box::pin(open_db(context.child("first"), partition.clone())).await;
1098        let db = apply_random_ops::<M, C>(ELEMENTS, true, rng_seed, db)
1099            .await
1100            .unwrap();
1101        let db = commit_writes(db, []).await.unwrap();
1102        let committed_root = db.root();
1103        let committed_op_count = db.bounds().end;
1104        let boundary = db.sync_boundary();
1105        let db = db.prune(boundary).await.unwrap();
1106
1107        // Perform more random operations without committing any of them.
1108        let db = apply_random_ops::<M, C>(ELEMENTS, false, rng_seed + 1, db)
1109            .await
1110            .unwrap();
1111
1112        // SCENARIO #1: Simulate a crash that happens before any writes. Upon reopening, the
1113        // state of the DB should be as of the last commit.
1114        drop(db);
1115        let db: C = Box::pin(open_db(
1116            context.child("scenario").with_attribute("index", 1),
1117            partition.clone(),
1118        ))
1119        .await;
1120        assert_eq!(db.root(), committed_root);
1121        assert_eq!(db.bounds().end, committed_op_count);
1122
1123        // Re-apply the exact same operations, this time committed.
1124        let db = apply_random_ops::<M, C>(ELEMENTS, true, rng_seed + 1, db)
1125            .await
1126            .unwrap();
1127
1128        // SCENARIO #2: Simulate a crash that happens after the any db has been committed, but
1129        // before sync/prune is called. We do this by dropping the db without calling
1130        // sync or prune.
1131        let committed_op_count = db.bounds().end;
1132        drop(db);
1133
1134        // We should be able to recover, so the root should differ from the previous commit, and
1135        // the op count should be greater than before.
1136        let db: C = Box::pin(open_db(
1137            context.child("scenario").with_attribute("index", 2),
1138            partition.clone(),
1139        ))
1140        .await;
1141        let scenario_2_root = db.root();
1142
1143        // To confirm the second committed hash is correct we'll re-build the DB in a new
1144        // partition, but without any failures. They should have the exact same state.
1145        let fresh_partition = "build-random-fail-commit-fresh".to_string();
1146        let db: C = Box::pin(open_db(context.child("fresh"), fresh_partition.clone())).await;
1147        let db = apply_random_ops::<M, C>(ELEMENTS, true, rng_seed, db)
1148            .await
1149            .unwrap();
1150        let db = commit_writes(db, []).await.unwrap();
1151        let db = apply_random_ops::<M, C>(ELEMENTS, true, rng_seed + 1, db)
1152            .await
1153            .unwrap();
1154        let boundary = db.sync_boundary();
1155        let db = db.prune(boundary).await.unwrap();
1156        // State from scenario #2 should match that of a successful commit.
1157        assert_eq!(db.bounds().end, committed_op_count);
1158        assert_eq!(db.root(), scenario_2_root);
1159
1160        db.destroy().await.unwrap();
1161    }
1162
1163    /// Run `test_different_pruning_delays_same_root` against a database factory.
1164    ///
1165    /// This test verifies that pruning operations do not affect the root hash. Two databases
1166    /// with identical operations but different pruning schedules should have the same root.
1167    pub async fn test_different_pruning_delays_same_root<M, C, F, Fut>(
1168        context: Context,
1169        mut open_db: F,
1170    ) where
1171        M: merkle::Graftable,
1172        C: DbAny<M>,
1173        C::Key: TestKey,
1174        <C as DbAny<M>>::Value: TestValue,
1175        F: FnMut(Context, String) -> Fut + Clone,
1176        Fut: Future<Output = C>,
1177    {
1178        const NUM_OPERATIONS: u64 = 1000;
1179
1180        let mut open_db_clone = open_db.clone();
1181        // Create two databases that are identical other than how they are pruned.
1182        let mut db_no_pruning: C = Box::pin(open_db_clone(
1183            context.child("no_pruning"),
1184            "no-pruning-test".into(),
1185        ))
1186        .await;
1187        let mut db_pruning: C =
1188            Box::pin(open_db(context.child("pruning"), "pruning-test".into())).await;
1189
1190        // Apply identical operations to both databases, but only prune one.
1191        // Accumulate writes between commits.
1192        let mut pending_no_pruning: WriteVec<M, C> = Vec::new();
1193        let mut pending_pruning: WriteVec<M, C> = Vec::new();
1194        for i in 0..NUM_OPERATIONS {
1195            let key: C::Key = TestKey::from_seed(i);
1196            let value: <C as DbAny<M>>::Value = TestValue::from_seed(i * 1000);
1197
1198            pending_no_pruning.push((key, Some(value.clone())));
1199            pending_pruning.push((key, Some(value)));
1200
1201            // Commit periodically
1202            if i % 50 == 49 {
1203                db_no_pruning = commit_writes(db_no_pruning, pending_no_pruning.drain(..))
1204                    .await
1205                    .unwrap();
1206                db_pruning = commit_writes(db_pruning, pending_pruning.drain(..))
1207                    .await
1208                    .unwrap();
1209                db_pruning = db_pruning
1210                    .prune(db_no_pruning.sync_boundary())
1211                    .await
1212                    .unwrap();
1213            }
1214        }
1215
1216        // Final commit for remaining writes.
1217        let db_no_pruning = commit_writes(db_no_pruning, pending_no_pruning)
1218            .await
1219            .unwrap();
1220        let db_pruning = commit_writes(db_pruning, pending_pruning).await.unwrap();
1221
1222        // Get roots from both databases. They should match.
1223        let root_no_pruning = db_no_pruning.root();
1224        let root_pruning = db_pruning.root();
1225        assert_eq!(root_no_pruning, root_pruning);
1226
1227        // Also verify inactivity floors match
1228        assert_eq!(
1229            db_no_pruning.inactivity_floor_loc(),
1230            db_pruning.inactivity_floor_loc()
1231        );
1232
1233        db_no_pruning.destroy().await.unwrap();
1234        db_pruning.destroy().await.unwrap();
1235    }
1236
1237    /// Run `test_sync_persists_bitmap_pruning_boundary` against a database factory.
1238    ///
1239    /// This test verifies that calling `sync()` persists the bitmap pruning boundary that was
1240    /// set during `commit()`. If `sync()` didn't call `write_pruned`, the
1241    /// `pruned_bits()` count would be 0 after reopen instead of the expected value.
1242    pub async fn test_sync_persists_bitmap_pruning_boundary<M, C, F, Fut>(
1243        mut context: Context,
1244        mut open_db: F,
1245    ) where
1246        M: merkle::Graftable,
1247        C: DbAny<M> + BitmapPrunedBits + 'static,
1248        C::Key: TestKey,
1249        <C as DbAny<M>>::Value: TestValue,
1250        F: FnMut(Context, String) -> Fut + Clone,
1251        Fut: Future<Output = C>,
1252    {
1253        const ELEMENTS: u64 = 500;
1254
1255        let mut open_db_clone = open_db.clone();
1256        let partition = "sync-bitmap-pruning".to_string();
1257        let rng_seed = context.next_u64();
1258        let db: C = Box::pin(open_db_clone(context.child("first"), partition.clone())).await;
1259
1260        // Apply random operations with commits to advance the inactivity floor.
1261        let db = apply_random_ops::<M, C>(ELEMENTS, true, rng_seed, db)
1262            .await
1263            .unwrap();
1264        let merkleized = db
1265            .new_batch()
1266            .merkleize(&db, None, &mut Proportional)
1267            .await
1268            .unwrap();
1269        let db = db.apply_batch(merkleized).await.unwrap();
1270
1271        // Prune to flatten bitmap layers and advance pruned_chunks.
1272        let boundary = db.sync_boundary();
1273        let db = db.prune(boundary).await.unwrap();
1274
1275        let pruned_bits_before = db.pruned_bits();
1276        warn!(
1277            "pruned_bits_before={}, inactivity_floor={}, op_count={}",
1278            pruned_bits_before,
1279            *db.inactivity_floor_loc(),
1280            *db.bounds().end
1281        );
1282
1283        // Verify we actually have some pruning (otherwise the test is meaningless).
1284        assert!(
1285            pruned_bits_before > 0,
1286            "Expected bitmap to have pruned bits after prune()"
1287        );
1288
1289        // Call sync() to persist the bitmap pruning boundary.
1290        let db = db.sync().await.unwrap();
1291
1292        // Record the root before dropping.
1293        let root_before = db.root();
1294        drop(db);
1295
1296        // Reopen the database.
1297        let db: C = Box::pin(open_db(context.child("second"), partition)).await;
1298
1299        // The pruned bits count should match. If sync() didn't persist the bitmap pruned
1300        // state, this would be 0.
1301        let pruned_bits_after = db.pruned_bits();
1302        warn!("pruned_bits_after={}", pruned_bits_after);
1303
1304        assert_eq!(
1305            pruned_bits_after, pruned_bits_before,
1306            "Bitmap pruned bits mismatch after reopen - sync() may not have called write_pruned()"
1307        );
1308
1309        // Also verify the root matches.
1310        assert_eq!(db.root(), root_before);
1311
1312        db.destroy().await.unwrap();
1313    }
1314
1315    /// Run `test_current_db_build_big` against a database factory.
1316    ///
1317    /// This test builds a database with 1000 keys, updates some, deletes some, and verifies that
1318    /// the final state matches an independently computed HashMap. It also verifies that the state
1319    /// persists correctly after close and reopen.
1320    pub async fn test_current_db_build_big<M, C, F, Fut>(context: Context, mut open_db: F)
1321    where
1322        M: merkle::Graftable,
1323        C: DbAny<M>,
1324        C::Key: TestKey,
1325        <C as DbAny<M>>::Value: TestValue,
1326        F: FnMut(Context, String) -> Fut + Clone,
1327        Fut: Future<Output = C>,
1328    {
1329        const ELEMENTS: u64 = 1000;
1330
1331        let mut open_db_clone = open_db.clone();
1332        let db: C = Box::pin(open_db_clone(context.child("first"), "build-big".into())).await;
1333
1334        let mut map = std::collections::HashMap::<C::Key, <C as DbAny<M>>::Value>::default();
1335
1336        // All creates, updates, and deletes in one batch.
1337        let mut batch = db.new_batch();
1338
1339        // Initial creates
1340        for i in 0u64..ELEMENTS {
1341            let k: C::Key = TestKey::from_seed(i);
1342            let v: <C as DbAny<M>>::Value = TestValue::from_seed(i * 1000);
1343            batch = batch.write(k, Some(v.clone()));
1344            map.insert(k, v);
1345        }
1346
1347        // Update every 3rd key
1348        for i in 0u64..ELEMENTS {
1349            if i % 3 != 0 {
1350                continue;
1351            }
1352            let k: C::Key = TestKey::from_seed(i);
1353            let v: <C as DbAny<M>>::Value = TestValue::from_seed((i + 1) * 10000);
1354            batch = batch.write(k, Some(v.clone()));
1355            map.insert(k, v);
1356        }
1357
1358        // Delete every 7th key
1359        for i in 0u64..ELEMENTS {
1360            if i % 7 != 1 {
1361                continue;
1362            }
1363            let k: C::Key = TestKey::from_seed(i);
1364            batch = batch.write(k, None);
1365            map.remove(&k);
1366        }
1367
1368        let merkleized = batch.merkleize(&db, None, &mut Proportional).await.unwrap();
1369        let db = db.apply_batch(merkleized).await.unwrap();
1370
1371        // Sync and prune.
1372        let db = db.sync().await.unwrap();
1373        let boundary = db.sync_boundary();
1374        let db = db.prune(boundary).await.unwrap();
1375
1376        // Record root before dropping.
1377        let root = db.root();
1378        db.sync().await.unwrap();
1379
1380        // Reopen the db and verify it has exactly the same state.
1381        let db: C = Box::pin(open_db(context.child("second"), "build-big".into())).await;
1382        assert_eq!(root, db.root());
1383
1384        // Confirm the db's state matches that of the separate map we computed independently.
1385        for i in 0u64..ELEMENTS {
1386            let k: C::Key = TestKey::from_seed(i);
1387            if let Some(map_value) = map.get(&k) {
1388                let Some(db_value) = db.get(&k).await.unwrap() else {
1389                    panic!("key not found in db: {k}");
1390                };
1391                assert_eq!(*map_value, db_value);
1392            } else {
1393                assert!(db.get(&k).await.unwrap().is_none());
1394            }
1395        }
1396    }
1397
1398    /// Run `test_stale_batch_side_effect_free` against a database factory.
1399    ///
1400    /// The stale batch must be rejected without mutating the committed state.
1401    pub async fn test_stale_batch_side_effect_free<M, C, F, Fut>(context: Context, mut open_db: F)
1402    where
1403        M: merkle::Graftable,
1404        C: DbAny<M>,
1405        C::Key: TestKey,
1406        <C as DbAny<M>>::Value: TestValue,
1407        F: FnMut(Context, String) -> Fut,
1408        Fut: Future<Output = C>,
1409    {
1410        let db: C = Box::pin(open_db(
1411            context.child("db"),
1412            "stale-side-effect-free".into(),
1413        ))
1414        .await;
1415
1416        let key1 = <C::Key as TestKey>::from_seed(1);
1417        let key2 = <C::Key as TestKey>::from_seed(2);
1418        let value1 = <<C as DbAny<M>>::Value as TestValue>::from_seed(10);
1419        let value2 = <<C as DbAny<M>>::Value as TestValue>::from_seed(20);
1420
1421        let mut batch = db.new_batch();
1422        batch = batch.write(key1, Some(value1.clone()));
1423        let batch_a = batch.merkleize(&db, None, &mut Proportional).await.unwrap();
1424        let mut batch = db.new_batch();
1425        batch = batch.write(key2, Some(value2));
1426        let batch_b = batch.merkleize(&db, None, &mut Proportional).await.unwrap();
1427
1428        let db = db.apply_batch(batch_a).await.unwrap();
1429        let db = db.commit().await.unwrap();
1430        let expected_root = db.root();
1431        let expected_bounds = db.bounds();
1432        let expected_metadata = db.get_metadata().await.unwrap();
1433        assert_eq!(db.get(&key1).await.unwrap(), Some(value1.clone()));
1434        assert_eq!(db.get(&key2).await.unwrap(), None);
1435
1436        let Err(err) = db.apply_batch(batch_b).await else {
1437            panic!("expected StaleBatch error");
1438        };
1439        assert!(
1440            matches!(err, Error::StaleBatch),
1441            "expected StaleBatch error, got {err:?}"
1442        );
1443
1444        // Reopen and confirm the stale batch left the committed state untouched.
1445        let db: C = Box::pin(open_db(
1446            context.child("reopen"),
1447            "stale-side-effect-free".into(),
1448        ))
1449        .await;
1450        assert_eq!(db.root(), expected_root);
1451        assert_eq!(db.bounds(), expected_bounds);
1452        assert_eq!(db.get_metadata().await.unwrap(), expected_metadata);
1453        assert_eq!(db.get(&key1).await.unwrap(), Some(value1));
1454        assert_eq!(db.get(&key2).await.unwrap(), None);
1455
1456        db.destroy().await.unwrap();
1457    }
1458
1459    /// Root, bounds, floor, and activity bits of a current db.
1460    struct Observed<M: merkle::Family> {
1461        /// Root.
1462        root: Digest,
1463        /// Retained operation range.
1464        bounds: Range<Location<M>>,
1465        /// Inactivity floor.
1466        floor: Location<M>,
1467        /// Bits pruned from the bitmap.
1468        pruned: u64,
1469        /// Activity bits in `[pruned, bounds.end)`.
1470        bits: Vec<bool>,
1471    }
1472
1473    impl<M: merkle::Family> Observed<M> {
1474        /// Observe `db`.
1475        fn capture<C>(db: &C) -> Self
1476        where
1477            C: DbAny<M, Digest = Digest> + BitmapPrunedBits,
1478        {
1479            let bounds = db.bounds();
1480            let pruned = db.pruned_bits();
1481            Self {
1482                root: db.root(),
1483                floor: db.inactivity_floor_loc(),
1484                bits: (pruned..*bounds.end).map(|i| db.get_bit(i)).collect(),
1485                pruned,
1486                bounds,
1487            }
1488        }
1489
1490        /// Assert the bits of `self` equal those of `expected` above both pruned prefixes.
1491        fn assert_bits(&self, expected: &Self, label: &str) {
1492            assert_eq!(
1493                self.bounds.end, expected.bounds.end,
1494                "{label}: size diverged",
1495            );
1496            let from = self.pruned.max(expected.pruned);
1497            assert_eq!(
1498                self.bits[(from - self.pruned) as usize..],
1499                expected.bits[(from - expected.pruned) as usize..],
1500                "{label}: activity bits diverged",
1501            );
1502        }
1503    }
1504
1505    /// Key `i` of [test_chained_schedules_match_sequential], for `i < 1024`. Keys sort by index,
1506    /// so a batch writes them in index order, and four consecutive indices share a translated
1507    /// bucket.
1508    fn indexed(i: u64) -> Digest {
1509        colliding_digest((i / 4) as u8, i)
1510    }
1511
1512    /// A chain applied under different apply, drop, and prune schedules must leave the root,
1513    /// size, floor, activity bits, and grafted tree that applying its batches one at a time
1514    /// leaves, and the same bounds when the schedule does not prune. A follow-on batch checks
1515    /// the DB's grafted tree, which a reopen rebuilds.
1516    pub async fn test_chained_schedules_match_sequential<M, U, const N: usize, S, C, F, Fut>(
1517        context: Context,
1518        mut open_db: F,
1519    ) where
1520        M: merkle::Graftable,
1521        U: Update,
1522        S: Strategy,
1523        Operation<M, U>: Codec,
1524        C: DbAny<
1525                M,
1526                Key = Digest,
1527                Value = Digest,
1528                Digest = Digest,
1529                Merkleized = Arc<batch::MerkleizedBatch<M, Digest, U, N, S>>,
1530                Batch = batch::UnmerkleizedBatch<M, Sha256, U, N, S>,
1531            > + BitmapPrunedBits,
1532        F: FnMut(Context, String) -> Fut,
1533        Fut: Future<Output = C>,
1534    {
1535        const SEED: u64 = 2 * CHUNK_BITS + 50;
1536        let k0 = indexed(0);
1537        let k1 = indexed(2 * CHUNK_BITS + 10);
1538        let kx = indexed(SEED);
1539        let c1 = indexed(CHUNK_BITS + 50);
1540        let fresh = SEED + 1;
1541        let keys: Vec<_> = (0..=fresh + CHUNK_BITS).map(indexed).collect();
1542
1543        // The seed places `c1` in chunk 1. A's floor raise moves it into A's tail, in a complete
1544        // chunk that F leaves untouched. F's root then reads C's overwrite of that grafted leaf
1545        // from the DB's grafted tree.
1546        let seed: WriteVec<M, C> = (0..SEED).map(|i| (indexed(i), Some(val(i)))).collect();
1547
1548        // A overwrites keys `0..CHUNK_BITS`, deletes k1, and creates kx.
1549        let mut a: WriteVec<M, C> = (0..CHUNK_BITS)
1550            .map(|i| (indexed(i), Some(val(SEED + i))))
1551            .collect();
1552        a.extend([(k1, None), (kx, Some(val(5000)))]);
1553
1554        // B updates k0, recreates k1, deletes kx, and creates enough keys to complete a chunk.
1555        let mut b: WriteVec<M, C> = vec![(k0, Some(val(5001))), (k1, Some(val(5002))), (kx, None)];
1556        b.extend((fresh..fresh + CHUNK_BITS).map(|i| (indexed(i), Some(val(i)))));
1557
1558        // E is empty. C updates k0, deletes k1, and updates c1. D updates k0 and recreates kx.
1559        let e: WriteVec<M, C> = Vec::new();
1560        let c: WriteVec<M, C> = vec![(k0, Some(val(5003))), (k1, None), (c1, Some(val(5004)))];
1561        let d: WriteVec<M, C> = vec![(k0, Some(val(5005))), (kx, Some(val(5006)))];
1562
1563        // F follows D: it updates k0 and c1 and creates one key.
1564        let f: WriteVec<M, C> = vec![
1565            (k0, Some(val(5007))),
1566            (c1, Some(val(5008))),
1567            (indexed(fresh + CHUNK_BITS), Some(val(5009))),
1568        ];
1569
1570        // Reference: apply A, B, E, C, and D one at a time.
1571        let reference: C =
1572            Box::pin(open_db(context.child("reference"), "chained-ref".into())).await;
1573        let reference = commit_writes(reference, seed.clone()).await.unwrap();
1574        let reference = commit_writes(reference, a.clone()).await.unwrap();
1575        let size_a = *reference.size();
1576        let reference = commit_writes(reference, b.clone()).await.unwrap();
1577        let size_b = *reference.size();
1578        let reference = commit_writes(reference, e.clone()).await.unwrap();
1579        let reference = commit_writes(reference, c.clone()).await.unwrap();
1580        let size_c = *reference.size();
1581        let reference = commit_writes(reference, d.clone()).await.unwrap();
1582
1583        // B appends a grafted leaf, while E and C only overwrite leaves.
1584        assert!(
1585            size_b / CHUNK_BITS > size_a / CHUNK_BITS,
1586            "B must complete a chunk",
1587        );
1588        assert_eq!(
1589            size_c / CHUNK_BITS,
1590            size_b / CHUNK_BITS,
1591            "E and C must not complete a chunk",
1592        );
1593        let expected = Observed::capture(&reference);
1594
1595        // Merkleize and apply F on the reference, then record every value.
1596        let f_batch = build(&reference, reference.new_batch(), &f).await;
1597        let f_root = f_batch.root();
1598        let reference = reference.apply_batch(f_batch).await.unwrap();
1599        let expected_f = Observed::capture(&reference);
1600        let mut values = Vec::with_capacity(keys.len());
1601        for key in &keys {
1602            values.push(reference.get(key).await.unwrap());
1603        }
1604        reference.destroy().await.unwrap();
1605
1606        for schedule in 1..=5u64 {
1607            let partition = format!("chained-{schedule}");
1608            let db: C = Box::pin(open_db(
1609                context.child("schedule").with_attribute("index", schedule),
1610                partition.clone(),
1611            ))
1612            .await;
1613            let db = commit_writes(db, seed.clone()).await.unwrap();
1614
1615            // Build A <- B <- E <- C on the seeded db.
1616            let a_batch = build(&db, db.new_batch(), &a).await;
1617            let b_batch = build(&db, a_batch.new_batch::<Sha256>(), &b).await;
1618            let e_batch = build(&db, b_batch.new_batch::<Sha256>(), &e).await;
1619            let c_batch = build(&db, e_batch.new_batch::<Sha256>(), &c).await;
1620
1621            // Apply D under the schedule. Only S5 prunes, which moves the retained start.
1622            let (db, pruned) = match schedule {
1623                // S1: apply D over all-unapplied ancestors.
1624                1 => {
1625                    let d_batch = build(&db, c_batch.new_batch::<Sha256>(), &d).await;
1626                    (db.apply_batch(d_batch).await.unwrap(), false)
1627                }
1628
1629                // S2: apply A, then a D built before A was applied.
1630                2 => {
1631                    let d_batch = build(&db, c_batch.new_batch::<Sha256>(), &d).await;
1632                    let db = db.apply_batch(a_batch).await.unwrap();
1633                    (db.apply_batch(d_batch).await.unwrap(), false)
1634                }
1635
1636                // S3: apply A and drop it, then merkleize D on C and apply it.
1637                3 => {
1638                    let db = db.apply_batch(a_batch).await.unwrap();
1639                    let d_batch = build(&db, c_batch.new_batch::<Sha256>(), &d).await;
1640                    (db.apply_batch(d_batch).await.unwrap(), false)
1641                }
1642
1643                // S4: as S3, but apply B before D.
1644                4 => {
1645                    let db = db.apply_batch(a_batch).await.unwrap();
1646                    let d_batch = build(&db, c_batch.new_batch::<Sha256>(), &d).await;
1647                    let db = db.apply_batch(b_batch).await.unwrap();
1648                    (db.apply_batch(d_batch).await.unwrap(), false)
1649                }
1650
1651                // S5: apply A and prune to the sync boundary, then apply a D built before the
1652                // prune.
1653                5 => {
1654                    let d_batch = build(&db, c_batch.new_batch::<Sha256>(), &d).await;
1655                    let db = db.apply_batch(a_batch).await.unwrap();
1656                    let db = db.commit().await.unwrap();
1657                    let boundary = db.sync_boundary();
1658                    let db = db.prune(boundary).await.unwrap();
1659                    assert!(db.pruned_bits() > 0, "S5 must prune bitmap bits");
1660                    (db.apply_batch(d_batch).await.unwrap(), true)
1661                }
1662                _ => unreachable!("five schedules"),
1663            };
1664
1665            // D leaves the reference's root, size, floor, and bits, and its bounds unless the
1666            // schedule pruned.
1667            let label = format!("S{schedule}");
1668            let observed = Observed::capture(&db);
1669            assert_eq!(observed.root, expected.root, "{label}: root diverged");
1670            if !pruned {
1671                assert_eq!(observed.bounds, expected.bounds, "{label}: bounds diverged");
1672            }
1673            assert_eq!(observed.floor, expected.floor, "{label}: floor diverged");
1674            observed.assert_bits(&expected, &label);
1675
1676            // F merkleizes to the reference's root only if the DB's grafted tree matches.
1677            let f_batch = build(&db, db.new_batch(), &f).await;
1678            assert_eq!(f_batch.root(), f_root, "{label}: follow-on root diverged");
1679            let db = db.apply_batch(f_batch).await.unwrap();
1680            let observed_f = Observed::capture(&db);
1681            observed_f.assert_bits(&expected_f, &format!("{label} after F"));
1682
1683            // Sync, drop, and reopen. The root and bits survive, and every value matches the
1684            // reference.
1685            db.sync().await.unwrap();
1686            let db: C = Box::pin(open_db(
1687                context.child("reopen").with_attribute("index", schedule),
1688                partition,
1689            ))
1690            .await;
1691            let reopened = Observed::capture(&db);
1692            assert_eq!(
1693                reopened.root, observed_f.root,
1694                "{label}: root diverged on reopen",
1695            );
1696            reopened.assert_bits(&observed_f, &format!("{label} on reopen"));
1697            for (key, value) in keys.iter().zip(&values) {
1698                assert_eq!(
1699                    &db.get(key).await.unwrap(),
1700                    value,
1701                    "{label}: value of {key} diverged on reopen",
1702                );
1703            }
1704            db.destroy().await.unwrap();
1705        }
1706    }
1707
1708    use crate::translator::OneCap;
1709    use commonware_cryptography::{Hasher as _, Sha256, sha256::Digest};
1710    use commonware_macros::{boxed, test_group, test_traced};
1711
1712    type OrderedFixedDb =
1713        ordered::fixed::Db<mmr::Family, Context, Digest, Digest, Sha256, OneCap, 32, Sequential>;
1714    type OrderedVariableDb =
1715        ordered::variable::Db<mmr::Family, Context, Digest, Digest, Sha256, OneCap, 32, Sequential>;
1716    type UnorderedFixedDb =
1717        unordered::fixed::Db<mmr::Family, Context, Digest, Digest, Sha256, OneCap, 32, Sequential>;
1718    type UnorderedVariableDb = unordered::variable::Db<
1719        mmr::Family,
1720        Context,
1721        Digest,
1722        Digest,
1723        Sha256,
1724        OneCap,
1725        32,
1726        Sequential,
1727    >;
1728    type OrderedFixedP1Db = ordered::fixed::partitioned::Db<
1729        mmr::Family,
1730        Context,
1731        Digest,
1732        Digest,
1733        Sha256,
1734        OneCap,
1735        1,
1736        32,
1737        Sequential,
1738    >;
1739    type OrderedVariableP1Db = ordered::variable::partitioned::Db<
1740        mmr::Family,
1741        Context,
1742        Digest,
1743        Digest,
1744        Sha256,
1745        OneCap,
1746        1,
1747        32,
1748        Sequential,
1749    >;
1750    type UnorderedFixedP1Db = unordered::fixed::partitioned::Db<
1751        mmr::Family,
1752        Context,
1753        Digest,
1754        Digest,
1755        Sha256,
1756        OneCap,
1757        1,
1758        32,
1759        Sequential,
1760    >;
1761    type UnorderedVariableP1Db = unordered::variable::partitioned::Db<
1762        mmr::Family,
1763        Context,
1764        Digest,
1765        Digest,
1766        Sha256,
1767        OneCap,
1768        1,
1769        32,
1770        Sequential,
1771    >;
1772    type OrderedFixedP2Db = ordered::fixed::partitioned::Db<
1773        mmr::Family,
1774        Context,
1775        Digest,
1776        Digest,
1777        Sha256,
1778        OneCap,
1779        2,
1780        32,
1781        Sequential,
1782    >;
1783    type OrderedVariableP2Db = ordered::variable::partitioned::Db<
1784        mmr::Family,
1785        Context,
1786        Digest,
1787        Digest,
1788        Sha256,
1789        OneCap,
1790        2,
1791        32,
1792        Sequential,
1793    >;
1794    type UnorderedFixedP2Db = unordered::fixed::partitioned::Db<
1795        mmr::Family,
1796        Context,
1797        Digest,
1798        Digest,
1799        Sha256,
1800        OneCap,
1801        2,
1802        32,
1803        Sequential,
1804    >;
1805    type UnorderedVariableP2Db = unordered::variable::partitioned::Db<
1806        mmr::Family,
1807        Context,
1808        Digest,
1809        Digest,
1810        Sha256,
1811        OneCap,
1812        2,
1813        32,
1814        Sequential,
1815    >;
1816
1817    type OrderedFixedMmbDb =
1818        ordered::fixed::Db<mmb::Family, Context, Digest, Digest, Sha256, OneCap, 32, Sequential>;
1819    type OrderedVariableMmbDb =
1820        ordered::variable::Db<mmb::Family, Context, Digest, Digest, Sha256, OneCap, 32, Sequential>;
1821    type UnorderedFixedMmbDb =
1822        unordered::fixed::Db<mmb::Family, Context, Digest, Digest, Sha256, OneCap, 32, Sequential>;
1823    type UnorderedVariableMmbDb = unordered::variable::Db<
1824        mmb::Family,
1825        Context,
1826        Digest,
1827        Digest,
1828        Sha256,
1829        OneCap,
1830        32,
1831        Sequential,
1832    >;
1833    type OrderedFixedMmbP1Db = ordered::fixed::partitioned::Db<
1834        mmb::Family,
1835        Context,
1836        Digest,
1837        Digest,
1838        Sha256,
1839        OneCap,
1840        1,
1841        32,
1842        Sequential,
1843    >;
1844    type OrderedVariableMmbP1Db = ordered::variable::partitioned::Db<
1845        mmb::Family,
1846        Context,
1847        Digest,
1848        Digest,
1849        Sha256,
1850        OneCap,
1851        1,
1852        32,
1853        Sequential,
1854    >;
1855    type UnorderedFixedMmbP1Db = unordered::fixed::partitioned::Db<
1856        mmb::Family,
1857        Context,
1858        Digest,
1859        Digest,
1860        Sha256,
1861        OneCap,
1862        1,
1863        32,
1864        Sequential,
1865    >;
1866    type UnorderedVariableMmbP1Db = unordered::variable::partitioned::Db<
1867        mmb::Family,
1868        Context,
1869        Digest,
1870        Digest,
1871        Sha256,
1872        OneCap,
1873        1,
1874        32,
1875        Sequential,
1876    >;
1877    type OrderedFixedMmbP2Db = ordered::fixed::partitioned::Db<
1878        mmb::Family,
1879        Context,
1880        Digest,
1881        Digest,
1882        Sha256,
1883        OneCap,
1884        2,
1885        32,
1886        Sequential,
1887    >;
1888    type OrderedVariableMmbP2Db = ordered::variable::partitioned::Db<
1889        mmb::Family,
1890        Context,
1891        Digest,
1892        Digest,
1893        Sha256,
1894        OneCap,
1895        2,
1896        32,
1897        Sequential,
1898    >;
1899    type UnorderedFixedMmbP2Db = unordered::fixed::partitioned::Db<
1900        mmb::Family,
1901        Context,
1902        Digest,
1903        Digest,
1904        Sha256,
1905        OneCap,
1906        2,
1907        32,
1908        Sequential,
1909    >;
1910    type UnorderedVariableMmbP2Db = unordered::variable::partitioned::Db<
1911        mmb::Family,
1912        Context,
1913        Digest,
1914        Digest,
1915        Sha256,
1916        OneCap,
1917        2,
1918        32,
1919        Sequential,
1920    >;
1921
1922    #[test_traced]
1923    fn test_reconstruction_views_expose_all_chunk_states() {
1924        let executor = deterministic::Runner::default();
1925        executor.start(|context| async move {
1926            let db = UnorderedFixedMmbDb::init(
1927                context.child("db"),
1928                fixed_config::<OneCap>("reconstruction-views"),
1929                None,
1930            )
1931            .await
1932            .unwrap();
1933
1934            // At this size, MMB has a grafted chunk, a complete pending chunk, and a trailing
1935            // partial chunk.
1936            let mut batch = db.new_batch();
1937            for i in 0u64..512 {
1938                let key = Sha256::hash(&[&i.to_be_bytes()]);
1939                let value = Sha256::hash(&[&(i + 1_000).to_be_bytes()]);
1940                batch = batch.write(key, Some(value));
1941            }
1942            let batch = batch.merkleize(&db, None, &mut Proportional).await.unwrap();
1943            let db = db.apply_batch(batch).await.unwrap();
1944
1945            // The exposed bitmap must describe the same operation boundary as the DB and surface
1946            // both reconstruction chunks that remain outside the grafted tree.
1947            let end = db.bounds().end;
1948            let bitmap = db.bitmap();
1949            assert_eq!(bitmap.len(), *end);
1950            assert_eq!(bitmap.pruned_chunks(), 0);
1951
1952            let first = bitmap.get_chunk(0);
1953            let chunk_bits = first.len() as u64 * 8;
1954            let grafting_height = chunk_bits.trailing_zeros();
1955            let complete = bitmap.complete_chunks() as u64;
1956            let graftable =
1957                grafting::graftable_chunks::<mmb::Family>(*end, grafting_height).min(complete);
1958            assert_eq!(complete - graftable, 1, "expected one pending chunk");
1959            assert!(first.iter().any(|byte| *byte != 0));
1960            let pending = bitmap.get_chunk(graftable as usize);
1961            assert!(pending.iter().any(|byte| *byte != 0));
1962
1963            let (partial, partial_bits) = bitmap.last_chunk();
1964            assert!(partial_bits > 0 && partial_bits < chunk_bits);
1965            assert!(partial.iter().any(|byte| *byte != 0));
1966
1967            // Tie the extracted bytes to the digests authenticated by the existing root witness.
1968            let witness = db.ops_root_witness().await.unwrap();
1969            assert_eq!(
1970                witness.pending_chunk_digest,
1971                Some(Sha256::hash(&[pending.as_slice()]))
1972            );
1973            assert_eq!(
1974                witness.partial_chunk,
1975                Some((partial_bits, Sha256::hash(&[partial.as_slice()])))
1976            );
1977
1978            // The virtual storage remains in ops-tree coordinates even where it substitutes
1979            // bitmap-authenticated grafted nodes.
1980            let storage = db.grafted_storage();
1981            assert_eq!(
1982                storage.size(),
1983                <mmb::Family as merkle::Family>::location_to_position(end)
1984            );
1985
1986            let ops_pos = <mmb::Family as merkle::Graftable>::subtree_root_position(
1987                Location::new(0),
1988                grafting_height,
1989            );
1990            assert!(storage.get_node(ops_pos).await.unwrap().is_some());
1991
1992            // Reconstruction frontiers use the raw ops-tree pin ordering. The virtual digest at
1993            // the grafting boundary must differ because the corresponding bitmap chunk is nonzero.
1994            let pinned_positions =
1995                <mmb::Family as merkle::Family>::nodes_to_pin(end).collect::<Vec<_>>();
1996            let raw_pinned = db.pinned_nodes_at(end).await.unwrap();
1997            assert_eq!(pinned_positions.len(), raw_pinned.len());
1998            let mut checked_grafted_node = false;
1999            for (pos, raw_digest) in pinned_positions.into_iter().zip(raw_pinned) {
2000                let digest = storage.get_node(pos).await.unwrap().unwrap();
2001                if pos == ops_pos {
2002                    assert_ne!(digest, raw_digest);
2003                    checked_grafted_node = true;
2004                }
2005            }
2006            assert!(checked_grafted_node);
2007
2008            drop(storage);
2009            db.destroy().await.unwrap();
2010        });
2011    }
2012
2013    // Regression test for a forged exclusion proof against the ordered partitioned index with
2014    // variable-length keys shorter than the partition prefix. Order-preserving routing keeps a
2015    // short key in lexicographic position, so an authenticated neighbor span cannot cover an
2016    // active key and prove it absent.
2017    #[test_traced]
2018    fn test_partitioned_ordered_short_key_exclusion_not_forgeable() {
2019        type ForgedExclusionDb = ordered::variable::partitioned::Db<
2020            mmr::Family,
2021            Context,
2022            Vec<u8>,
2023            Vec<u8>,
2024            Sha256,
2025            OneCap,
2026            2,
2027            32,
2028            Sequential,
2029        >;
2030
2031        let executor = deterministic::Runner::default();
2032        executor.start(|context| async move {
2033            let page_cache = CacheRef::new(PAGE_SIZE, PAGE_CACHE_SIZE);
2034            let cfg = VariableConfig {
2035                merkle_config: MerkleConfig {
2036                    journal_partition: "forged-exclusion-journal".to_string(),
2037                    metadata_partition: "forged-exclusion-metadata".to_string(),
2038                    items_per_blob: NZU64!(11),
2039                    write_buffer: NZUsize!(1024),
2040                    replay_buffer: NZUsize!(1024),
2041                    strategy: Sequential,
2042                    page_cache: page_cache.clone(),
2043                },
2044                journal_config: VConfig {
2045                    partition: "forged-exclusion-log".to_string(),
2046                    items_per_section: NZU64!(7),
2047                    compression: None,
2048                    codec_config: (((0..=8).into(), ()), ((0..=8).into(), ())),
2049                    page_cache,
2050                    write_buffer: NZUsize!(1024),
2051                    replay_buffer: NZUsize!(1024),
2052                },
2053                grafted_metadata_partition: "forged-exclusion-grafted".to_string(),
2054                translator: OneCap,
2055                init_cache: Some(NZUsize!(1024)),
2056                init_buffer: NZUsize!(1 << 21),
2057                init_concurrency: NZUsize!(1),
2058            };
2059            let db = ForgedExclusionDb::init(context.child("db"), cfg, None)
2060                .await
2061                .unwrap();
2062
2063            // `b` is shorter than the 2-byte prefix and lexicographically falls between `a` and `c`
2064            // (`b` is a proper prefix of `c`). The bug routed `b` to partition 0x0001 instead of
2065            // 0x0100, below `a` at 0x0080.
2066            let a = vec![0x00u8, 0x80];
2067            let b = vec![0x01u8];
2068            let c = vec![0x01u8, 0x00];
2069            let (va, vb, vc) = (vec![0x0au8], vec![0x0bu8], vec![0x0cu8]);
2070
2071            // Commit `a` and `b` (ring a -> b -> a), then `c` in a later batch.
2072            let merkleized = db
2073                .new_batch()
2074                .write(a.clone(), Some(va.clone()))
2075                .write(b.clone(), Some(vb.clone()))
2076                .merkleize(&db, None, &mut Proportional)
2077                .await
2078                .unwrap();
2079            let db = db.apply_batch(merkleized).await.unwrap();
2080            let merkleized = db
2081                .new_batch()
2082                .write(c.clone(), Some(vc.clone()))
2083                .merkleize(&db, None, &mut Proportional)
2084                .await
2085                .unwrap();
2086            let db = db.apply_batch(merkleized).await.unwrap();
2087            let root = db.root();
2088
2089            // All three keys are active.
2090            assert_eq!(db.get(&a).await.unwrap(), Some(va));
2091            assert_eq!(db.get(&b).await.unwrap(), Some(vb));
2092            assert_eq!(db.get(&c).await.unwrap(), Some(vc));
2093
2094            // Root cause: the ring must link keys in lexicographic order, so `b`'s successor is `c`
2095            // (pre-fix it wrapped around to `a`).
2096            let (_, span_b) = db.get_span(&b).await.unwrap().unwrap();
2097            assert_eq!(
2098                span_b.next_key, c,
2099                "b.next_key must be c, not the wrapped-around a"
2100            );
2101
2102            // Honest exclusion proving rejects an active key.
2103            assert!(matches!(
2104                db.exclusion_proof(&c).await,
2105                Err(Error::KeyExists)
2106            ));
2107
2108            // Forge attempt: package `b`'s authenticated update as an exclusion proof for `c`. The
2109            // span [b, c) does not cover `c`, so the verifier rejects it (pre-fix the span was
2110            // [b, a), which cyclically covered `c` and verified).
2111            let kvp = db.key_value_proof(b.clone()).await.unwrap();
2112            let forged = ordered::proof::constant::ExclusionProof::KeyValue(kvp.proof, span_b);
2113            assert!(!forged.verify::<Sha256>(&c, &root));
2114
2115            db.destroy().await.unwrap();
2116        });
2117    }
2118
2119    // Helper macro to create an open_db closure for a specific variant.
2120    macro_rules! open_db_fn {
2121        ($db:ty, $cfg:ident) => {
2122            |ctx: Context, partition: String| async move {
2123                <$db>::init(ctx.child("storage"), $cfg::<OneCap>(&partition), None)
2124                    .await
2125                    .unwrap()
2126            }
2127        };
2128    }
2129
2130    // Defines all variants across both supported Merkle families.
2131    macro_rules! with_all_variants {
2132        ($cb:ident!($($args:tt)*)) => {
2133            $cb!($($args)*, of, OrderedFixedDb, fixed_config);
2134            $cb!($($args)*, ov, OrderedVariableDb, variable_config);
2135            $cb!($($args)*, uf, UnorderedFixedDb, fixed_config);
2136            $cb!($($args)*, uv, UnorderedVariableDb, variable_config);
2137            $cb!($($args)*, ofp1, OrderedFixedP1Db, fixed_config_partitioned);
2138            $cb!($($args)*, ovp1, OrderedVariableP1Db, variable_config_partitioned);
2139            $cb!($($args)*, ufp1, UnorderedFixedP1Db, fixed_config_partitioned);
2140            $cb!($($args)*, uvp1, UnorderedVariableP1Db, variable_config_partitioned);
2141            $cb!($($args)*, ofp2, OrderedFixedP2Db, fixed_config_partitioned);
2142            $cb!($($args)*, ovp2, OrderedVariableP2Db, variable_config_partitioned);
2143            $cb!($($args)*, ufp2, UnorderedFixedP2Db, fixed_config_partitioned);
2144            $cb!($($args)*, uvp2, UnorderedVariableP2Db, variable_config_partitioned);
2145            $cb!($($args)*, of_mmb, OrderedFixedMmbDb, fixed_config);
2146            $cb!($($args)*, ov_mmb, OrderedVariableMmbDb, variable_config);
2147            $cb!($($args)*, uf_mmb, UnorderedFixedMmbDb, fixed_config);
2148            $cb!($($args)*, uv_mmb, UnorderedVariableMmbDb, variable_config);
2149            $cb!($($args)*, ofp1_mmb, OrderedFixedMmbP1Db, fixed_config_partitioned);
2150            $cb!($($args)*, ovp1_mmb, OrderedVariableMmbP1Db, variable_config_partitioned);
2151            $cb!($($args)*, ufp1_mmb, UnorderedFixedMmbP1Db, fixed_config_partitioned);
2152            $cb!($($args)*, uvp1_mmb, UnorderedVariableMmbP1Db, variable_config_partitioned);
2153            $cb!($($args)*, ofp2_mmb, OrderedFixedMmbP2Db, fixed_config_partitioned);
2154            $cb!($($args)*, ovp2_mmb, OrderedVariableMmbP2Db, variable_config_partitioned);
2155            $cb!($($args)*, ufp2_mmb, UnorderedFixedMmbP2Db, fixed_config_partitioned);
2156            $cb!($($args)*, uvp2_mmb, UnorderedVariableMmbP2Db, variable_config_partitioned);
2157        };
2158    }
2159
2160    // Defines 6 ordered variants.
2161    macro_rules! with_ordered_variants {
2162        ($cb:ident!($($args:tt)*)) => {
2163            $cb!($($args)*, of, OrderedFixedDb, fixed_config);
2164            $cb!($($args)*, ov, OrderedVariableDb, variable_config);
2165            $cb!($($args)*, ofp1, OrderedFixedP1Db, fixed_config_partitioned);
2166            $cb!($($args)*, ovp1, OrderedVariableP1Db, variable_config_partitioned);
2167            $cb!($($args)*, ofp2, OrderedFixedP2Db, fixed_config_partitioned);
2168            $cb!($($args)*, ovp2, OrderedVariableP2Db, variable_config_partitioned);
2169            $cb!($($args)*, of_mmb, OrderedFixedMmbDb, fixed_config);
2170            $cb!($($args)*, ov_mmb, OrderedVariableMmbDb, variable_config);
2171            $cb!($($args)*, ofp1_mmb, OrderedFixedMmbP1Db, fixed_config_partitioned);
2172            $cb!($($args)*, ovp1_mmb, OrderedVariableMmbP1Db, variable_config_partitioned);
2173            $cb!($($args)*, ofp2_mmb, OrderedFixedMmbP2Db, fixed_config_partitioned);
2174            $cb!($($args)*, ovp2_mmb, OrderedVariableMmbP2Db, variable_config_partitioned);
2175        };
2176    }
2177
2178    // Defines 6 unordered variants.
2179    macro_rules! with_unordered_variants {
2180        ($cb:ident!($($args:tt)*)) => {
2181            $cb!($($args)*, uf, UnorderedFixedDb, fixed_config);
2182            $cb!($($args)*, uv, UnorderedVariableDb, variable_config);
2183            $cb!($($args)*, ufp1, UnorderedFixedP1Db, fixed_config_partitioned);
2184            $cb!($($args)*, uvp1, UnorderedVariableP1Db, variable_config_partitioned);
2185            $cb!($($args)*, ufp2, UnorderedFixedP2Db, fixed_config_partitioned);
2186            $cb!($($args)*, uvp2, UnorderedVariableP2Db, variable_config_partitioned);
2187            $cb!($($args)*, uf_mmb, UnorderedFixedMmbDb, fixed_config);
2188            $cb!($($args)*, uv_mmb, UnorderedVariableMmbDb, variable_config);
2189            $cb!($($args)*, ufp1_mmb, UnorderedFixedMmbP1Db, fixed_config_partitioned);
2190            $cb!($($args)*, uvp1_mmb, UnorderedVariableMmbP1Db, variable_config_partitioned);
2191            $cb!($($args)*, ufp2_mmb, UnorderedFixedMmbP2Db, fixed_config_partitioned);
2192            $cb!($($args)*, uvp2_mmb, UnorderedVariableMmbP2Db, variable_config_partitioned);
2193        };
2194    }
2195
2196    // Emit one `#[test_group("slow")] #[test_traced]` test per variant.
2197    // The fn name is `<f>_<variant_label>`, the body runs the async test
2198    // function `f` on a fresh runner, passing it the `Context` and a DB opener
2199    // for the variant.
2200    macro_rules! test_for_variant {
2201        ($f:ident, $traced:literal, $label:ident, $db:ty, $cfg:ident) => {
2202            paste::paste! {
2203                #[test_group("slow")]
2204                #[test_traced($traced)]
2205                fn [<$f _ $label>]() {
2206                    let executor = deterministic::Runner::default();
2207                    executor.start(|context| async move {
2208                        // Box the future to prevent stack overflow when
2209                        // monomorphized across DB variants.
2210                        Box::pin($f(context, open_db_fn!($db, $cfg))).await
2211                    });
2212                }
2213            }
2214        };
2215    }
2216
2217    // Generate one slow test per variant across all 24 variants.
2218    macro_rules! test_for_all_variants {
2219        ($f:ident, $traced:literal) => {
2220            with_all_variants!(test_for_variant!($f, $traced));
2221        };
2222    }
2223
2224    // Generate one slow test per variant across the 12 ordered variants.
2225    macro_rules! test_for_ordered_variants {
2226        ($f:ident, $traced:literal) => {
2227            with_ordered_variants!(test_for_variant!($f, $traced));
2228        };
2229    }
2230
2231    // Generate one slow test per variant across the 12 unordered variants.
2232    macro_rules! test_for_unordered_variants {
2233        ($f:ident, $traced:literal) => {
2234            with_unordered_variants!(test_for_variant!($f, $traced));
2235        };
2236    }
2237
2238    // Wrapper functions for build_big tests with ordered/unordered expected values.
2239    async fn test_ordered_build_big<M, C, F, Fut>(context: Context, open_db: F)
2240    where
2241        M: merkle::Graftable,
2242        C: DbAny<M>,
2243        C::Key: TestKey,
2244        <C as DbAny<M>>::Value: TestValue,
2245        F: FnMut(Context, String) -> Fut + Clone,
2246        Fut: Future<Output = C>,
2247    {
2248        test_current_db_build_big::<M, C, F, Fut>(context, open_db).await;
2249    }
2250
2251    async fn test_unordered_build_big<M, C, F, Fut>(context: Context, open_db: F)
2252    where
2253        M: merkle::Graftable,
2254        C: DbAny<M>,
2255        C::Key: TestKey,
2256        <C as DbAny<M>>::Value: TestValue,
2257        F: FnMut(Context, String) -> Fut + Clone,
2258        Fut: Future<Output = C>,
2259    {
2260        test_current_db_build_big::<M, C, F, Fut>(context, open_db).await;
2261    }
2262
2263    test_for_all_variants!(test_build_random_close_reopen, "WARN");
2264    test_for_all_variants!(test_simulate_write_failures, "WARN");
2265    test_for_all_variants!(test_different_pruning_delays_same_root, "WARN");
2266    test_for_all_variants!(test_sync_persists_bitmap_pruning_boundary, "WARN");
2267    test_for_all_variants!(test_commit_after_sync_recovery, "WARN");
2268    test_for_all_variants!(test_stale_batch_side_effect_free, "WARN");
2269    test_for_all_variants!(test_chained_schedules_match_sequential, "WARN");
2270
2271    test_for_ordered_variants!(test_ordered_build_big, "WARN");
2272    test_for_ordered_variants!(test_ordered_build_small_close_reopen, "DEBUG");
2273
2274    test_for_unordered_variants!(test_unordered_build_big, "WARN");
2275    test_for_unordered_variants!(test_unordered_build_small_close_reopen, "DEBUG");
2276
2277    //
2278    // These exercise the current wrapper's batch methods (root, ops_root,
2279    // MerkleizedBatch::get, batch chaining) which layer bitmap and grafted tree
2280    // computation on top of the `any` batch.
2281
2282    #[test_traced]
2283    fn test_current_foreign_db_merkleize_rejected() {
2284        deterministic::Runner::default().start(|context| async move {
2285            let db_a = UnorderedFixedDb::init(
2286                context.child("a"),
2287                fixed_config::<OneCap>("foreign-a"),
2288                None,
2289            )
2290            .await
2291            .unwrap();
2292            let db_b = UnorderedFixedDb::init(
2293                context.child("b"),
2294                fixed_config::<OneCap>("foreign-b"),
2295                None,
2296            )
2297            .await
2298            .unwrap();
2299
2300            let seed_a = db_a
2301                .new_batch()
2302                .write(key(1), Some(val(1)))
2303                .merkleize(&db_a, None, &mut Proportional)
2304                .await
2305                .unwrap();
2306            let db_a = db_a.apply_batch(seed_a).await.unwrap();
2307            let seed_b = db_b
2308                .new_batch()
2309                .write(key(2), Some(val(2)))
2310                .merkleize(&db_b, None, &mut Proportional)
2311                .await
2312                .unwrap();
2313            let db_b = db_b.apply_batch(seed_b).await.unwrap();
2314
2315            assert_eq!(db_a.bounds().end, db_b.bounds().end);
2316            assert_ne!(db_a.ops_root(), db_b.ops_root());
2317            assert_ne!(db_a.root(), db_b.root());
2318
2319            let staged_keys = [key(1)];
2320            let staged_refs: Vec<_> = staged_keys.iter().collect();
2321            assert!(matches!(
2322                db_a.new_batch().stage(&staged_refs, &db_b).await,
2323                Err(Error::StaleRead)
2324            ));
2325
2326            let batch = db_a.new_batch().write(key(1), Some(val(3)));
2327            assert!(matches!(
2328                batch.merkleize(&db_b, None, &mut Proportional).await,
2329                Err(Error::StaleBatch)
2330            ));
2331        });
2332    }
2333
2334    #[test_traced]
2335    fn test_current_unordered_merkleize_rejects_foreign_instance() {
2336        deterministic::Runner::default().start(|context| async move {
2337            // Independent instances begin with the same committed state.
2338            let db_a = UnorderedFixedDb::init(
2339                context.child("a"),
2340                fixed_config::<OneCap>("foreign-instance-unordered-a"),
2341                None,
2342            )
2343            .await
2344            .unwrap();
2345            let db_b = UnorderedFixedDb::init(
2346                context.child("b"),
2347                fixed_config::<OneCap>("foreign-instance-unordered-b"),
2348                None,
2349            )
2350            .await
2351            .unwrap();
2352            assert_eq!(db_a.root(), db_b.root());
2353
2354            // The unmerkleized and staged batches retain A's bitmap.
2355            let pending = db_a.new_batch().write(key(2), Some(val(2)));
2356            let staged_keys = [key(2)];
2357            let staged_refs: Vec<_> = staged_keys.iter().collect();
2358            let (_, staged) = db_a.new_batch().stage(&staged_refs, &db_a).await.unwrap();
2359
2360            // Applying a sibling changes A's bitmap while B remains at the original commitment.
2361            let sibling = db_a
2362                .new_batch()
2363                .write(key(1), Some(val(1)))
2364                .merkleize(&db_a, None, &mut Proportional)
2365                .await
2366                .unwrap();
2367            let _db_a = db_a.apply_batch(sibling).await.unwrap();
2368
2369            // B matches the commitment the batches were created from, but is not their instance.
2370            assert!(matches!(
2371                pending.merkleize(&db_b, None, &mut Proportional).await,
2372                Err(Error::StaleBatch)
2373            ));
2374            assert!(matches!(
2375                staged
2376                    .merkleize(
2377                        vec![Slot::Put(val(3))],
2378                        Vec::new(),
2379                        None,
2380                        &db_b,
2381                        &mut Proportional
2382                    )
2383                    .await,
2384                Err(Error::StaleBatch)
2385            ));
2386        });
2387    }
2388
2389    #[test_traced]
2390    fn test_current_ordered_merkleize_rejects_foreign_instance() {
2391        deterministic::Runner::default().start(|context| async move {
2392            // Independent instances begin with the same committed state.
2393            let db_a = OrderedFixedDb::init(
2394                context.child("a"),
2395                fixed_config::<OneCap>("foreign-instance-ordered-a"),
2396                None,
2397            )
2398            .await
2399            .unwrap();
2400            let db_b = OrderedFixedDb::init(
2401                context.child("b"),
2402                fixed_config::<OneCap>("foreign-instance-ordered-b"),
2403                None,
2404            )
2405            .await
2406            .unwrap();
2407            assert_eq!(db_a.root(), db_b.root());
2408
2409            // The unmerkleized and staged batches retain A's bitmap.
2410            let pending = db_a.new_batch().write(key(2), Some(val(2)));
2411            let staged_keys = [key(2)];
2412            let staged_refs: Vec<_> = staged_keys.iter().collect();
2413            let (_, staged) = db_a.new_batch().stage(&staged_refs, &db_a).await.unwrap();
2414
2415            // Applying a sibling changes A's bitmap while B remains at the original commitment.
2416            let sibling = db_a
2417                .new_batch()
2418                .write(key(1), Some(val(1)))
2419                .merkleize(&db_a, None, &mut Proportional)
2420                .await
2421                .unwrap();
2422            let _db_a = db_a.apply_batch(sibling).await.unwrap();
2423
2424            // B matches the commitment the batches were created from, but is not their instance.
2425            assert!(matches!(
2426                pending.merkleize(&db_b, None, &mut Proportional).await,
2427                Err(Error::StaleBatch)
2428            ));
2429            assert!(matches!(
2430                staged
2431                    .merkleize(
2432                        vec![Slot::Put(val(3))],
2433                        Vec::new(),
2434                        None,
2435                        &db_b,
2436                        &mut Proportional
2437                    )
2438                    .await,
2439                Err(Error::StaleBatch)
2440            ));
2441        });
2442    }
2443
2444    #[test_traced]
2445    fn test_current_merkleize_rejects_stale_sibling() {
2446        deterministic::Runner::default().start(|context| async move {
2447            let db = OrderedFixedDb::init(
2448                context.child("db"),
2449                fixed_config::<OneCap>("stale-unmerkleized"),
2450                None,
2451            )
2452            .await
2453            .unwrap();
2454
2455            let seed = db
2456                .new_batch()
2457                .write(key(1), Some(val(1)))
2458                .merkleize(&db, None, &mut Proportional)
2459                .await
2460                .unwrap();
2461            let db = db.apply_batch(seed).await.unwrap();
2462
2463            let stale = db.new_batch().write(key(1), Some(val(2)));
2464            let sibling = db
2465                .new_batch()
2466                .write(key(1), Some(val(3)))
2467                .merkleize(&db, None, &mut Proportional)
2468                .await
2469                .unwrap();
2470            let db = db.apply_batch(sibling).await.unwrap();
2471
2472            assert!(matches!(
2473                stale.merkleize(&db, None, &mut Proportional).await,
2474                Err(Error::StaleBatch)
2475            ));
2476        });
2477    }
2478
2479    #[test_traced]
2480    fn test_current_staged_merkleize_rejects_stale_sibling() {
2481        deterministic::Runner::default().start(|context| async move {
2482            let db = UnorderedFixedDb::init(
2483                context.child("db"),
2484                fixed_config::<OneCap>("stale-staged"),
2485                None,
2486            )
2487            .await
2488            .unwrap();
2489            let target = key(1);
2490
2491            let seed = db
2492                .new_batch()
2493                .write(target, Some(val(1)))
2494                .merkleize(&db, None, &mut Proportional)
2495                .await
2496                .unwrap();
2497            let db = db.apply_batch(seed).await.unwrap();
2498
2499            let staged_keys = [&target];
2500            let (_, staged) = db.new_batch().stage(&staged_keys, &db).await.unwrap();
2501            let sibling = db
2502                .new_batch()
2503                .write(target, Some(val(2)))
2504                .merkleize(&db, None, &mut Proportional)
2505                .await
2506                .unwrap();
2507            let db = db.apply_batch(sibling).await.unwrap();
2508
2509            assert!(matches!(
2510                staged
2511                    .merkleize(
2512                        vec![Slot::Put(val(3))],
2513                        Vec::new(),
2514                        None,
2515                        &db,
2516                        &mut Proportional
2517                    )
2518                    .await,
2519                Err(Error::StaleBatch)
2520            ));
2521        });
2522    }
2523
2524    /// Bitmap chunk size in bits for the `N = 32` database aliases above.
2525    const CHUNK_BITS: u64 = commonware_utils::bitmap::BitMap::<32>::CHUNK_SIZE_BITS;
2526
2527    fn key(i: u64) -> Digest {
2528        Sha256::hash(&[&i.to_be_bytes()])
2529    }
2530
2531    fn val(i: u64) -> Digest {
2532        Sha256::hash(&[&(i + 10000).to_be_bytes()])
2533    }
2534
2535    #[boxed]
2536    async fn mmb_commit(
2537        db: UnorderedVariableMmbDb,
2538        writes: impl IntoIterator<Item = (Digest, Option<Digest>)>,
2539    ) -> UnorderedVariableMmbDb {
2540        let mut batch = db.new_batch();
2541        for (k, v) in writes {
2542            batch = batch.write(k, v);
2543        }
2544        let merkleized = batch.merkleize(&db, None, &mut Proportional).await.unwrap();
2545        let db = db.apply_batch(merkleized).await.unwrap();
2546        db.commit().await.unwrap()
2547    }
2548
2549    #[boxed]
2550    async fn commit_writes_with_metadata(
2551        db: UnorderedVariableDb,
2552        writes: impl IntoIterator<Item = (Digest, Option<Digest>)>,
2553        metadata: Option<Digest>,
2554    ) -> UnorderedVariableDb {
2555        let mut batch = db.new_batch();
2556        for (k, v) in writes {
2557            batch = batch.write(k, v);
2558        }
2559        let merkleized = batch
2560            .merkleize(&db, metadata, &mut Proportional)
2561            .await
2562            .unwrap();
2563        let expected_end = merkleized.bounds().tip.size;
2564        let db = db.apply_batch(merkleized).await.unwrap();
2565        assert_eq!(db.bounds().end, expected_end);
2566        db.commit().await.unwrap()
2567    }
2568
2569    /// State observed after one generation of [build_generations].
2570    struct Generation {
2571        /// Exclusive operation-log end after this generation's commit.
2572        size: Location<mmr::Family>,
2573        /// Logical inactivity floor recorded with this generation.
2574        floor: Location<mmr::Family>,
2575        /// Latest safe chunk boundary for pruning and state synchronization.
2576        sync_boundary: Location<mmr::Family>,
2577        /// Canonical root after this generation's commit.
2578        root: Digest,
2579    }
2580
2581    /// Commit `generations` batches that each rewrite the same 384 keys. Every key stays active,
2582    /// so each commit's floor lags its size by more than one bitmap chunk, and each generation
2583    /// moves the floor past the previous generation's writes.
2584    async fn build_generations(
2585        ctx: &Context,
2586        partition: &str,
2587        generations: u64,
2588    ) -> (UnorderedVariableDb, Vec<Generation>) {
2589        let mut db: UnorderedVariableDb = UnorderedVariableDb::init(
2590            ctx.child("storage"),
2591            variable_config::<OneCap>(partition),
2592            None,
2593        )
2594        .await
2595        .unwrap();
2596        let mut history = Vec::new();
2597        for generation in 0..generations {
2598            db = commit_writes_with_metadata(
2599                db,
2600                (0..384).map(|i| (key(i), Some(val(generation * 1_000 + i)))),
2601                None,
2602            )
2603            .await;
2604            history.push(Generation {
2605                size: db.bounds().end,
2606                floor: db.inactivity_floor_loc(),
2607                sync_boundary: db.sync_boundary(),
2608                root: db.root(),
2609            });
2610        }
2611        (db, history)
2612    }
2613
2614    #[test_traced("INFO")]
2615    fn test_current_bounded_initialization_recovery() {
2616        let executor = deterministic::Runner::default();
2617        executor.start(|context| async move {
2618            let partition = "current-rewind-recovery";
2619            let ctx = context.child("db");
2620            let db: UnorderedVariableDb = UnorderedVariableDb::init(
2621                ctx.child("storage"),
2622                variable_config::<OneCap>(partition),
2623                None,
2624            )
2625            .await
2626            .unwrap();
2627            let initial_size = db.bounds().end;
2628            let initial_root = db.root();
2629            let initial_ops_root = db.ops_root();
2630            let initial_floor = db.inactivity_floor_loc();
2631
2632            let metadata_a = val(900);
2633            let db = commit_writes_with_metadata(
2634                db,
2635                [(key(0), Some(val(0))), (key(1), Some(val(1)))],
2636                Some(metadata_a),
2637            )
2638            .await;
2639            let size_before = db.bounds().end;
2640            let root_before = db.root();
2641            let ops_root_before = db.ops_root();
2642            let floor_before = db.inactivity_floor_loc();
2643
2644            let metadata_b = val(901);
2645            let db = commit_writes_with_metadata(
2646                db,
2647                [
2648                    (key(0), Some(val(100))),
2649                    (key(1), None),
2650                    (key(2), Some(val(2))),
2651                ],
2652                Some(metadata_b),
2653            )
2654            .await;
2655            assert_ne!(db.root(), root_before);
2656            assert_eq!(db.get_metadata().await.unwrap(), Some(val(901)));
2657            assert_eq!(db.get(&key(0)).await.unwrap(), Some(val(100)));
2658            assert_eq!(db.get(&key(1)).await.unwrap(), None);
2659            assert_eq!(db.get(&key(2)).await.unwrap(), Some(val(2)));
2660
2661            let db = {
2662                _ = db.sync().await.unwrap();
2663                UnorderedVariableDb::init(
2664                    ctx.child("cap"),
2665                    variable_config::<OneCap>(partition),
2666                    Some(size_before),
2667                )
2668                .await
2669            }
2670            .unwrap();
2671            assert_eq!(db.bounds().end, size_before);
2672            assert_eq!(db.root(), root_before);
2673            assert_eq!(db.ops_root(), ops_root_before);
2674            assert_eq!(db.inactivity_floor_loc(), floor_before);
2675            assert_eq!(db.get_metadata().await.unwrap(), Some(metadata_a));
2676            assert_eq!(db.get(&key(0)).await.unwrap(), Some(val(0)));
2677            assert_eq!(db.get(&key(1)).await.unwrap(), Some(val(1)));
2678            assert_eq!(db.get(&key(2)).await.unwrap(), None);
2679
2680            let db = db.commit().await.unwrap();
2681            drop(db);
2682
2683            let reopened: UnorderedVariableDb = UnorderedVariableDb::init(
2684                context.child("reopen"),
2685                variable_config::<OneCap>(partition),
2686                None,
2687            )
2688            .await
2689            .unwrap();
2690            assert_eq!(reopened.bounds().end, size_before);
2691            assert_eq!(reopened.root(), root_before);
2692            assert_eq!(reopened.ops_root(), ops_root_before);
2693            assert_eq!(reopened.inactivity_floor_loc(), floor_before);
2694            assert_eq!(reopened.get_metadata().await.unwrap(), Some(val(900)));
2695            assert_eq!(reopened.get(&key(0)).await.unwrap(), Some(val(0)));
2696            assert_eq!(reopened.get(&key(1)).await.unwrap(), Some(val(1)));
2697            assert_eq!(reopened.get(&key(2)).await.unwrap(), None);
2698
2699            let reopened = {
2700                _ = reopened.sync().await.unwrap();
2701                UnorderedVariableDb::init(
2702                    ctx.child("cap"),
2703                    variable_config::<OneCap>(partition),
2704                    Some(initial_size),
2705                )
2706                .await
2707            }
2708            .unwrap();
2709            assert_eq!(reopened.bounds().end, initial_size);
2710            assert_eq!(reopened.root(), initial_root);
2711            assert_eq!(reopened.ops_root(), initial_ops_root);
2712            assert_eq!(reopened.inactivity_floor_loc(), initial_floor);
2713            assert_eq!(reopened.get_metadata().await.unwrap(), None);
2714            assert_eq!(reopened.get(&key(0)).await.unwrap(), None);
2715            assert_eq!(reopened.get(&key(1)).await.unwrap(), None);
2716            assert_eq!(reopened.get(&key(2)).await.unwrap(), None);
2717
2718            let reopened = reopened.commit().await.unwrap();
2719            drop(reopened);
2720
2721            let reopened_initial: UnorderedVariableDb = UnorderedVariableDb::init(
2722                context.child("reopen_initial"),
2723                variable_config::<OneCap>(partition),
2724                None,
2725            )
2726            .await
2727            .unwrap();
2728            assert_eq!(reopened_initial.bounds().end, initial_size);
2729            assert_eq!(reopened_initial.root(), initial_root);
2730            assert_eq!(reopened_initial.ops_root(), initial_ops_root);
2731            assert_eq!(reopened_initial.inactivity_floor_loc(), initial_floor);
2732            assert_eq!(reopened_initial.get_metadata().await.unwrap(), None);
2733            assert_eq!(reopened_initial.get(&key(0)).await.unwrap(), None);
2734            assert_eq!(reopened_initial.get(&key(1)).await.unwrap(), None);
2735            assert_eq!(reopened_initial.get(&key(2)).await.unwrap(), None);
2736
2737            reopened_initial.destroy().await.unwrap();
2738        });
2739    }
2740
2741    #[test_traced("INFO")]
2742    fn test_current_bounded_initialization_recovery_pruned_repeated_updates() {
2743        let executor = deterministic::Runner::default();
2744        executor.start(|context| async move {
2745            const COMMITS: u64 = 200;
2746
2747            let partition = "current-rewind-pruned-recovery";
2748            let ctx = context.child("db");
2749            let mut db: UnorderedVariableDb = UnorderedVariableDb::init(
2750                ctx.child("storage"),
2751                variable_config::<OneCap>(partition),
2752                None,
2753            )
2754            .await
2755            .unwrap();
2756
2757            let key0 = key(0);
2758            let mut history = Vec::new();
2759            for round in 0..COMMITS {
2760                db = commit_writes_with_metadata(db, [(key0, Some(val(20_000 + round)))], None)
2761                    .await;
2762                history.push((
2763                    db.bounds().end,
2764                    db.inactivity_floor_loc(),
2765                    db.root(),
2766                    db.ops_root(),
2767                    val(20_000 + round),
2768                ));
2769            }
2770
2771            // Prune to the chunk boundary below the floor of a commit three rounds back: the log
2772            // keeps the last few commits, and the whole chunks below them leave the bitmap, so
2773            // bounded initialization uses pinned-node reconstruction (`pruned_chunks > 0` path).
2774            let (_, older_floor, _, _, _) = history[history.len() - 4];
2775            let prune_loc = Location::new(*older_floor / CHUNK_BITS * CHUNK_BITS);
2776            let db = db.prune(prune_loc).await.unwrap();
2777            let pruned_bits = db.pruned_bits();
2778            assert!(
2779                pruned_bits > 0,
2780                "expected bitmap pruning: prune_loc={prune_loc} bounds={:?}",
2781                db.bounds()
2782            );
2783            assert_eq!(pruned_bits, *prune_loc);
2784            let bounds = db.bounds();
2785
2786            let (target_size, target_root, target_ops_root, target_value) = history
2787                .iter()
2788                .enumerate()
2789                .find_map(|(idx, (size, floor, root, ops_root, value))| {
2790                    let removed_commits = history.len() - idx - 1;
2791                    if removed_commits >= 3 && *size > bounds.start && *floor >= pruned_bits {
2792                        Some((*size, *root, *ops_root, *value))
2793                    } else {
2794                        None
2795                    }
2796                })
2797                .unwrap_or_else(|| {
2798                    panic!(
2799                        "expected recoverable pruned target with repeated updates. \
2800                         bounds={bounds:?}, pruned_bits={pruned_bits}, latest_floor={:?}, \
2801                         history={history:?}",
2802                        db.inactivity_floor_loc()
2803                    )
2804                });
2805
2806            _ = db.sync().await.unwrap();
2807            let db = UnorderedVariableDb::init(
2808                ctx.child("cap"),
2809                variable_config::<OneCap>(partition),
2810                Some(target_size),
2811            )
2812            .await
2813            .unwrap();
2814            assert_eq!(db.root(), target_root);
2815            assert_eq!(db.ops_root(), target_ops_root);
2816            assert_eq!(db.bounds().end, target_size);
2817            assert_eq!(db.get(&key0).await.unwrap(), Some(target_value));
2818
2819            let db = db.commit().await.unwrap();
2820            drop(db);
2821
2822            let reopened: UnorderedVariableDb = UnorderedVariableDb::init(
2823                context.child("reopen_pruned_recovery"),
2824                variable_config::<OneCap>(partition),
2825                None,
2826            )
2827            .await
2828            .unwrap();
2829            assert_eq!(reopened.root(), target_root);
2830            assert_eq!(reopened.ops_root(), target_ops_root);
2831            assert_eq!(reopened.bounds().end, target_size);
2832            assert_eq!(reopened.get(&key0).await.unwrap(), Some(target_value));
2833
2834            let metadata_after_reopen = val(30_000);
2835            let new_key = key(1);
2836            let new_value = val(30_001);
2837            let reopened = commit_writes_with_metadata(
2838                reopened,
2839                [(new_key, Some(new_value))],
2840                Some(metadata_after_reopen),
2841            )
2842            .await;
2843            let size_after_new_write = reopened.bounds().end;
2844            let root_after_new_write = reopened.root();
2845            let ops_root_after_new_write = reopened.ops_root();
2846            assert_eq!(
2847                reopened.get_metadata().await.unwrap(),
2848                Some(metadata_after_reopen)
2849            );
2850            assert_eq!(reopened.get(&key0).await.unwrap(), Some(target_value));
2851            assert_eq!(reopened.get(&new_key).await.unwrap(), Some(new_value));
2852
2853            drop(reopened);
2854            let reopened_after_new_write: UnorderedVariableDb = UnorderedVariableDb::init(
2855                context.child("reopen_pruned_after_new_write"),
2856                variable_config::<OneCap>(partition),
2857                None,
2858            )
2859            .await
2860            .unwrap();
2861            assert_eq!(reopened_after_new_write.root(), root_after_new_write);
2862            assert_eq!(
2863                reopened_after_new_write.ops_root(),
2864                ops_root_after_new_write
2865            );
2866            assert_eq!(reopened_after_new_write.bounds().end, size_after_new_write);
2867            assert_eq!(
2868                reopened_after_new_write.get_metadata().await.unwrap(),
2869                Some(metadata_after_reopen)
2870            );
2871            assert_eq!(
2872                reopened_after_new_write.get(&key0).await.unwrap(),
2873                Some(target_value)
2874            );
2875            assert_eq!(
2876                reopened_after_new_write.get(&new_key).await.unwrap(),
2877                Some(new_value)
2878            );
2879
2880            reopened_after_new_write.destroy().await.unwrap();
2881        });
2882    }
2883
2884    /// Verify that the delayed-merge settlement guard holds `sync_boundary` at 0 during the
2885    /// unsettled window, so `prune` rejects any non-zero `prune_loc`.
2886    #[test_traced("INFO")]
2887    fn test_current_mmb_settlement_guard_defers_pruning() {
2888        let executor = deterministic::Runner::default();
2889        executor.start(|context| async move {
2890            const COMMITS: u64 = 100;
2891
2892            let partition = "current-mmb-reopen-prove-after-prune";
2893            let ctx = context.child("db");
2894            let mut db: UnorderedVariableMmbDb = UnorderedVariableMmbDb::init(
2895                ctx.child("storage"),
2896                variable_config::<OneCap>(partition),
2897                None,
2898            )
2899            .await
2900            .unwrap();
2901
2902            let k = key(0);
2903            let mut expected = None;
2904            for round in 0..COMMITS {
2905                expected = Some(val(50_000 + round));
2906                let mut batch = db.new_batch();
2907                batch = batch.write(k, expected);
2908                let merkleized = batch.merkleize(&db, None, &mut Proportional).await.unwrap();
2909                db = db.apply_batch(merkleized).await.unwrap();
2910                db = db.commit().await.unwrap();
2911            }
2912
2913            let root_before = db.root();
2914            assert!(
2915                *db.inactivity_floor_loc() >= 256,
2916                "expected inactivity floor past chunk 0"
2917            );
2918            assert_eq!(
2919                *db.sync_boundary(),
2920                0,
2921                "settlement guard should hold boundary at 0 during unsettled window"
2922            );
2923
2924            // `prune` must reject any non-zero loc because sync_boundary is still 0.
2925            let Err(err) = db.prune(Location::<mmb::Family>::new(1)).await else {
2926                panic!("expected PruneBeyondMinRequired");
2927            };
2928            assert!(
2929                matches!(err, Error::PruneBeyondMinRequired(_, _)),
2930                "expected PruneBeyondMinRequired, got {err:?}"
2931            );
2932
2933            // Reopen: no pruning occurred, state is unchanged.
2934            let reopened: UnorderedVariableMmbDb = UnorderedVariableMmbDb::init(
2935                context.child("reopen"),
2936                variable_config::<OneCap>(partition),
2937                None,
2938            )
2939            .await
2940            .unwrap();
2941
2942            assert_eq!(reopened.pruned_bits(), 0);
2943            assert_eq!(reopened.root(), root_before);
2944            assert_eq!(reopened.get(&k).await.unwrap(), expected);
2945
2946            // key_value_proof: RangeProof::new must also handle pruned chunk 0.
2947            let _proof = reopened.key_value_proof(k).await.unwrap();
2948
2949            reopened.destroy().await.unwrap();
2950        });
2951    }
2952
2953    #[test_traced("INFO")]
2954    fn test_current_mmb_bounded_initialization_rejects_unsettled_pruned_window() {
2955        let executor = deterministic::Runner::default();
2956        executor.start(|context| async move {
2957            const COMMITS: u64 = 320;
2958            const N: usize = 32;
2959
2960            let partition = "current-mmb-rewind-unsettled-window";
2961            let ctx = context.child("db");
2962            let mut db: UnorderedVariableMmbDb = UnorderedVariableMmbDb::init(
2963                ctx.child("storage"),
2964                variable_config::<OneCap>(partition),
2965                None,
2966            )
2967            .await
2968            .unwrap();
2969
2970            let key0 = key(0);
2971            let mut history = Vec::new();
2972            for round in 0..COMMITS {
2973                let mut batch = db.new_batch();
2974                batch = batch.write(key0, Some(val(60_000 + round)));
2975                let merkleized = batch.merkleize(&db, None, &mut Proportional).await.unwrap();
2976                db = db.apply_batch(merkleized).await.unwrap();
2977                db = db.commit().await.unwrap();
2978                history.push((db.bounds().end, db.inactivity_floor_loc()));
2979            }
2980
2981            let boundary = db.sync_boundary();
2982            let db = db.prune(boundary).await.unwrap();
2983            let pruned_bits = db.pruned_bits();
2984            assert!(pruned_bits > 0, "expected MMB bitmap pruning to be active");
2985            let db = db.sync().await.unwrap();
2986
2987            let chunk_bits = commonware_utils::bitmap::BitMap::<N>::CHUNK_SIZE_BITS;
2988            let pruned_chunks = (pruned_bits / chunk_bits) as u64;
2989            let gh = super::grafting::height::<N>();
2990            let youngest = pruned_chunks - 1;
2991            let pair_chunk = youngest & !1;
2992            let pair_start = pair_chunk << gh;
2993            let pair_pos = <mmb::Family as merkle::Graftable>::subtree_root_position(
2994                merkle::Location::<mmb::Family>::new(pair_start),
2995                gh + 1,
2996            );
2997            let absorbed_after =
2998                <mmb::Family as merkle::Graftable>::peak_birth_size(pair_pos, gh + 1);
2999
3000            let unsafe_target = history
3001                .iter()
3002                .filter_map(|(size, floor)| {
3003                    let s = **size;
3004                    if s >= pruned_bits && s < absorbed_after && **floor >= pruned_bits {
3005                        Some(s)
3006                    } else {
3007                        None
3008                    }
3009                })
3010                .max()
3011                .unwrap_or_else(|| {
3012                    panic!(
3013                        "expected initialization target in unsettled window. \
3014                         pruned_bits={pruned_bits}, absorbed_after={absorbed_after}, \
3015                         history={history:?}"
3016                    )
3017                });
3018
3019            let original_root = db.root();
3020            _ = db.sync().await.unwrap();
3021            let Err(err) = UnorderedVariableMmbDb::init(
3022                ctx.child("cap"),
3023                variable_config::<OneCap>(partition),
3024                Some(merkle::Location::<mmb::Family>::new(unsafe_target)),
3025            )
3026            .await
3027            else {
3028                panic!("expected initialization rejection in unsettled delayed-merge window");
3029            };
3030            assert!(
3031                matches!(err, Error::HistoricalFloorPruned(_)),
3032                "unexpected bounded initialization error for unsettled delayed-merge window. \
3033                 {err:?}"
3034            );
3035            let db = UnorderedVariableMmbDb::init(
3036                ctx.child("unchanged"),
3037                variable_config::<OneCap>(partition),
3038                None,
3039            )
3040            .await
3041            .unwrap();
3042            assert_eq!(db.root(), original_root);
3043        });
3044    }
3045
3046    /// Verify that `Db::prune` never advances the ops journal past the settled bitmap
3047    /// pruning boundary on a delayed-merge (MMB) family. The journal's lower bound must be
3048    /// less than or equal to `sync_boundary()`, and the test setup must force the lag to
3049    /// be strictly active so the assertion is not vacuous.
3050    #[test_traced]
3051    fn test_current_mmb_prune_respects_sync_boundary() {
3052        let executor = deterministic::Runner::default();
3053        executor.start(|context| async move {
3054            const COMMITS: u64 = 320;
3055
3056            let ctx = context.child("db");
3057            let mut db: UnorderedVariableMmbDb = UnorderedVariableMmbDb::init(
3058                ctx.child("storage"),
3059                variable_config::<OneCap>("prune-clip-mmb"),
3060                None,
3061            )
3062            .await
3063            .unwrap();
3064
3065            let k = key(0);
3066            for round in 0..COMMITS {
3067                db = mmb_commit(db, [(k, Some(val(70_000 + round)))]).await;
3068            }
3069
3070            let prune_boundary = db.sync_boundary();
3071            let db = db.prune(prune_boundary).await.unwrap();
3072
3073            let boundary = db.sync_boundary();
3074            let floor = db.inactivity_floor_loc();
3075            assert!(
3076                boundary < floor,
3077                "delayed-merge lag must be strictly active: boundary={boundary}, floor={floor}"
3078            );
3079            assert!(
3080                db.bounds().start <= boundary,
3081                "ops journal was pruned past the settled bitmap boundary: \
3082                 bounds.start={}, boundary={boundary}",
3083                db.bounds().start
3084            );
3085
3086            db.destroy().await.unwrap();
3087        });
3088    }
3089
3090    /// Verify that on a non-delayed-merge (MMR) family `sync_boundary()` lags the inactivity
3091    /// floor only by chunk alignment (less than one chunk), never by a delayed-merge absorption
3092    /// window. Guards against an accidental regression that would introduce a larger lag on
3093    /// families that don't need it.
3094    #[test_traced]
3095    fn test_current_mmr_prune_boundary_lag_is_only_chunk_alignment() {
3096        let executor = deterministic::Runner::default();
3097        executor.start(|context| async move {
3098            const COMMITS: u64 = 320;
3099            const N: usize = 32;
3100
3101            let ctx = context.child("db");
3102            let mut db: UnorderedVariableDb = UnorderedVariableDb::init(
3103                ctx.child("storage"),
3104                variable_config::<OneCap>("prune-clip-mmr"), None,
3105            )
3106            .await
3107            .unwrap();
3108
3109            for round in 0..COMMITS {
3110                db = commit_writes_with_metadata(
3111                    db,
3112                    [(key(0), Some(val(80_000 + round)))],
3113                    None,
3114                )
3115                .await;
3116            }
3117
3118            let prune_boundary = db.sync_boundary();
3119            let db = db.prune(prune_boundary).await.unwrap();
3120
3121            let boundary = db.sync_boundary();
3122            let floor = db.inactivity_floor_loc();
3123            let chunk_bits = commonware_utils::bitmap::BitMap::<N>::CHUNK_SIZE_BITS;
3124            assert!(
3125                boundary <= floor && *floor - *boundary < chunk_bits,
3126                "MMR lag should be only chunk alignment: boundary={boundary}, floor={floor}, chunk_bits={chunk_bits}"
3127            );
3128            assert!(
3129                db.bounds().start <= boundary,
3130                "ops journal bounds must be <= sync_boundary: bounds.start={}, boundary={boundary}",
3131                db.bounds().start
3132            );
3133
3134            db.destroy().await.unwrap();
3135        });
3136    }
3137
3138    /// Verify that `prune(loc)` with `loc < sync_boundary()` prunes the ops journal only as far
3139    /// as the caller requested.
3140    #[test_traced]
3141    fn test_current_prune_below_settled_boundary_is_honored() {
3142        let executor = deterministic::Runner::default();
3143        executor.start(|context| async move {
3144            const COMMITS: u64 = 100;
3145
3146            let ctx = context.child("db");
3147            let mut db: UnorderedVariableDb = UnorderedVariableDb::init(
3148                ctx.child("storage"),
3149                variable_config::<OneCap>("prune-below-boundary"), None,
3150            )
3151            .await
3152            .unwrap();
3153
3154            for round in 0..COMMITS {
3155                db = commit_writes_with_metadata(db, [(key(0), Some(val(90_000 + round)))], None)
3156                    .await;
3157            }
3158
3159            assert!(*db.inactivity_floor_loc() > 1);
3160            let small = Location::new(1);
3161            let db = db.prune(small).await.unwrap();
3162
3163            assert!(
3164                db.bounds().start <= small,
3165                "journal pruning exceeded the caller-supplied target: bounds.start={}, requested={small}",
3166                db.bounds().start
3167            );
3168
3169            db.destroy().await.unwrap();
3170        });
3171    }
3172
3173    /// Prune, then grow without pruning again so delayed MMB merges occur inside the
3174    /// already-pruned region. Verify proof + reopen correctness.
3175    #[test_traced]
3176    fn test_current_mmb_reopen_and_prove_after_prune_delayed_merge() {
3177        let executor = deterministic::Runner::default();
3178        executor.start(|context| async move {
3179            let db_ctx = context.child("db_init");
3180            let mut db: UnorderedVariableMmbDb = UnorderedVariableMmbDb::init(
3181                db_ctx.child("db"),
3182                variable_config::<OneCap>("test_prune_delayed_merge"),
3183                None,
3184            )
3185            .await
3186            .unwrap();
3187
3188            let k = key(0);
3189
3190            for round in 0..200u64 {
3191                db = mmb_commit(db, [(k, Some(val(60_000 + round)))]).await;
3192            }
3193
3194            let boundary = db.sync_boundary();
3195            db = db.prune(boundary).await.unwrap();
3196            db = db.sync().await.unwrap();
3197
3198            // Keep growing without pruning: delayed merges now occur in the pruned region.
3199            for round in 200..300u64 {
3200                db = mmb_commit(db, [(key(1), Some(val(round)))]).await;
3201            }
3202
3203            let proof = db.key_value_proof(k).await.unwrap();
3204            assert!(UnorderedVariableMmbDb::verify_key_value_proof(
3205                k,
3206                val(60_000 + 199),
3207                &proof,
3208                &db.root()
3209            ));
3210
3211            let target_root = db.root();
3212            drop(db);
3213
3214            let reopen_ctx = context.child("db_reopen");
3215            let reopened: UnorderedVariableMmbDb = UnorderedVariableMmbDb::init(
3216                reopen_ctx.child("db"),
3217                variable_config::<OneCap>("test_prune_delayed_merge"),
3218                None,
3219            )
3220            .await
3221            .unwrap();
3222
3223            assert_eq!(reopened.root(), target_root);
3224
3225            let proof = reopened.key_value_proof(k).await.unwrap();
3226            assert!(UnorderedVariableMmbDb::verify_key_value_proof(
3227                k,
3228                val(60_000 + 199),
3229                &proof,
3230                &reopened.root()
3231            ));
3232
3233            reopened.destroy().await.unwrap();
3234        });
3235    }
3236
3237    /// Grow past 2 full pruned chunks, prune, reopen, verify root + value.
3238    #[test_traced]
3239    fn test_current_mmb_reopen_after_prune_two_chunks() {
3240        let executor = deterministic::Runner::default();
3241        executor.start(|context| async move {
3242            let db_ctx = context.child("db");
3243            let mut db: UnorderedVariableMmbDb = UnorderedVariableMmbDb::init(
3244                db_ctx.child("db"),
3245                variable_config::<OneCap>("test_prune_two"),
3246                None,
3247            )
3248            .await
3249            .unwrap();
3250
3251            let k = key(0);
3252            // Always assigned before the loop breaks.
3253            let mut expected;
3254
3255            // Keep growing until the settle guard allows 2+ pruned chunks.
3256            // The absorber for chunk pair [0,1] at gh=8 needs ~766 ops leaves.
3257            let mut round = 0u64;
3258            loop {
3259                expected = Some(val(60_000 + round));
3260                db = mmb_commit(db, [(k, expected)]).await;
3261                round += 1;
3262                let boundary = db.sync_boundary();
3263                db = db.prune(boundary).await.unwrap();
3264                if db.pruned_bits() >= 512 {
3265                    break;
3266                }
3267                assert!(
3268                    round < 500,
3269                    "failed to reach 2 pruned chunks after {round} commits"
3270                );
3271            }
3272            let db = db.sync().await.unwrap();
3273
3274            let target_root = db.root();
3275            drop(db);
3276
3277            let reopen_ctx = context.child("db_reopen");
3278            let reopened: UnorderedVariableMmbDb = UnorderedVariableMmbDb::init(
3279                reopen_ctx.child("db"),
3280                variable_config::<OneCap>("test_prune_two"),
3281                None,
3282            )
3283            .await
3284            .unwrap();
3285
3286            assert_eq!(reopened.root(), target_root);
3287            assert_eq!(reopened.get(&k).await.unwrap(), expected);
3288            reopened.destroy().await.unwrap();
3289        });
3290    }
3291
3292    /// Three rounds of grow + prune + reopen. Verifies repeated prune cycles don't diverge.
3293    #[test_traced]
3294    fn test_current_mmb_repeated_prune() {
3295        let executor = deterministic::Runner::default();
3296        executor.start(|context| async move {
3297            let mut db_ctx = context.child("db_init");
3298            let mut db: UnorderedVariableMmbDb = UnorderedVariableMmbDb::init(
3299                db_ctx.child("db"),
3300                variable_config::<OneCap>("test_repeated_prune"),
3301                None,
3302            )
3303            .await
3304            .unwrap();
3305
3306            for round in 0..3u64 {
3307                let k = key(round * 1000);
3308                let mut expected = None;
3309                for i in 0..90 {
3310                    expected = Some(val(round * 1000 + i));
3311                    db = mmb_commit(db, [(k, expected)]).await;
3312                }
3313
3314                let boundary = db.sync_boundary();
3315                db = db.prune(boundary).await.unwrap();
3316                db = db.sync().await.unwrap();
3317
3318                let root_before = db.root();
3319                db_ctx = context.child("db").with_attribute("round", round);
3320
3321                drop(db);
3322                db = UnorderedVariableMmbDb::init(
3323                    db_ctx.child("db"),
3324                    variable_config::<OneCap>("test_repeated_prune"),
3325                    None,
3326                )
3327                .await
3328                .unwrap();
3329
3330                assert_eq!(db.root(), root_before);
3331                assert_eq!(db.get(&k).await.unwrap(), expected);
3332            }
3333
3334            db.destroy().await.unwrap();
3335        });
3336    }
3337
3338    /// Step-by-step growth after prune, comparing roots against an unpruned reference.
3339    #[test_traced]
3340    fn test_current_mmb_stepwise_growth_matches_unpruned_reference() {
3341        let executor = deterministic::Runner::default();
3342        executor.start(|context| async move {
3343            let db_ctx = context.child("db_stepwise");
3344            let mut db: UnorderedVariableMmbDb = UnorderedVariableMmbDb::init(
3345                db_ctx.child("db"),
3346                variable_config::<OneCap>("test_stepwise"),
3347                None,
3348            )
3349            .await
3350            .unwrap();
3351
3352            let ref_ctx = context.child("ref_stepwise");
3353            let mut ref_db: UnorderedVariableMmbDb = UnorderedVariableMmbDb::init(
3354                ref_ctx.child("db"),
3355                variable_config::<OneCap>("test_stepwise_ref"),
3356                None,
3357            )
3358            .await
3359            .unwrap();
3360
3361            let k = key(0);
3362            let mut commit_idx = 0u64;
3363
3364            // Grow until the inactivity floor reaches 4 chunks.
3365            while *db.inactivity_floor_loc() < 1024 {
3366                let value = Some(val(80_000 + commit_idx));
3367                db = mmb_commit(db, [(k, value)]).await;
3368                ref_db = mmb_commit(ref_db, [(k, value)]).await;
3369                commit_idx += 1;
3370            }
3371
3372            let boundary = db.sync_boundary();
3373            db = db.prune(boundary).await.unwrap();
3374            db = db.sync().await.unwrap();
3375            assert_eq!(
3376                db.root(),
3377                ref_db.root(),
3378                "root mismatch immediately after prune"
3379            );
3380
3381            // Step-by-step growth through the delayed-merge window.
3382            loop {
3383                let db_leaves =
3384                    *Location::<mmb::Family>::try_from(db.any.log.merkle.size()).unwrap();
3385                if db_leaves >= 1560 {
3386                    break;
3387                }
3388
3389                let value = Some(val(80_000 + commit_idx));
3390                db = mmb_commit(db, [(k, value)]).await;
3391                ref_db = mmb_commit(ref_db, [(k, value)]).await;
3392                commit_idx += 1;
3393
3394                let db_leaves =
3395                    *Location::<mmb::Family>::try_from(db.any.log.merkle.size()).unwrap();
3396                assert_eq!(
3397                    db.root(),
3398                    ref_db.root(),
3399                    "stepwise root mismatch: leaves={db_leaves}, commit_idx={commit_idx}"
3400                );
3401            }
3402
3403            db.destroy().await.unwrap();
3404            ref_db.destroy().await.unwrap();
3405        });
3406    }
3407
3408    /// Multi-round prune + reopen + proof against an unpruned reference.
3409    #[test_traced]
3410    fn test_current_mmb_large_repeated_prune_matches_unpruned_reference() {
3411        let executor = deterministic::Runner::default();
3412        executor.start(|context| async move {
3413            const ROUNDS: u64 = 8;
3414            const COMMITS_PER_ROUND: u64 = 120;
3415
3416            let mut db_ctx = context.child("db_init");
3417            let mut db: UnorderedVariableMmbDb = UnorderedVariableMmbDb::init(
3418                db_ctx.child("db"),
3419                variable_config::<OneCap>("test_large_prune"),
3420                None,
3421            )
3422            .await
3423            .unwrap();
3424
3425            let ref_ctx = context.child("ref");
3426            let mut ref_db: UnorderedVariableMmbDb = UnorderedVariableMmbDb::init(
3427                ref_ctx.child("db"),
3428                variable_config::<OneCap>("test_large_prune_ref"),
3429                None,
3430            )
3431            .await
3432            .unwrap();
3433
3434            let k = key(0);
3435            let mut expected = None;
3436
3437            for round in 0..ROUNDS {
3438                for i in 0..COMMITS_PER_ROUND {
3439                    let value = Some(val(round * 10_000 + i));
3440                    expected = value;
3441                    db = mmb_commit(db, [(k, value)]).await;
3442                    ref_db = mmb_commit(ref_db, [(k, value)]).await;
3443                }
3444
3445                assert_eq!(
3446                    db.root(),
3447                    ref_db.root(),
3448                    "root mismatch before prune at round {round}"
3449                );
3450
3451                let boundary = db.sync_boundary();
3452                db = db.prune(boundary).await.unwrap();
3453                db = db.sync().await.unwrap();
3454
3455                assert_eq!(
3456                    db.root(),
3457                    ref_db.root(),
3458                    "root mismatch after prune at round {round}"
3459                );
3460
3461                let proof = db.key_value_proof(k).await.unwrap();
3462                assert!(
3463                    UnorderedVariableMmbDb::verify_key_value_proof(
3464                        k,
3465                        expected.expect("value should exist"),
3466                        &proof,
3467                        &db.root()
3468                    ),
3469                    "proof verification failed at round {round}"
3470                );
3471
3472                db_ctx = context.child("db_reopen").with_attribute("round", round);
3473                drop(db);
3474                db = UnorderedVariableMmbDb::init(
3475                    db_ctx.child("db"),
3476                    variable_config::<OneCap>("test_large_prune"),
3477                    None,
3478                )
3479                .await
3480                .unwrap();
3481
3482                assert_eq!(
3483                    db.root(),
3484                    ref_db.root(),
3485                    "root mismatch after reopen at round {round}"
3486                );
3487                assert_eq!(
3488                    db.get(&k).await.unwrap(),
3489                    expected,
3490                    "value mismatch after reopen at round {round}"
3491                );
3492
3493                let proof = db.key_value_proof(k).await.unwrap();
3494                assert!(
3495                    UnorderedVariableMmbDb::verify_key_value_proof(
3496                        k,
3497                        expected.expect("value should exist"),
3498                        &proof,
3499                        &db.root()
3500                    ),
3501                    "proof verification failed after reopen at round {round}"
3502                );
3503            }
3504
3505            db.destroy().await.unwrap();
3506            ref_db.destroy().await.unwrap();
3507        });
3508    }
3509
3510    /// Verify that prune beyond the sync boundary is rejected without mutating state.
3511    #[test_traced]
3512    fn test_current_prune_rejects_beyond_sync_boundary_without_mutation() {
3513        let executor = deterministic::Runner::default();
3514        executor.start(|context| async move {
3515            const COMMITS: u64 = 160;
3516
3517            let partition = "current-prune-beyond-boundary";
3518            let ctx = context.child("db");
3519            let mut db: UnorderedVariableDb = UnorderedVariableDb::init(
3520                ctx.child("storage"),
3521                variable_config::<OneCap>(partition),
3522                None,
3523            )
3524            .await
3525            .unwrap();
3526
3527            let key0 = key(0);
3528            for round in 0..COMMITS {
3529                db = commit_writes_with_metadata(db, [(key0, Some(val(40_000 + round)))], None)
3530                    .await;
3531            }
3532
3533            let expected_root = db.root();
3534            let expected_ops_root = db.ops_root();
3535            let expected_boundary = db.sync_boundary();
3536            let expected_pruned_bits = db.pruned_bits();
3537            let expected_value = db.get(&key0).await.unwrap();
3538
3539            // 32 * 8 = 256 bits per chunk for N=32.
3540            let invalid_prune_loc = expected_boundary + 256;
3541            let Err(err) = db.prune(invalid_prune_loc).await else {
3542                panic!("expected prune rejection above sync boundary");
3543            };
3544            assert!(
3545                matches!(err, Error::PruneBeyondMinRequired(loc, boundary)
3546                    if loc == invalid_prune_loc && boundary == expected_boundary),
3547                "expected prune rejection above sync boundary, got {err:?}"
3548            );
3549
3550            let reopened: UnorderedVariableDb = UnorderedVariableDb::init(
3551                context.child("reopen"),
3552                variable_config::<OneCap>(partition),
3553                None,
3554            )
3555            .await
3556            .unwrap();
3557            assert_eq!(reopened.root(), expected_root);
3558            assert_eq!(reopened.ops_root(), expected_ops_root);
3559            assert_eq!(reopened.pruned_bits(), expected_pruned_bits);
3560            assert_eq!(reopened.get(&key0).await.unwrap(), expected_value);
3561
3562            reopened.destroy().await.unwrap();
3563        });
3564    }
3565
3566    #[test_traced("INFO")]
3567    fn test_current_bounded_initialization_small_delta_large_history() {
3568        let executor = deterministic::Runner::default();
3569        executor.start(|context| async move {
3570            const COMMITS: u64 = 200;
3571
3572            let partition = "current-rewind-small-delta";
3573            let ctx = context.child("db");
3574            let mut db: UnorderedVariableDb = UnorderedVariableDb::init(
3575                ctx.child("storage"),
3576                variable_config::<OneCap>(partition),
3577                None,
3578            )
3579            .await
3580            .unwrap();
3581
3582            let key0 = key(0);
3583            let key1 = key(1);
3584            let mut history = Vec::new();
3585
3586            for round in 0..COMMITS {
3587                let key0_value = val(40_000 + round);
3588                let key1_value = if round % 3 == 1 {
3589                    None
3590                } else {
3591                    Some(val(50_000 + round))
3592                };
3593
3594                db = commit_writes_with_metadata(
3595                    db,
3596                    [(key0, Some(key0_value)), (key1, key1_value)],
3597                    None,
3598                )
3599                .await;
3600
3601                history.push((
3602                    db.bounds().end,
3603                    db.root(),
3604                    db.ops_root(),
3605                    key0_value,
3606                    key1_value,
3607                ));
3608            }
3609
3610            let target = *history
3611                .get(history.len() - 3)
3612                .expect("history should contain at least three commits");
3613            let (target_size, target_root, target_ops_root, target_key0, target_key1) = target;
3614
3615            let db = {
3616                _ = db.sync().await.unwrap();
3617                UnorderedVariableDb::init(
3618                    ctx.child("cap"),
3619                    variable_config::<OneCap>(partition),
3620                    Some(target_size),
3621                )
3622                .await
3623            }
3624            .unwrap();
3625            assert_eq!(db.bounds().end, target_size);
3626            assert_eq!(db.root(), target_root);
3627            assert_eq!(db.ops_root(), target_ops_root);
3628            assert_eq!(db.get(&key0).await.unwrap(), Some(target_key0));
3629            assert_eq!(db.get(&key1).await.unwrap(), target_key1);
3630
3631            let db = db.commit().await.unwrap();
3632            drop(db);
3633
3634            let reopened: UnorderedVariableDb = UnorderedVariableDb::init(
3635                context.child("reopen_small_delta"),
3636                variable_config::<OneCap>(partition),
3637                None,
3638            )
3639            .await
3640            .unwrap();
3641            assert_eq!(reopened.bounds().end, target_size);
3642            assert_eq!(reopened.root(), target_root);
3643            assert_eq!(reopened.ops_root(), target_ops_root);
3644            assert_eq!(reopened.get(&key0).await.unwrap(), Some(target_key0));
3645            assert_eq!(reopened.get(&key1).await.unwrap(), target_key1);
3646
3647            reopened.destroy().await.unwrap();
3648        });
3649    }
3650
3651    #[test_traced("INFO")]
3652    fn test_current_bounded_initialization_pruned_target_errors() {
3653        let executor = deterministic::Runner::default();
3654        executor.start(|context| async move {
3655            const KEYS: u64 = 384;
3656
3657            let partition = "current-rewind-pruned";
3658            let ctx = context.child("db");
3659            let db: UnorderedVariableDb =
3660                UnorderedVariableDb::init(
3661                    ctx.child("storage"),
3662                    variable_config::<OneCap>(partition), None,
3663                )
3664                    .await
3665                    .unwrap();
3666
3667            let first_start = db.bounds().end;
3668            let db = commit_writes_with_metadata(
3669                db,
3670                (0..KEYS).map(|i| (key(i), Some(val(i)))),
3671                None,
3672            )
3673            .await;
3674            let db = commit_writes_with_metadata(
3675                db,
3676                (0..KEYS).map(|i| (key(i), Some(val(1000 + i)))),
3677                None,
3678            )
3679            .await;
3680
3681            let boundary = db.sync_boundary();
3682            let db = db.prune(boundary).await.unwrap();
3683            let pruned_bits = db.pruned_bits();
3684            assert!(
3685                pruned_bits > *first_start,
3686                "expected bitmap pruning boundary above initialization bound: pruned_bits={pruned_bits}, target={:?}",
3687                first_start
3688            );
3689
3690            let oldest_retained = db.bounds().start;
3691            _ = db.sync().await.unwrap();
3692            let Err(boundary_err) = UnorderedVariableDb::init(
3693                ctx.child("cap"),
3694                variable_config::<OneCap>(partition),
3695                Some(oldest_retained),
3696            )
3697            .await else {
3698                panic!("expected initialization rejection at retained boundary");
3699            };
3700            assert!(
3701                matches!(
3702                    boundary_err,
3703                    Error::Journal(crate::journal::Error::ItemPruned(_))
3704                ),
3705                "unexpected bounded initialization error at retained boundary: {boundary_err:?}"
3706            );
3707
3708            let db: UnorderedVariableDb = UnorderedVariableDb::init(
3709                ctx.child("reopen"),
3710                variable_config::<OneCap>(partition), None,
3711            )
3712            .await
3713            .unwrap();
3714            let expected_pruned_loc = *first_start;
3715            _ = db.sync().await.unwrap();
3716            let Err(err) = UnorderedVariableDb::init(
3717                ctx.child("cap"),
3718                variable_config::<OneCap>(partition),
3719                Some(first_start),
3720            )
3721            .await else {
3722                panic!("expected initialization rejection at pruned target");
3723            };
3724            assert!(
3725                matches!(
3726                    err,
3727                    Error::Journal(crate::journal::Error::ItemPruned(loc))
3728                    if loc == expected_pruned_loc
3729                ),
3730                "unexpected bounded initialization error: {err:?}"
3731            );
3732        });
3733    }
3734
3735    /// A retained commit is rejected when its inactivity floor lies at or above the log's
3736    /// retained start but below the chunk-aligned bitmap boundary: the log holds the commit's
3737    /// whole active range, but the bitmap chunk covering its floor is gone.
3738    #[test_traced("INFO")]
3739    fn test_current_bounded_initialization_rejects_target_below_bitmap_floor() {
3740        let executor = deterministic::Runner::default();
3741        executor.start(|context| async move {
3742            const COMMITS: u64 = 120;
3743
3744            let partition = "current-rewind-bitmap-floor";
3745            let ctx = context.child("db");
3746            let mut db: UnorderedVariableDb = UnorderedVariableDb::init(
3747                ctx.child("storage"),
3748                variable_config::<OneCap>(partition),
3749                None,
3750            )
3751            .await
3752            .unwrap();
3753
3754            // Each round updates one key, moves the previous write and commits, so every commit
3755            // keeps a floor two operations below its size.
3756            let mut history = Vec::new();
3757            for round in 0..COMMITS {
3758                db = commit_writes_with_metadata(db, [(key(0), Some(val(10_000 + round)))], None)
3759                    .await;
3760                history.push((db.bounds().end, db.inactivity_floor_loc()));
3761            }
3762
3763            // Prune to the first chunk boundary. The bitmap lands exactly there, while the log
3764            // keeps whole sections and retains from below it.
3765            let prune_loc = Location::new(CHUNK_BITS);
3766            assert!(prune_loc <= db.sync_boundary());
3767            let db = db.prune(prune_loc).await.unwrap();
3768            let pruned_bits = db.pruned_bits();
3769            assert_eq!(pruned_bits, CHUNK_BITS);
3770            let retained_start = db.bounds().start;
3771            assert!(retained_start < prune_loc);
3772
3773            // Pick a commit the log retains in full whose floor the bitmap has pruned.
3774            let (target_size, target_floor) = history
3775                .iter()
3776                .copied()
3777                .find(|(size, floor)| {
3778                    **size >= pruned_bits && *floor >= retained_start && **floor < pruned_bits
3779                })
3780                .unwrap_or_else(|| {
3781                    panic!(
3782                        "expected initialization target below bitmap boundary. \
3783                         retained_start={retained_start:?}, pruned_bits={pruned_bits}, \
3784                         history={history:?}"
3785                    )
3786                });
3787            assert!(retained_start <= target_floor);
3788            assert!(*target_floor < pruned_bits);
3789            assert!(pruned_bits <= *target_size);
3790
3791            let original_root = db.root();
3792            _ = db.sync().await.unwrap();
3793            let Err(err) = UnorderedVariableDb::init(
3794                ctx.child("cap"),
3795                variable_config::<OneCap>(partition),
3796                Some(target_size),
3797            )
3798            .await
3799            else {
3800                panic!("expected initialization rejection below bitmap floor");
3801            };
3802            assert!(
3803                matches!(err, Error::HistoricalFloorPruned(loc) if loc == target_size),
3804                "unexpected bounded initialization error: {err:?}"
3805            );
3806            let db = UnorderedVariableDb::init(
3807                ctx.child("unchanged"),
3808                variable_config::<OneCap>(partition),
3809                None,
3810            )
3811            .await
3812            .unwrap();
3813            assert_eq!(db.root(), original_root);
3814        });
3815    }
3816
3817    /// Model the glue maintenance prune: four generations rewrite the same keys, the database
3818    /// is pruned to the second generation's sync boundary, every generation from there up must
3819    /// still initialize bounded with its recorded root and sync boundary, and the first
3820    /// generation, below the retained start, is rejected.
3821    #[test_traced("INFO")]
3822    fn test_current_prune_keeps_retained_checkpoints_initializable() {
3823        let executor = deterministic::Runner::default();
3824        executor.start(|context| async move {
3825            let partition = "current-prune-retained-checkpoints";
3826            let ctx = context.child("db");
3827            let (db, history) = build_generations(&ctx, partition, 4).await;
3828
3829            // The database's sync boundary sits above the older generations' floors, so pruning the
3830            // bitmap to it instead of to `prune_loc` would reject them.
3831            assert!(history[2].floor < db.sync_boundary());
3832
3833            // Prune as the glue adapter does, to the oldest retained generation's sync boundary.
3834            // That boundary is a whole chunk above zero, so the prune moves both the log and the
3835            // bitmap, and the bitmap lands on the requested boundary rather than the log's
3836            // section boundary.
3837            let prune_loc = history[1].sync_boundary;
3838            assert!(prune_loc >= Location::new(CHUNK_BITS));
3839            let db = db.prune(prune_loc).await.unwrap();
3840            assert_eq!(db.pruned_bits(), *prune_loc);
3841            let retained_start = db.bounds().start;
3842            assert!(retained_start > Location::new(0));
3843            assert!(history[0].size <= retained_start);
3844            let db = db.sync().await.unwrap();
3845            drop(db);
3846
3847            for (label, generation) in [
3848                ("gen3", &history[3]),
3849                ("gen2", &history[2]),
3850                ("gen1", &history[1]),
3851            ] {
3852                let db = UnorderedVariableDb::init(
3853                    ctx.child(label),
3854                    variable_config::<OneCap>(partition),
3855                    Some(generation.size),
3856                )
3857                .await
3858                .unwrap();
3859                assert_eq!(db.bounds().end, generation.size);
3860                assert_eq!(db.root(), generation.root);
3861                assert_eq!(db.sync_boundary(), generation.sync_boundary);
3862            }
3863
3864            let Err(err) = UnorderedVariableDb::init(
3865                ctx.child("gen0"),
3866                variable_config::<OneCap>(partition),
3867                Some(history[0].size),
3868            )
3869            .await
3870            else {
3871                panic!("expected initialization rejection below the retained start");
3872            };
3873            assert!(
3874                matches!(
3875                    err,
3876                    Error::Journal(crate::journal::Error::ItemPruned(loc))
3877                    if loc == *history[0].size
3878                ),
3879                "unexpected bounded initialization error: {err:?}"
3880            );
3881        });
3882    }
3883
3884    /// Verify that the speculative canonical root from a merkleized batch matches the root
3885    /// recomputed from committed state after sync + reopen.
3886    ///
3887    /// Uses enough operations to cross a chunk boundary (CHUNK_SIZE_BITS = N*8), which exercises
3888    /// the grafted root computation for newly completed chunks.
3889    pub async fn test_speculative_root_matches_committed<M, C, F, Fut>(
3890        context: Context,
3891        mut open_db: F,
3892    ) where
3893        M: merkle::Graftable,
3894        C: DbAny<M> + 'static,
3895        C::Key: TestKey,
3896        <C as DbAny<M>>::Value: TestValue,
3897        F: FnMut(Context, String) -> Fut + Clone,
3898        Fut: Future<Output = C>,
3899    {
3900        let mut open_db_clone = open_db.clone();
3901        let partition = "speculative-root".to_string();
3902
3903        // Write enough operations to cross a chunk boundary. With N=32 (CHUNK_SIZE_BITS=256),
3904        // 260 writes + 1 CommitFloor = 261 operations, completing one chunk with 5 ops in the
3905        // next partial chunk. This ensures the grafted root computation must handle the
3906        // newly completed chunk.
3907        let db: C = Box::pin(open_db_clone(context.child("init"), partition.clone())).await;
3908        let mut batch = db.new_batch();
3909        for i in 0..260 {
3910            batch = batch.write(TestKey::from_seed(i), Some(TestValue::from_seed(i + 1000)));
3911        }
3912        let merkleized = batch.merkleize(&db, None, &mut Proportional).await.unwrap();
3913        let db = db.apply_batch(merkleized).await.unwrap();
3914        let speculative_root = db.root();
3915
3916        // Sync, close, and reopen to get the root recomputed from committed state.
3917        db.sync().await.unwrap();
3918
3919        let db: C = Box::pin(open_db(context.child("reopen"), partition)).await;
3920        assert_eq!(db.root(), speculative_root);
3921
3922        db.destroy().await.unwrap();
3923    }
3924
3925    test_for_all_variants!(test_speculative_root_matches_committed, "INFO");
3926
3927    /// MerkleizedBatch::get() at the current level reads the batch's diff, then the database.
3928    #[test_traced("INFO")]
3929    fn test_current_batch_merkleized_get() {
3930        let executor = deterministic::Runner::default();
3931        executor.start(|context| async move {
3932            let ctx = context.child("db");
3933            let mut db: UnorderedVariableDb = UnorderedVariableDb::init(
3934                ctx.child("storage"),
3935                variable_config::<OneCap>("mg"),
3936                None,
3937            )
3938            .await
3939            .unwrap();
3940
3941            let ka = key(0);
3942            let kb = key(1);
3943            let kc = key(2);
3944
3945            // Pre-populate A.
3946            {
3947                let mut batch = db.new_batch();
3948                batch = batch.write(ka, Some(val(0)));
3949                let merkleized = batch.merkleize(&db, None, &mut Proportional).await.unwrap();
3950                db = db.apply_batch(merkleized).await.unwrap();
3951            }
3952
3953            // Batch: update A, delete nothing, create B.
3954            let va2 = val(100);
3955            let vb = val(1);
3956            let mut batch = db.new_batch();
3957            batch = batch.write(ka, Some(va2));
3958            batch = batch.write(kb, Some(vb));
3959            let merkleized = batch.merkleize(&db, None, &mut Proportional).await.unwrap();
3960
3961            assert_eq!(merkleized.get(&ka, &db).await.unwrap(), Some(va2));
3962            assert_eq!(merkleized.get(&kb, &db).await.unwrap(), Some(vb));
3963            assert_eq!(merkleized.get(&kc, &db).await.unwrap(), None);
3964
3965            db.destroy().await.unwrap();
3966        });
3967    }
3968
3969    /// Batch chaining at the current level: parent -> merkleize -> child -> merkleize.
3970    /// Child's canonical root matches db.root() after apply.
3971    #[test_traced("INFO")]
3972    fn test_current_batch_chaining() {
3973        let executor = deterministic::Runner::default();
3974        executor.start(|context| async move {
3975            let ctx = context.child("db");
3976            let db: UnorderedVariableDb = UnorderedVariableDb::init(
3977                ctx.child("storage"),
3978                variable_config::<OneCap>("ch"),
3979                None,
3980            )
3981            .await
3982            .unwrap();
3983
3984            // Parent batch writes keys 0..5.
3985            let mut parent = db.new_batch();
3986            for i in 0..5 {
3987                parent = parent.write(key(i), Some(val(i)));
3988            }
3989            let parent_m = parent
3990                .merkleize(&db, None, &mut Proportional)
3991                .await
3992                .unwrap();
3993
3994            // Child batch writes keys 5..10 and overrides key 0.
3995            let mut child = parent_m.new_batch::<Sha256>();
3996            for i in 5..10 {
3997                child = child.write(key(i), Some(val(i)));
3998            }
3999            child = child.write(key(0), Some(val(999)));
4000            let child_m = child.merkleize(&db, None, &mut Proportional).await.unwrap();
4001
4002            let child_root = child_m.root();
4003
4004            // Child get reads through all layers.
4005            assert_eq!(child_m.get(&key(0), &db).await.unwrap(), Some(val(999)));
4006            assert_eq!(child_m.get(&key(3), &db).await.unwrap(), Some(val(3)));
4007            assert_eq!(child_m.get(&key(7), &db).await.unwrap(), Some(val(7)));
4008
4009            let db = db.apply_batch(child_m).await.unwrap();
4010            assert_eq!(db.root(), child_root);
4011
4012            // Verify all keys are correct.
4013            assert_eq!(db.get(&key(0)).await.unwrap(), Some(val(999)));
4014            for i in 1..10 {
4015                assert_eq!(db.get(&key(i)).await.unwrap(), Some(val(i)));
4016            }
4017
4018            db.destroy().await.unwrap();
4019        });
4020    }
4021
4022    #[test_traced("INFO")]
4023    fn test_current_unordered_root_matches_between_pending_and_committed_paths() {
4024        let executor = deterministic::Runner::default();
4025        executor.start(|context| async move {
4026            let ctx = context.child("db");
4027            let db: UnorderedFixedDb =
4028                UnorderedFixedDb::init(ctx.child("storage"), fixed_config::<OneCap>("ucr"), None)
4029                    .await
4030                    .unwrap();
4031            let key_a = colliding_digest(0xAA, 1);
4032            let key_b = colliding_digest(0xAA, 0);
4033
4034            // Seed four colliding committed keys, then update only key_a.
4035            // The specific 4 / 1 / 0 shape is a concrete counterexample:
4036            // key_b remains outside the parent diff and is still resolved
4037            // through the committed snapshot in the child.
4038            let mut initial = db.new_batch();
4039            for i in 0..4 {
4040                initial = initial.write(colliding_digest(0xAA, i), Some(colliding_digest(0xBB, i)));
4041            }
4042            let merkleized = initial
4043                .merkleize(&db, None, &mut Proportional)
4044                .await
4045                .unwrap();
4046            let db = db.apply_batch(merkleized).await.unwrap();
4047            let db = db.commit().await.unwrap();
4048
4049            // Update only key_a so the colliding sibling key_b remains outside
4050            // the parent diff and must still be resolved through the committed
4051            // snapshot in the child.
4052            let parent = db
4053                .new_batch()
4054                .write(key_a, Some(colliding_digest(0xCC, 1)))
4055                .merkleize(&db, None, &mut Proportional)
4056                .await
4057                .unwrap();
4058
4059            // Build the child while the parent is unapplied, then rebuild
4060            // the same logical child after committing the parent and compare
4061            // both canonical and ops roots.
4062            let pending_child = parent
4063                .new_batch::<Sha256>()
4064                .write(key_a, Some(colliding_digest(0xDD, 1)))
4065                .write(key_b, Some(colliding_digest(0xDD, 0)))
4066                .merkleize(&db, None, &mut Proportional)
4067                .await
4068                .unwrap();
4069
4070            let pending_root = pending_child.root();
4071            let pending_ops_root = pending_child.ops_root();
4072
4073            let db = db.apply_batch(parent).await.unwrap();
4074            let db = db.commit().await.unwrap();
4075
4076            let committed_child = db
4077                .new_batch()
4078                .write(key_a, Some(colliding_digest(0xDD, 1)))
4079                .write(key_b, Some(colliding_digest(0xDD, 0)))
4080                .merkleize(&db, None, &mut Proportional)
4081                .await
4082                .unwrap();
4083
4084            assert_eq!(pending_root, committed_child.root());
4085            assert_eq!(pending_ops_root, committed_child.ops_root());
4086
4087            // Apply `pending_child` onto the committed parent and ensure the applied wrapper roots
4088            // still match.
4089            let db = db.apply_batch(pending_child).await.unwrap();
4090            assert_eq!(db.root(), committed_child.root());
4091            assert_eq!(db.ops_root(), committed_child.ops_root());
4092
4093            db.destroy().await.unwrap();
4094        });
4095    }
4096
4097    #[test_traced("INFO")]
4098    fn test_current_ordered_root_matches_between_pending_and_committed_paths() {
4099        let executor = deterministic::Runner::default();
4100        executor.start(|context| async move {
4101            let ctx = context.child("db");
4102            let db: OrderedFixedDb =
4103                OrderedFixedDb::init(ctx.child("storage"), fixed_config::<OneCap>("ocr"), None)
4104                    .await
4105                    .unwrap();
4106            let key_a = colliding_digest(0xAA, 1);
4107            let key_b = colliding_digest(0xAA, 0);
4108
4109            // Match the unordered counterexample shape on the ordered path so
4110            // both wrappers exercise the same collision pattern.
4111            let mut initial = db.new_batch();
4112            for i in 0..4 {
4113                initial = initial.write(colliding_digest(0xAA, i), Some(colliding_digest(0xBB, i)));
4114            }
4115            let merkleized = initial
4116                .merkleize(&db, None, &mut Proportional)
4117                .await
4118                .unwrap();
4119            let db = db.apply_batch(merkleized).await.unwrap();
4120            let db = db.commit().await.unwrap();
4121
4122            // Update only key_a so the colliding sibling key_b remains outside
4123            // the parent diff and must still be resolved through the committed
4124            // snapshot in the child.
4125            let parent = db
4126                .new_batch()
4127                .write(key_a, Some(colliding_digest(0xCC, 1)))
4128                .merkleize(&db, None, &mut Proportional)
4129                .await
4130                .unwrap();
4131
4132            // Build the child while the parent is unapplied, then rebuild
4133            // the same logical child after committing the parent.
4134            let pending_child = parent
4135                .new_batch::<Sha256>()
4136                .write(key_a, Some(colliding_digest(0xDD, 1)))
4137                .write(key_b, Some(colliding_digest(0xDD, 0)))
4138                .merkleize(&db, None, &mut Proportional)
4139                .await
4140                .unwrap();
4141
4142            let pending_root = pending_child.root();
4143            let pending_ops_root = pending_child.ops_root();
4144
4145            let db = db.apply_batch(parent).await.unwrap();
4146            let db = db.commit().await.unwrap();
4147
4148            let committed_child = db
4149                .new_batch()
4150                .write(key_a, Some(colliding_digest(0xDD, 1)))
4151                .write(key_b, Some(colliding_digest(0xDD, 0)))
4152                .merkleize(&db, None, &mut Proportional)
4153                .await
4154                .unwrap();
4155
4156            assert_eq!(pending_root, committed_child.root());
4157            assert_eq!(pending_ops_root, committed_child.ops_root());
4158
4159            // Apply `pending_child` onto the committed parent and compare the applied wrapper roots
4160            // with the committed-path child roots.
4161            let db = db.apply_batch(pending_child).await.unwrap();
4162            assert_eq!(db.root(), committed_child.root());
4163            assert_eq!(db.ops_root(), committed_child.ops_root());
4164
4165            db.destroy().await.unwrap();
4166        });
4167    }
4168
4169    /// Applying without `commit()` publishes in memory but is not recovered after reopen.
4170    #[test_traced("INFO")]
4171    fn test_current_batch_apply_requires_commit_for_recovery() {
4172        let executor = deterministic::Runner::default();
4173        executor.start(|context| async move {
4174            let partition = "apply_requires_commit";
4175            let ctx = context.child("db");
4176            let db: UnorderedVariableDb = UnorderedVariableDb::init(
4177                ctx.child("storage"),
4178                variable_config::<OneCap>(partition),
4179                None,
4180            )
4181            .await
4182            .unwrap();
4183
4184            let committed_root = db.root();
4185
4186            let merkleized = db
4187                .new_batch()
4188                .write(key(0), Some(val(0)))
4189                .merkleize(&db, None, &mut Proportional)
4190                .await
4191                .unwrap();
4192            let db = db.apply_batch(merkleized).await.unwrap();
4193
4194            assert_eq!(db.get(&key(0)).await.unwrap(), Some(val(0)));
4195
4196            drop(db);
4197
4198            let reopened: UnorderedVariableDb = UnorderedVariableDb::init(
4199                context.child("reopen"),
4200                variable_config::<OneCap>(partition),
4201                None,
4202            )
4203            .await
4204            .unwrap();
4205            assert_eq!(reopened.root(), committed_root);
4206            assert_eq!(reopened.get(&key(0)).await.unwrap(), None);
4207
4208            reopened.destroy().await.unwrap();
4209        });
4210    }
4211
4212    /// One-stage pipelining lets the next batch be built while the prior batch commits.
4213    #[test_traced("INFO")]
4214    fn test_current_batch_single_stage_pipeline() {
4215        let executor = deterministic::Runner::default();
4216        executor.start(|context| async move {
4217            let ctx = context.child("db");
4218            let db: UnorderedVariableDb = UnorderedVariableDb::init(
4219                ctx.child("storage"),
4220                variable_config::<OneCap>("pipe"),
4221                None,
4222            )
4223            .await
4224            .unwrap();
4225
4226            let mut batch = db.new_batch();
4227            batch = batch.write(key(0), Some(val(0)));
4228            let parent_merkleized = batch.merkleize(&db, None, &mut Proportional).await.unwrap();
4229            let db = db.apply_batch(parent_merkleized).await.unwrap();
4230
4231            let child_merkleized = {
4232                assert_eq!(db.get(&key(0)).await.unwrap(), Some(val(0)));
4233                let mut child = db.new_batch();
4234                child = child.write(key(1), Some(val(1)));
4235                child.merkleize(&db, None, &mut Proportional).await.unwrap()
4236            };
4237            let db = db.commit().await.unwrap();
4238
4239            let db = db.apply_batch(child_merkleized).await.unwrap();
4240            let db = db.commit().await.unwrap();
4241
4242            assert_eq!(db.get(&key(0)).await.unwrap(), Some(val(0)));
4243            assert_eq!(db.get(&key(1)).await.unwrap(), Some(val(1)));
4244
4245            db.destroy().await.unwrap();
4246        });
4247    }
4248
4249    /// Apply parent then child sequentially. Both keys
4250    /// present and canonical root matches a fresh single-batch build.
4251    #[test_traced("INFO")]
4252    fn test_current_sequential_commit() {
4253        let executor = deterministic::Runner::default();
4254        executor.start(|context| async move {
4255            let ctx = context.child("db");
4256            let db: UnorderedVariableDb = UnorderedVariableDb::init(
4257                ctx.child("storage"),
4258                variable_config::<OneCap>("ff"),
4259                None,
4260            )
4261            .await
4262            .unwrap();
4263
4264            // Parent batch: insert key(0).
4265            let parent_m = db
4266                .new_batch()
4267                .write(key(0), Some(val(0)))
4268                .merkleize(&db, None, &mut Proportional)
4269                .await
4270                .unwrap();
4271
4272            // Child batch on parent: insert key(1).
4273            let child_m = parent_m
4274                .new_batch::<Sha256>()
4275                .write(key(1), Some(val(1)))
4276                .merkleize(&db, None, &mut Proportional)
4277                .await
4278                .unwrap();
4279
4280            let db = db.apply_batch(parent_m).await.unwrap();
4281            let db = db.apply_batch(child_m).await.unwrap();
4282
4283            // Both keys present.
4284            assert_eq!(db.get(&key(0)).await.unwrap(), Some(val(0)));
4285            assert_eq!(db.get(&key(1)).await.unwrap(), Some(val(1)));
4286
4287            // Build the same result via two sequential plain batches in a fresh DB
4288            // and verify the roots match.
4289            let ctx2 = context.child("db").with_attribute("index", 2);
4290            let db2: UnorderedVariableDb =
4291                UnorderedVariableDb::init(ctx2.child("db"), variable_config::<OneCap>("ff2"), None)
4292                    .await
4293                    .unwrap();
4294            let m1 = db2
4295                .new_batch()
4296                .write(key(0), Some(val(0)))
4297                .merkleize(&db2, None, &mut Proportional)
4298                .await
4299                .unwrap();
4300            let db2 = db2.apply_batch(m1).await.unwrap();
4301            let m2 = db2
4302                .new_batch()
4303                .write(key(1), Some(val(1)))
4304                .merkleize(&db2, None, &mut Proportional)
4305                .await
4306                .unwrap();
4307            let db2 = db2.apply_batch(m2).await.unwrap();
4308
4309            assert_eq!(db.root(), db2.root());
4310
4311            db.destroy().await.unwrap();
4312            db2.destroy().await.unwrap();
4313        });
4314    }
4315
4316    /// to_batch() produces a MerkleizedBatch that can be used to chain further
4317    /// batches via new_batch().
4318    #[test_traced("INFO")]
4319    fn test_current_to_batch_then_chain() {
4320        let executor = deterministic::Runner::default();
4321        executor.start(|context| async move {
4322            let ctx = context.child("db");
4323            let db: UnorderedVariableDb = UnorderedVariableDb::init(
4324                ctx.child("storage"),
4325                variable_config::<OneCap>("tb"),
4326                None,
4327            )
4328            .await
4329            .unwrap();
4330
4331            // Apply an initial batch.
4332            let m = db
4333                .new_batch()
4334                .write(key(0), Some(val(0)))
4335                .merkleize(&db, None, &mut Proportional)
4336                .await
4337                .unwrap();
4338            let db = db.apply_batch(m).await.unwrap();
4339
4340            // Get an owned batch at the DB's current state.
4341            let snapshot = db.to_batch();
4342            assert_eq!(snapshot.root(), db.root());
4343
4344            // Chain a child batch from `snapshot`.
4345            let child = snapshot
4346                .new_batch::<Sha256>()
4347                .write(key(1), Some(val(1)))
4348                .merkleize(&db, None, &mut Proportional)
4349                .await
4350                .unwrap();
4351
4352            // The child's root should differ from `snapshot`'s.
4353            assert_ne!(child.root(), snapshot.root());
4354
4355            // Apply child.
4356            let db = db.apply_batch(child).await.unwrap();
4357            assert_eq!(db.get(&key(0)).await.unwrap(), Some(val(0)));
4358            assert_eq!(db.get(&key(1)).await.unwrap(), Some(val(1)));
4359
4360            db.destroy().await.unwrap();
4361        });
4362    }
4363
4364    /// A held batch built on the DB must remain readable and applicable after [`Db::prune`]
4365    /// advances the shared bitmap's pruning boundary. Pruning only discards chunks for inactive
4366    /// bits (below the inactivity floor), and the batch's own chain and overlays operate at or
4367    /// above the floor, so no reads should land in the pruned region.
4368    #[test_traced("INFO")]
4369    fn test_current_live_batch_safe_across_prune() {
4370        let executor = deterministic::Runner::default();
4371        executor.start(|context| async move {
4372            let ctx = context.child("db");
4373            let db: UnorderedVariableDb = UnorderedVariableDb::init(
4374                ctx.child("storage"),
4375                variable_config::<OneCap>("prune-live"),
4376                None,
4377            )
4378            .await
4379            .unwrap();
4380
4381            // Seed enough ops to span multiple bitmap chunks.
4382            let mut seed = db.new_batch();
4383            for i in 0u64..300 {
4384                seed = seed.write(key(i), Some(val(i)));
4385            }
4386            let seed_m = seed.merkleize(&db, None, &mut Proportional).await.unwrap();
4387            let db = db.apply_batch(seed_m).await.unwrap();
4388            let db = db.commit().await.unwrap();
4389
4390            // Overwrite keys 0..250 so the inactivity floor advances past chunk 0.
4391            let mut p = db.new_batch();
4392            for i in 0u64..250 {
4393                p = p.write(key(i), Some(val(i + 10_000)));
4394            }
4395            let p_m = p.merkleize(&db, None, &mut Proportional).await.unwrap();
4396            let db = db.apply_batch(Arc::clone(&p_m)).await.unwrap();
4397            let db = db.commit().await.unwrap();
4398
4399            // Build c off p_m. c is held and shares the DB's bitmap via its chain.
4400            let c = p_m
4401                .new_batch::<Sha256>()
4402                .write(key(250), Some(val(99_999)))
4403                .merkleize(&db, None, &mut Proportional)
4404                .await
4405                .unwrap();
4406
4407            // Prune with c still held. This advances pruned_chunks on the shared bitmap.
4408            let boundary = db.sync_boundary();
4409            let db = db.prune(boundary).await.unwrap();
4410
4411            // Sanity: c's pending write is still readable via the any-layer diff chain.
4412            assert_eq!(c.get(&key(250), &db).await.unwrap(), Some(val(99_999)));
4413
4414            // The actual prune-interaction test: apply c after prune. apply_batch skips overlay
4415            // chunks below the current pruned boundary.
4416            let db = db.apply_batch(c).await.unwrap();
4417            assert_eq!(db.get(&key(0)).await.unwrap(), Some(val(10_000)));
4418            assert_eq!(db.get(&key(250)).await.unwrap(), Some(val(99_999)));
4419
4420            db.destroy().await.unwrap();
4421        });
4422    }
4423
4424    /// Regression: extending a batch after it has been applied (building a child off the
4425    /// just-applied parent) must produce correct data.
4426    ///
4427    /// With the shared-bitmap `RwLock` design, applying `A` mutates the DB's bitmap in place.
4428    /// Reads through `A`'s chain after apply reach the DB's bitmap (which now reflects `A`'s
4429    /// state), and `A`'s own overlays applied on top agree with it. So `A.new_batch()` followed
4430    /// by merkleize + apply is the right-by-construction case, and this test locks it in.
4431    #[test_traced("INFO")]
4432    fn test_current_extend_applied_batch() {
4433        let executor = deterministic::Runner::default();
4434        executor.start(|context| async move {
4435            let ctx = context.child("db");
4436            let db: UnorderedVariableDb = UnorderedVariableDb::init(
4437                ctx.child("storage"),
4438                variable_config::<OneCap>("xtend"),
4439                None,
4440            )
4441            .await
4442            .unwrap();
4443
4444            // Apply A, retaining our Arc so we can extend it post-apply.
4445            let a = db
4446                .new_batch()
4447                .write(key(0), Some(val(0)))
4448                .merkleize(&db, None, &mut Proportional)
4449                .await
4450                .unwrap();
4451            let db = db.apply_batch(Arc::clone(&a)).await.unwrap();
4452
4453            // Build B off A after A was applied. B's chain walks through A's layer and falls
4454            // through to the DB's bitmap (now post-A). B's merkleize must read matching state
4455            // from both sources.
4456            let b = a
4457                .new_batch::<Sha256>()
4458                .write(key(1), Some(val(1)))
4459                .merkleize(&db, None, &mut Proportional)
4460                .await
4461                .unwrap();
4462            let db = db.apply_batch(b).await.unwrap();
4463
4464            assert_eq!(db.get(&key(0)).await.unwrap(), Some(val(0)));
4465            assert_eq!(db.get(&key(1)).await.unwrap(), Some(val(1)));
4466
4467            // Extend once more to lock in multi-generation behavior.
4468            let c = db
4469                .new_batch()
4470                .write(key(2), Some(val(2)))
4471                .merkleize(&db, None, &mut Proportional)
4472                .await
4473                .unwrap();
4474            let db = db.apply_batch(c).await.unwrap();
4475
4476            assert_eq!(db.get(&key(0)).await.unwrap(), Some(val(0)));
4477            assert_eq!(db.get(&key(1)).await.unwrap(), Some(val(1)));
4478            assert_eq!(db.get(&key(2)).await.unwrap(), Some(val(2)));
4479
4480            db.destroy().await.unwrap();
4481        });
4482    }
4483
4484    /// Build a child batch from a held parent whose apply was followed by a prune, then
4485    /// merkleize and apply the child. The parent's `BitmapBatch` chain terminates in the DB's
4486    /// shared bitmap, and `prune` mutates that bitmap's pruning boundary in place. When the
4487    /// child is constructed via `parent.new_batch()`, the internal `trim_committed` call must
4488    /// observe the advanced boundary and produce a correct child chain, and merkleize and apply
4489    /// must then produce correct state for keys at and beyond the advanced floor.
4490    #[test_traced("INFO")]
4491    fn test_current_live_batch_child_after_prune() {
4492        let executor = deterministic::Runner::default();
4493        executor.start(|context| async move {
4494            let ctx = context.child("db");
4495            let db: UnorderedVariableDb = UnorderedVariableDb::init(
4496                ctx.child("storage"),
4497                variable_config::<OneCap>("child-after-prune"),
4498                None,
4499            )
4500            .await
4501            .unwrap();
4502
4503            // Seed enough ops to span multiple bitmap chunks.
4504            let mut seed = db.new_batch();
4505            for i in 0u64..300 {
4506                seed = seed.write(key(i), Some(val(i)));
4507            }
4508            let seed_m = seed.merkleize(&db, None, &mut Proportional).await.unwrap();
4509            let db = db.apply_batch(seed_m).await.unwrap();
4510            let db = db.commit().await.unwrap();
4511
4512            // Overwrite keys 0..250 so the inactivity floor advances past chunk 0.
4513            let mut a_batch = db.new_batch();
4514            for i in 0u64..250 {
4515                a_batch = a_batch.write(key(i), Some(val(i + 10_000)));
4516            }
4517            let a = a_batch
4518                .merkleize(&db, None, &mut Proportional)
4519                .await
4520                .unwrap();
4521            let db = db.apply_batch(Arc::clone(&a)).await.unwrap();
4522            let db = db.commit().await.unwrap();
4523
4524            // Prune while `a` is still held. Mutates the shared bitmap's pruning boundary in place.
4525            let boundary = db.sync_boundary();
4526            let db = db.prune(boundary).await.unwrap();
4527
4528            // Extend `a` into `b` AFTER the prune. Building `b` off `a` triggers
4529            // `trim_committed` on `a`'s chain, which must correctly see the advanced pruning
4530            // boundary on the shared bitmap.
4531            let b = a
4532                .new_batch::<Sha256>()
4533                .write(key(300), Some(val(300)))
4534                .merkleize(&db, None, &mut Proportional)
4535                .await
4536                .unwrap();
4537
4538            let db = db.apply_batch(b).await.unwrap();
4539            assert_eq!(db.get(&key(0)).await.unwrap(), Some(val(10_000)));
4540            assert_eq!(db.get(&key(249)).await.unwrap(), Some(val(10_249)));
4541            assert_eq!(db.get(&key(300)).await.unwrap(), Some(val(300)));
4542
4543            db.destroy().await.unwrap();
4544        });
4545    }
4546
4547    /// Regression: applying a batch after its ancestor Arc is dropped (without
4548    /// committing) must still apply the ancestor's bitmap pushes/clears and
4549    /// snapshot diffs.
4550    #[test_traced("WARN")]
4551    fn test_current_apply_after_ancestor_dropped() {
4552        let executor = deterministic::Runner::default();
4553        executor.start(|context| async move {
4554            let ctx = context.child("db");
4555            let db: UnorderedVariableDb = UnorderedVariableDb::init(
4556                ctx.child("storage"),
4557                variable_config::<OneCap>("adrop"),
4558                None,
4559            )
4560            .await
4561            .unwrap();
4562
4563            // Chain: DB <- A <- B <- C
4564            let mut a = db.new_batch();
4565            for i in 0..3 {
4566                a = a.write(key(i), Some(val(i)));
4567            }
4568            let a_m = a.merkleize(&db, None, &mut Proportional).await.unwrap();
4569
4570            let mut b = a_m.new_batch::<Sha256>();
4571            for i in 3..6 {
4572                b = b.write(key(i), Some(val(i)));
4573            }
4574            let b_m = b.merkleize(&db, None, &mut Proportional).await.unwrap();
4575
4576            let mut c = b_m.new_batch::<Sha256>();
4577            for i in 6..9 {
4578                c = c.write(key(i), Some(val(i)));
4579            }
4580            let c_m = c.merkleize(&db, None, &mut Proportional).await.unwrap();
4581
4582            // Drop A and B without committing. Their Weak refs in C are now dead.
4583            drop(a_m);
4584            drop(b_m);
4585
4586            // Apply only the tip. This is !skip_ancestors (DB hasn't changed).
4587            let db = db.apply_batch(c_m).await.unwrap().commit().await.unwrap();
4588
4589            // All nine keys must be accessible.
4590            for i in 0..9 {
4591                assert_eq!(
4592                    db.get(&key(i)).await.unwrap(),
4593                    Some(val(i)),
4594                    "key({i}) missing after apply_batch with dropped ancestors"
4595                );
4596            }
4597
4598            db.destroy().await.unwrap();
4599        });
4600    }
4601
4602    /// Regression: applying a 3-deep chain as a single batch must leave the
4603    /// bitmap in the same state as applying the same operations sequentially.
4604    /// This fails if ancestor bitmap pushes are concatenated in the wrong order
4605    /// (tip-to-root instead of root-to-tip), because Delete operations produce
4606    /// false bitmap bits, and wrong ordering puts the false at the wrong
4607    /// position. We detect this by building a NEW batch on top of the
4608    /// (possibly corrupted) bitmap and comparing its root against the
4609    /// sequential path.
4610    #[test_traced("WARN")]
4611    fn test_current_chain_bitmap_order_matches_sequential() {
4612        let executor = deterministic::Runner::default();
4613        executor.start(|context| async move {
4614            let ctx1 = context.child("db").with_attribute("index", 1);
4615            let db1: UnorderedVariableDb = UnorderedVariableDb::init(
4616                ctx1.child("db"),
4617                variable_config::<OneCap>("ord1"),
4618                None,
4619            )
4620            .await
4621            .unwrap();
4622
4623            // Seed some committed data so there's a base bitmap to clear.
4624            let db1 = commit_writes_with_metadata(
4625                db1,
4626                [(key(10), Some(val(10))), (key(11), Some(val(11)))],
4627                None,
4628            )
4629            .await;
4630
4631            // Chain: DB <- A <- B <- C
4632            // A: updates key(10) and DELETES key(11). The delete produces a
4633            //    false bitmap bit. If A's bits end up at B's positions (wrong
4634            //    order), the false bit lands at the wrong journal location.
4635            // B: updates key(12) and key(13). All true bits.
4636            // C: updates key(14). All true bits.
4637            let a = db1
4638                .new_batch()
4639                .write(key(10), Some(val(100)))
4640                .write(key(11), None) // DELETE
4641                .merkleize(&db1, None, &mut Proportional)
4642                .await
4643                .unwrap();
4644
4645            let b = a
4646                .new_batch::<Sha256>()
4647                .write(key(12), Some(val(120)))
4648                .write(key(13), Some(val(130)))
4649                .merkleize(&db1, None, &mut Proportional)
4650                .await
4651                .unwrap();
4652
4653            let c = b
4654                .new_batch::<Sha256>()
4655                .write(key(14), Some(val(140)))
4656                .merkleize(&db1, None, &mut Proportional)
4657                .await
4658                .unwrap();
4659
4660            let db1 = db1.apply_batch(c).await.unwrap().commit().await.unwrap();
4661
4662            // Build one more batch on top to exercise the bitmap state.
4663            let d1 = db1
4664                .new_batch()
4665                .write(key(20), Some(val(200)))
4666                .merkleize(&db1, None, &mut Proportional)
4667                .await
4668                .unwrap();
4669            let chain_then_d_root = d1.root();
4670
4671            let ctx2 = context.child("db").with_attribute("index", 2);
4672            let db2: UnorderedVariableDb = UnorderedVariableDb::init(
4673                ctx2.child("db"),
4674                variable_config::<OneCap>("ord2"),
4675                None,
4676            )
4677            .await
4678            .unwrap();
4679
4680            let db2 = commit_writes_with_metadata(
4681                db2,
4682                [(key(10), Some(val(10))), (key(11), Some(val(11)))],
4683                None,
4684            )
4685            .await;
4686
4687            let a2 = db2
4688                .new_batch()
4689                .write(key(10), Some(val(100)))
4690                .write(key(11), None)
4691                .merkleize(&db2, None, &mut Proportional)
4692                .await
4693                .unwrap();
4694            let db2 = db2.apply_batch(a2).await.unwrap().commit().await.unwrap();
4695
4696            let b2 = db2
4697                .new_batch()
4698                .write(key(12), Some(val(120)))
4699                .write(key(13), Some(val(130)))
4700                .merkleize(&db2, None, &mut Proportional)
4701                .await
4702                .unwrap();
4703            let db2 = db2.apply_batch(b2).await.unwrap().commit().await.unwrap();
4704
4705            let c2 = db2
4706                .new_batch()
4707                .write(key(14), Some(val(140)))
4708                .merkleize(&db2, None, &mut Proportional)
4709                .await
4710                .unwrap();
4711            let db2 = db2.apply_batch(c2).await.unwrap().commit().await.unwrap();
4712
4713            let d2 = db2
4714                .new_batch()
4715                .write(key(20), Some(val(200)))
4716                .merkleize(&db2, None, &mut Proportional)
4717                .await
4718                .unwrap();
4719            let sequential_then_d_root = d2.root();
4720
4721            assert_eq!(
4722                chain_then_d_root, sequential_then_d_root,
4723                "batch D's root on top of chain-applied state must match sequential state"
4724            );
4725
4726            db1.destroy().await.unwrap();
4727            db2.destroy().await.unwrap();
4728        });
4729    }
4730
4731    /// Regression: C's diff entry has a stale `base_old_loc` (255) pointing into a chunk that
4732    /// was pruned after parent P was committed. Applied-ancestor diff precedence in
4733    /// `any::Db::apply_batch` must override the stale value with P's rewrite location, so the
4734    /// `set_bit(false)` call targets P's post-walk loc, not the pruned chunk.
4735    ///
4736    /// With N=32, CHUNK_SIZE_BITS=256. Seed places key(0) at loc 255 (end of chunk 0). P
4737    /// overwrites keys 1..254. P's floor walk moves key(0) from 255 to a fresh loc above 255.
4738    /// C is built from P and writes key(0) again. After committing P and pruning chunk 0, C's
4739    /// pre-merkleize `base_old_loc=255` is no longer the right clear target. P's applied diff
4740    /// supplies its rewrite loc instead. If that precedence path broke, apply would panic
4741    /// (`set_bit` on a pruned bit).
4742    #[test_traced("WARN")]
4743    fn test_current_stale_bitmap_clears_after_prune() {
4744        let executor = deterministic::Runner::default();
4745        executor.start(|context| async move {
4746            let ctx = context.child("db");
4747            let db: UnorderedVariableDb = UnorderedVariableDb::init(
4748                ctx.child("storage"),
4749                variable_config::<OneCap>("stale-clears"),
4750                None,
4751            )
4752            .await
4753            .unwrap();
4754
4755            // Seed: 255 keys in one batch. key(0) lands at loc 255 (chunk 0).
4756            let mut seed = db.new_batch();
4757            for i in 0u64..255 {
4758                seed = seed.write(key(i), Some(val(i)));
4759            }
4760            let seed_m = seed.merkleize(&db, None, &mut Proportional).await.unwrap();
4761            let db = db.apply_batch(seed_m).await.unwrap();
4762            let db = db.commit().await.unwrap();
4763
4764            // P: overwrite keys 1..254. Does NOT touch key(0), but P's floor
4765            // walk moves key(0) from 255, advancing the floor past chunk 0.
4766            let mut p = db.new_batch();
4767            for i in 1u64..255 {
4768                p = p.write(key(i), Some(val(i + 10000)));
4769            }
4770            let p_m = p.merkleize(&db, None, &mut Proportional).await.unwrap();
4771
4772            // C: built from P. Writes key(0). base_old_loc = 255 (chunk 0).
4773            let c_m = p_m
4774                .new_batch::<Sha256>()
4775                .write(key(0), Some(val(9999)))
4776                .merkleize(&db, None, &mut Proportional)
4777                .await
4778                .unwrap();
4779
4780            // Commit P, prune chunk 0, then apply C.
4781            let db = db.apply_batch(p_m).await.unwrap().commit().await.unwrap();
4782
4783            let floor = *db.inactivity_floor_loc();
4784            assert!(floor >= 256, "floor must be past chunk 0: floor={floor}",);
4785
4786            let boundary = db.sync_boundary();
4787            let db = db.prune(boundary).await.unwrap();
4788            let db = db.apply_batch(c_m).await.unwrap();
4789
4790            db.destroy().await.unwrap();
4791        });
4792    }
4793
4794    /// Apply C (grandchild of A) after only A is applied. B's data (any-layer
4795    /// snapshot diff + current-layer bitmap) must still be applied.
4796    #[test_traced("INFO")]
4797    fn test_current_partial_ancestor_commit() {
4798        let executor = deterministic::Runner::default();
4799        executor.start(|context| async move {
4800            let ctx = context.child("db");
4801            let db: UnorderedVariableDb = UnorderedVariableDb::init(
4802                ctx.child("storage"),
4803                variable_config::<OneCap>("pac"),
4804                None,
4805            )
4806            .await
4807            .unwrap();
4808
4809            let a = db
4810                .new_batch()
4811                .write(key(0), Some(val(0)))
4812                .merkleize(&db, None, &mut Proportional)
4813                .await
4814                .unwrap();
4815            let b = a
4816                .new_batch::<Sha256>()
4817                .write(key(1), Some(val(1)))
4818                .merkleize(&db, None, &mut Proportional)
4819                .await
4820                .unwrap();
4821            let c = b
4822                .new_batch::<Sha256>()
4823                .write(key(2), Some(val(2)))
4824                .merkleize(&db, None, &mut Proportional)
4825                .await
4826                .unwrap();
4827
4828            let expected_root = c.root();
4829
4830            let db = db.apply_batch(a).await.unwrap();
4831            let db = db.apply_batch(c).await.unwrap();
4832
4833            assert_eq!(db.root(), expected_root);
4834            assert_eq!(db.get(&key(0)).await.unwrap(), Some(val(0)));
4835            assert_eq!(db.get(&key(1)).await.unwrap(), Some(val(1)));
4836            assert_eq!(db.get(&key(2)).await.unwrap(), Some(val(2)));
4837
4838            db.destroy().await.unwrap();
4839        });
4840    }
4841
4842    /// Regression: bitmap ancestor skip logic must correctly pair each ancestor's
4843    /// bitmap data with its batch_end. Requires a 3-ancestor chain (A->B->C->D)
4844    /// to expose ordering bugs.
4845    #[test_traced("INFO")]
4846    fn test_current_partial_ancestor_bitmap_ordering() {
4847        let executor = deterministic::Runner::default();
4848        executor.start(|context| async move {
4849            let ctx = context.child("db");
4850            let db: UnorderedVariableDb = UnorderedVariableDb::init(
4851                ctx.child("storage"),
4852                variable_config::<OneCap>("bmo"),
4853                None,
4854            )
4855            .await
4856            .unwrap();
4857
4858            // Build A -> B -> C -> D. Each writes a distinct key.
4859            let a = db
4860                .new_batch()
4861                .write(key(0), Some(val(0)))
4862                .merkleize(&db, None, &mut Proportional)
4863                .await
4864                .unwrap();
4865            let b = a
4866                .new_batch::<Sha256>()
4867                .write(key(1), Some(val(1)))
4868                .merkleize(&db, None, &mut Proportional)
4869                .await
4870                .unwrap();
4871            let c = b
4872                .new_batch::<Sha256>()
4873                .write(key(2), Some(val(2)))
4874                .merkleize(&db, None, &mut Proportional)
4875                .await
4876                .unwrap();
4877            let d = c
4878                .new_batch::<Sha256>()
4879                .write(key(3), Some(val(3)))
4880                .merkleize(&db, None, &mut Proportional)
4881                .await
4882                .unwrap();
4883
4884            // Apply A only, then apply D (B and C unapplied).
4885            // D has 3 ancestors: [C, B, A] (parent-first) with batch_ends [C.total, B.total, A.total].
4886            // Bitmap ancestors are also parent-first: [C, B, A].
4887            let db = db.apply_batch(a).await.unwrap();
4888            let db = db.apply_batch(d.clone()).await.unwrap();
4889
4890            // Build a new batch E on top of the current state. If the bitmap was
4891            // corrupted by the ordering bug (A's pushes duplicated or B/C's pushes
4892            // missing), merkleize will compute a different root than a reference
4893            // that applied all ancestors sequentially.
4894            let e = db
4895                .new_batch()
4896                .write(key(4), Some(val(4)))
4897                .merkleize(&db, None, &mut Proportional)
4898                .await
4899                .unwrap();
4900            let db = db.apply_batch(e).await.unwrap();
4901
4902            // Reference: apply all five sequentially.
4903            let ref_ctx = context.child("ref");
4904            let mut ref_db: UnorderedVariableDb = UnorderedVariableDb::init(
4905                ref_ctx.child("db"),
4906                variable_config::<OneCap>("bmo_ref"),
4907                None,
4908            )
4909            .await
4910            .unwrap();
4911            for i in 0..5 {
4912                let batch = ref_db
4913                    .new_batch()
4914                    .write(key(i), Some(val(i)))
4915                    .merkleize(&ref_db, None, &mut Proportional)
4916                    .await
4917                    .unwrap();
4918                ref_db = ref_db.apply_batch(batch).await.unwrap();
4919            }
4920
4921            assert_eq!(
4922                db.root(),
4923                ref_db.root(),
4924                "root mismatch: bitmap ordering bug"
4925            );
4926
4927            db.destroy().await.unwrap();
4928            ref_db.destroy().await.unwrap();
4929        });
4930    }
4931
4932    /// Regression: the bitmap chunks produced by the speculative `BitmapBatch` chain during
4933    /// merkleize must equal the bytes that `any::Db::apply_batch` writes via diff-driven
4934    /// updates. `current::Db::apply_batch` relies on this equivalence to install the precomputed
4935    /// `batch.grafted` against the now-current bitmap.
4936    ///
4937    /// The workload spans multiple bitmap chunks and exercises:
4938    /// - parent/child same-key overwrite (applied-ancestor diff precedence in `apply_batch`),
4939    /// - parent-create then child-delete (unapplied-ancestor precedence),
4940    /// - mixed deletes and overwrites in different chunks (clear-bit + set-bit paths).
4941    #[test_traced("INFO")]
4942    fn test_current_apply_chunks_match_speculative_chunks() {
4943        const N: usize = 32;
4944        const CHUNK_SIZE_BITS: u64 = commonware_utils::bitmap::Prunable::<N>::CHUNK_SIZE_BITS;
4945        // Seed enough keys to cross at least one chunk boundary. Each batch also produces a
4946        // CommitFloor op, so the bitmap grows past the user-visible key count.
4947        const SEED_KEYS: u64 = CHUNK_SIZE_BITS + 50;
4948
4949        let executor = deterministic::Runner::default();
4950        executor.start(|context| async move {
4951            let ctx = context.child("db");
4952            let db: UnorderedVariableDb = UnorderedVariableDb::init(
4953                ctx.child("storage"),
4954                variable_config::<OneCap>("spec_eq"),
4955                None,
4956            )
4957            .await
4958            .unwrap();
4959
4960            // Seed all keys in one committed batch.
4961            let seed = (0..SEED_KEYS).fold(db.new_batch(), |b, i| b.write(key(i), Some(val(i))));
4962            let seed = seed.merkleize(&db, None, &mut Proportional).await.unwrap();
4963            let db = db.apply_batch(seed).await.unwrap().commit().await.unwrap();
4964
4965            // Setup sanity: the DB's bitmap spans at least two chunks.
4966            assert!(
4967                Readable::<N>::len(db.any.bitmap.as_ref()) > CHUNK_SIZE_BITS,
4968                "setup must cross a chunk boundary",
4969            );
4970
4971            // Parent (unapplied): overwrites + delete + creates spread across the bitmap.
4972            let parent = db
4973                .new_batch()
4974                .write(key(10), Some(val(110))) // overwrite (low chunk)
4975                .write(key(50), None) // delete (low chunk)
4976                .write(key(CHUNK_SIZE_BITS + 5), Some(val(120))) // overwrite (high chunk)
4977                .write(key(SEED_KEYS), Some(val(130))) // create new key
4978                .write(key(SEED_KEYS + 1), Some(val(131))) // create new key
4979                .merkleize(&db, None, &mut Proportional)
4980                .await
4981                .unwrap();
4982
4983            // Child (unapplied, descendant of parent):
4984            //   - same-key overwrite of parent's key(10)        -> applied-ancestor precedence
4985            //   - delete of parent's just-created key(SEED_KEYS) -> unapplied-create-child-delete
4986            //   - additional delete + overwrite in mixed chunks -> set-bit + clear-bit coverage
4987            let child = parent
4988                .new_batch::<Sha256>()
4989                .write(key(10), Some(val(210)))
4990                .write(key(SEED_KEYS), None)
4991                .write(key(75), None)
4992                .write(key(CHUNK_SIZE_BITS + 30), Some(val(220)))
4993                .merkleize(&db, None, &mut Proportional)
4994                .await
4995                .unwrap();
4996
4997            // Snapshot every chunk in the speculative `BitmapBatch` chain (read through child).
4998            let speculative_chunks: Vec<[u8; N]> = {
4999                let len = Readable::<N>::len(&child.bitmap);
5000                let chunk_count = len.div_ceil(CHUNK_SIZE_BITS) as usize;
5001                (0..chunk_count)
5002                    .map(|idx| Readable::<N>::get_chunk(&child.bitmap, idx))
5003                    .collect()
5004            };
5005            // Setup sanity: speculative state spans at least two chunks.
5006            assert!(speculative_chunks.len() >= 2);
5007
5008            // Apply child (applies parent + child) and re-read every chunk from the DB's
5009            // bitmap. The two views must be byte-identical; otherwise the precomputed
5010            // `batch.canonical_root` is no longer valid against the post-apply state.
5011            let db = db.apply_batch(child).await.unwrap();
5012            let committed_chunks: Vec<[u8; N]> = {
5013                let len = Readable::<N>::len(db.any.bitmap.as_ref());
5014                let chunk_count = len.div_ceil(CHUNK_SIZE_BITS) as usize;
5015                (0..chunk_count)
5016                    .map(|idx| Readable::<N>::get_chunk(db.any.bitmap.as_ref(), idx))
5017                    .collect()
5018            };
5019
5020            assert_eq!(
5021                speculative_chunks, committed_chunks,
5022                "speculative chunks must equal post-apply committed chunks across all chunks",
5023            );
5024
5025            db.destroy().await.unwrap();
5026        });
5027    }
5028
5029    /// Regression: `ops_historical_proof` must verify with QMDB's ops-tree hasher configuration.
5030    #[test_traced("INFO")]
5031    fn test_current_mmb_ops_historical_proof_verifies_with_backward_bagging() {
5032        let executor = deterministic::Runner::default();
5033        executor.start(|context| async move {
5034            let ctx = context.child("db");
5035            let db: UnorderedFixedMmbDb = UnorderedFixedMmbDb::init(
5036                ctx.child("storage"),
5037                fixed_config::<OneCap>("mmb-ops-proof"),
5038                None,
5039            )
5040            .await
5041            .unwrap();
5042
5043            // Apply a batch and commit so an ops historical proof exists.
5044            let writes: Vec<(Digest, Option<Digest>)> =
5045                (0u64..16).map(|i| (key(i), Some(val(i)))).collect();
5046            let db = commit_writes(db, writes).await.unwrap();
5047
5048            let ops_root = db.ops_root();
5049            let historical_size = db.bounds().end;
5050            let (proof, ops) = db
5051                .ops_historical_proof(historical_size, Location::new(0), NZU64!(32))
5052                .await
5053                .unwrap();
5054
5055            // Verifies under the QMDB ops-tree hasher configuration.
5056            assert!(verify_proof::<Sha256, _, _>(
5057                &proof,
5058                Location::new(0),
5059                &ops,
5060                &ops_root
5061            ));
5062
5063            db.destroy().await.unwrap();
5064        });
5065    }
5066
5067    /// A child policy over an unapplied parent replaces and evicts applied updates and passes the
5068    /// update the parent superseded. The applied chain proves the results.
5069    #[test_traced("INFO")]
5070    fn test_current_ordered_policy_replace_and_ancestor_proofs() {
5071        deterministic::Runner::default().start(|context| async move {
5072            let ctx = context.child("db");
5073            let partition = "current-ordered-policy-ancestor";
5074            let db: OrderedFixedDb = OrderedFixedDb::init(
5075                ctx.child("storage"),
5076                fixed_config::<OneCap>(partition),
5077                None,
5078            )
5079            .await
5080            .unwrap();
5081
5082            // Seed three keys in key order with a held floor.
5083            let mut keys = [key(1), key(2), key(3)];
5084            keys.sort();
5085            let seed = keys
5086                .into_iter()
5087                .enumerate()
5088                .fold(db.new_batch(), |batch, (i, key)| {
5089                    batch.write(key, Some(val(i as u64)))
5090                });
5091            let seed = seed.merkleize(&db, None, &mut Hold).await.unwrap();
5092            let db = db.apply_batch(seed).await.unwrap().commit().await.unwrap();
5093
5094            // An unapplied ancestor supersedes the middle base operation.
5095            let parent = db
5096                .new_batch()
5097                .write(keys[1], Some(val(11)))
5098                .merkleize(&db, None, &mut Hold)
5099                .await
5100                .unwrap();
5101
5102            // The child passes the initial commit, replaces the oldest key, passes the update the
5103            // parent superseded, and evicts the last base key. The parent's update lies past the
5104            // remaining skips.
5105            let oldest = keys[0];
5106            let mut policy = Script::new(usize::MAX, 2, move |key: &Digest| {
5107                if *key == oldest {
5108                    Choice::Replace(val(10))
5109                } else {
5110                    Choice::Evict
5111                }
5112            });
5113            let child = parent
5114                .new_batch::<Sha256>()
5115                .merkleize(&db, None, &mut policy)
5116                .await
5117                .unwrap();
5118            let first = policy.visited[0].0;
5119            assert_eq!(
5120                policy.visited,
5121                [
5122                    (first, keys[0], val(0)),
5123                    (Location::new(*first + 2), keys[2], val(2)),
5124                ]
5125            );
5126            assert_eq!(child.bounds().inactivity_floor, Location::new(*first + 3));
5127
5128            // The applied chain matches the speculative root, proves the active keys and their
5129            // links, and proves the evicted key excluded.
5130            let speculative_root = child.root();
5131            let db = db.apply_batch(parent).await.unwrap();
5132            let db = db.apply_batch(child).await.unwrap();
5133            let root = db.root();
5134            assert_eq!(root, speculative_root);
5135            let proof = db.key_value_proof(keys[0]).await.unwrap();
5136            assert_eq!(proof.next_key, keys[1]);
5137            assert!(
5138                proof.verify::<Sha256, crate::qmdb::any::value::FixedEncoding<Digest>>(
5139                    keys[0],
5140                    val(10),
5141                    &root
5142                )
5143            );
5144            let proof = db.key_value_proof(keys[1]).await.unwrap();
5145            assert_eq!(proof.next_key, keys[0]);
5146            assert!(
5147                proof.verify::<Sha256, crate::qmdb::any::value::FixedEncoding<Digest>>(
5148                    keys[1],
5149                    val(11),
5150                    &root
5151                )
5152            );
5153            let exclusion = db.exclusion_proof(&keys[2]).await.unwrap();
5154            assert!(exclusion.verify::<Sha256>(&keys[2], &root));
5155
5156            // The state survives reopen.
5157            let db = db.sync().await.unwrap();
5158            assert_eq!(db.root(), root);
5159            drop(db);
5160            let reopened: OrderedFixedDb =
5161                OrderedFixedDb::init(ctx.child("reopen"), fixed_config::<OneCap>(partition), None)
5162                    .await
5163                    .unwrap();
5164            assert_eq!(reopened.root(), root);
5165            assert!(
5166                reopened
5167                    .exclusion_proof(&keys[2])
5168                    .await
5169                    .unwrap()
5170                    .verify::<Sha256>(&keys[2], &root)
5171            );
5172            reopened.destroy().await.unwrap();
5173        });
5174    }
5175
5176    /// Evicting the only key empties an ordered database. The empty database proves the key's
5177    /// exclusion through the commit.
5178    #[test_traced("INFO")]
5179    fn test_current_ordered_policy_to_empty_proves_exclusion() {
5180        deterministic::Runner::default().start(|context| async move {
5181            let ctx = context.child("db");
5182            let db: OrderedFixedDb = OrderedFixedDb::init(
5183                ctx.child("storage"),
5184                fixed_config::<OneCap>("current-ordered-policy-empty"),
5185                None,
5186            )
5187            .await
5188            .unwrap();
5189
5190            // Seed one key.
5191            let k = key(7);
5192            let seed = db
5193                .new_batch()
5194                .write(k, Some(val(7)))
5195                .merkleize(&db, None, &mut Proportional)
5196                .await
5197                .unwrap();
5198            let db = db.apply_batch(seed).await.unwrap();
5199            assert!(!db.is_empty());
5200
5201            // Evict the only key.
5202            let mut policy = Script::new(usize::MAX, u64::MAX, |_: &Digest| Choice::Evict);
5203            let batch = db
5204                .new_batch()
5205                .merkleize(&db, None, &mut policy)
5206                .await
5207                .unwrap();
5208            assert_eq!(policy.visited.len(), 1);
5209            assert_eq!((policy.visited[0].1, policy.visited[0].2), (k, val(7)));
5210
5211            // The empty database proves exclusion through the commit.
5212            let db = db.apply_batch(batch).await.unwrap();
5213            assert_eq!(db.get(&k).await.unwrap(), None);
5214            assert!(db.is_empty());
5215            let proof = db.exclusion_proof(&k).await.unwrap();
5216            assert!(matches!(
5217                proof,
5218                ordered::proof::constant::ExclusionProof::Commit(..)
5219            ));
5220            assert!(proof.verify::<Sha256>(&k, &db.root()));
5221            db.destroy().await.unwrap();
5222        });
5223    }
5224
5225    /// The speculative bitmap lets a child policy pass applied updates that an unapplied parent
5226    /// superseded without reading them.
5227    #[test_traced("INFO")]
5228    fn test_current_policy_skips_updates_superseded_by_pending_parent() {
5229        deterministic::Runner::default().start(|context| async move {
5230            let ctx = context.child("db");
5231            let db: UnorderedFixedDb = UnorderedFixedDb::init(
5232                ctx.child("storage"),
5233                fixed_config::<OneCap>("current-policy-speculative-skip"),
5234                None,
5235            )
5236            .await
5237            .unwrap();
5238
5239            // Commit ten keys in key order after the initial commit.
5240            let mut keys: Vec<_> = (40..50).map(key).collect();
5241            keys.sort();
5242            let seed = keys
5243                .iter()
5244                .enumerate()
5245                .fold(db.new_batch(), |batch, (i, key)| {
5246                    batch.write(*key, Some(val(i as u64)))
5247                })
5248                .merkleize(&db, None, &mut Hold)
5249                .await
5250                .unwrap();
5251            let db = db.apply_batch(seed).await.unwrap().commit().await.unwrap();
5252
5253            // An unapplied parent supersedes every applied key except the last.
5254            let parent = keys[..9]
5255                .iter()
5256                .fold(db.new_batch(), |batch, key| {
5257                    batch.write(*key, Some(val(100)))
5258                })
5259                .merkleize(&db, None, &mut Hold)
5260                .await
5261                .unwrap();
5262
5263            // The child's policy reads only the remaining applied update before deciding it.
5264            let items_read = || counter(&context, "log_journal_items_read_total");
5265            let before = items_read();
5266            let mut reads = Vec::new();
5267            let mut policy = Script::new(1, u64::MAX, |_: &Digest| {
5268                reads.push(items_read());
5269                Choice::Keep
5270            });
5271            let child = parent
5272                .new_batch::<Sha256>()
5273                .merkleize(&db, None, &mut policy)
5274                .await
5275                .unwrap();
5276            assert_eq!(policy.visited.len(), 1);
5277            assert_eq!(policy.visited[0].1, keys[9]);
5278            assert_eq!(reads, [before + 1]);
5279
5280            drop((child, parent));
5281            db.destroy().await.unwrap();
5282        });
5283    }
5284
5285    /// A staged proportional walk over an unapplied parent continues into the parent's operations
5286    /// over a pruned bitmap when the applied candidates run out before its entries do. The staged
5287    /// batch matches the unstaged one and a twin merkleized after the parent is applied.
5288    #[test_traced("INFO")]
5289    fn test_current_staged_walk_continues_past_applied_candidates() {
5290        deterministic::Runner::default().start(|context| async move {
5291            let ctx = context.child("db");
5292            let mut db: UnorderedFixedDb = UnorderedFixedDb::init(
5293                ctx.child("storage"),
5294                fixed_config::<OneCap>("current-staged-walk"),
5295                None,
5296            )
5297            .await
5298            .unwrap();
5299
5300            // Rewrite one key until the floor passes a bitmap chunk, then prune the chunks below
5301            // it. Each rewrite's walk moves the written update, so the floor ends at the moved
5302            // update.
5303            let rounds = CHUNK_BITS / 2;
5304            for i in 0..rounds {
5305                let batch = db
5306                    .new_batch()
5307                    .write(key(0), Some(val(i)))
5308                    .merkleize(&db, None, &mut Proportional)
5309                    .await
5310                    .unwrap();
5311                db = db.apply_batch(batch).await.unwrap();
5312            }
5313            let db = db.commit().await.unwrap();
5314            let floor = db.inactivity_floor_loc();
5315            let db = db
5316                .prune(Location::new(*floor / CHUNK_BITS * CHUNK_BITS))
5317                .await
5318                .unwrap();
5319            assert!(db.pruned_bits() > 0);
5320
5321            // Seed four more keys in key order with a held floor, and rewrite them all in an
5322            // unapplied parent, so the only applied update the parent leaves active is the
5323            // rewritten key's.
5324            let mut keys: Vec<_> = (1..5).map(key).collect();
5325            keys.sort();
5326            let seed = keys
5327                .iter()
5328                .fold(db.new_batch(), |batch, k| batch.write(*k, Some(val(1))))
5329                .merkleize(&db, None, &mut Hold)
5330                .await
5331                .unwrap();
5332            let db = db.apply_batch(seed).await.unwrap();
5333            let parent = keys
5334                .iter()
5335                .fold(db.new_batch(), |batch, k| batch.write(*k, Some(val(2))))
5336                .merkleize(&db, None, &mut Hold)
5337                .await
5338                .unwrap();
5339
5340            // A child that stages and rewrites the parent's first key has two entries, one for
5341            // the update it supersedes and one for the previous commit. The walk spends them on
5342            // the applied update at the floor and the parent's update of the second key.
5343            let (_, staged) = parent
5344                .new_batch::<Sha256>()
5345                .stage(&[&keys[0]], &db)
5346                .await
5347                .unwrap();
5348            let staged = staged
5349                .merkleize(
5350                    vec![Slot::Put(val(3))],
5351                    Vec::new(),
5352                    None,
5353                    &db,
5354                    &mut Proportional,
5355                )
5356                .await
5357                .unwrap();
5358            let direct = parent
5359                .new_batch::<Sha256>()
5360                .write(keys[0], Some(val(3)))
5361                .merkleize(&db, None, &mut Proportional)
5362                .await
5363                .unwrap();
5364            assert_eq!(staged.root(), direct.root());
5365            let (_, operations) = staged.operations();
5366            assert_eq!(
5367                operations[1..3],
5368                [
5369                    Operation::Update(update::Unordered(key(0), val(rounds - 1))),
5370                    Operation::Update(update::Unordered(keys[1], val(2))),
5371                ]
5372            );
5373
5374            // A twin merkleized after the parent is applied produces the same root.
5375            let db = db.apply_batch(parent).await.unwrap();
5376            let twin = db
5377                .new_batch()
5378                .write(keys[0], Some(val(3)))
5379                .merkleize(&db, None, &mut Proportional)
5380                .await
5381                .unwrap();
5382            assert_eq!(twin.root(), staged.root());
5383            drop((staged, direct, twin));
5384            db.destroy().await.unwrap();
5385        });
5386    }
5387
5388    /// Proof checks for one current DB kind.
5389    trait Proves {
5390        /// Assert that each key in `live` proves its value against the canonical root, and that
5391        /// each key in `evicted` is absent.
5392        async fn assert_proves(&self, live: &BTreeMap<Digest, Digest>, evicted: &[Digest]);
5393    }
5394
5395    /// Implement [`Proves`] for unordered current DB kinds.
5396    macro_rules! unordered_proves {
5397        ($($db:ty),+) => {$(
5398            impl Proves for $db {
5399                async fn assert_proves(&self, live: &BTreeMap<Digest, Digest>, evicted: &[Digest]) {
5400                    let root = self.root();
5401                    for (key, value) in live {
5402                        let proof = self.key_value_proof(*key).await.unwrap();
5403                        assert!(
5404                            Self::verify_key_value_proof(*key, *value, &proof, &root),
5405                            "{key} fails to prove its value",
5406                        );
5407                    }
5408                    for key in evicted {
5409                        assert_eq!(self.get(key).await.unwrap(), None, "{key} is live");
5410                        assert!(matches!(
5411                            self.key_value_proof(*key).await,
5412                            Err(Error::KeyNotFound)
5413                        ));
5414                    }
5415                }
5416            }
5417        )+};
5418    }
5419
5420    /// Implement [`Proves`] for ordered current DB kinds. Each active key also proves its link to
5421    /// the next active key in key order, and each evicted key proves its exclusion.
5422    macro_rules! ordered_proves {
5423        ($($db:ty),+) => {$(
5424            impl Proves for $db {
5425                async fn assert_proves(&self, live: &BTreeMap<Digest, Digest>, evicted: &[Digest]) {
5426                    let keys: Vec<_> = live.keys().copied().collect();
5427                    for (i, (key, value)) in live.iter().enumerate() {
5428                        self.assert_link(*key, *value, keys[(i + 1) % keys.len()])
5429                            .await;
5430                    }
5431                    for key in evicted {
5432                        self.assert_absent(*key).await;
5433                    }
5434                }
5435            }
5436        )+};
5437    }
5438
5439    unordered_proves!(UnorderedFixedDb, UnorderedFixedMmbDb);
5440    ordered_proves!(OrderedFixedDb, OrderedFixedMmbDb);
5441
5442    /// Instantiate the policy consistency test for one current DB kind.
5443    ///
5444    /// A child over an unapplied parent evicts an applied update and an update of the parent,
5445    /// replaces another update of the parent, and keeps, replaces, and evicts its own writes,
5446    /// including a key it creates. Its operations complete the bitmap's first chunk. The layered
5447    /// bitmap before apply and the database's bitmap after apply mark exactly the final active
5448    /// updates and the commit, active keys prove against the canonical root, evicted keys are
5449    /// absent, and the root survives reopen. A twin merkleized after the parent applies makes the
5450    /// same decisions, operations, and root.
5451    macro_rules! policy_consistency_test {
5452        ($name:ident, $db:ty) => {
5453            #[test_traced("WARN")]
5454            fn $name() {
5455                deterministic::Runner::default().start(|context| async move {
5456                    let ctx = context.child("db");
5457                    let partition = stringify!($name);
5458                    let mut db: $db = <$db>::init(
5459                        ctx.child("storage"),
5460                        fixed_config::<OneCap>(partition),
5461                        None,
5462                    )
5463                    .await
5464                    .unwrap();
5465
5466                    // Seed seven policy keys and ten fillers, then rewrite the fillers with a
5467                    // held floor until the log ends 6 locations short of the first chunk's end.
5468                    let keys: Vec<Digest> = (0..7).map(key).collect();
5469                    let created = [key(7), key(8)];
5470                    let fillers: Vec<Digest> = (100..110).map(key).collect();
5471                    let mut values = BTreeMap::new();
5472                    let mut round = 0;
5473                    while *db.size() + 11 <= CHUNK_BITS - 6 {
5474                        let writes: Vec<Digest> = if round == 0 {
5475                            keys.iter().chain(&fillers).copied().collect()
5476                        } else {
5477                            fillers.clone()
5478                        };
5479                        let batch = writes
5480                            .iter()
5481                            .fold(db.new_batch(), |batch, key| {
5482                                batch.write(*key, Some(val(round)))
5483                            })
5484                            .merkleize(&db, None, &mut Hold)
5485                            .await
5486                            .unwrap();
5487                        db = db.apply_batch(batch).await.unwrap();
5488                        for key in writes {
5489                            values.insert(key, val(round));
5490                        }
5491                        round += 1;
5492                    }
5493                    assert_eq!(*db.size(), CHUNK_BITS - 6);
5494
5495                    // An unapplied parent rewrites two policy keys with a held floor.
5496                    let parent_writes = [(keys[2], val(1002)), (keys[3], val(1003))];
5497                    let parent = parent_writes
5498                        .iter()
5499                        .fold(db.new_batch(), |batch, (key, value)| {
5500                            batch.write(*key, Some(*value))
5501                        })
5502                        .merkleize(&db, None, &mut Hold)
5503                        .await
5504                        .unwrap();
5505                    values.extend(parent_writes);
5506
5507                    // The child rewrites three policy keys and creates two.
5508                    let child_writes = [
5509                        (keys[4], val(2004)),
5510                        (keys[5], val(2005)),
5511                        (keys[6], val(2006)),
5512                        (created[0], val(2007)),
5513                        (created[1], val(2008)),
5514                    ];
5515                    let write = |batch: <$db as crate::qmdb::any::traits::BatchableDb>::Batch| {
5516                        child_writes
5517                            .iter()
5518                            .fold(batch, |batch, (key, value)| batch.write(*key, Some(*value)))
5519                    };
5520                    values.extend(child_writes);
5521
5522                    // The policy evicts an applied update, an update of the parent, a rewrite of
5523                    // its own, and a key it creates. It replaces an update of the parent and a
5524                    // rewrite of its own, and keeps every other update.
5525                    let evict = [keys[0], keys[2], keys[6], created[0]];
5526                    let replace = [(keys[3], val(3003)), (keys[5], val(3005))];
5527                    let choose = move |key: &Digest| {
5528                        if evict.contains(key) {
5529                            Choice::Evict
5530                        } else if let Some((_, value)) = replace.iter().find(|(k, _)| k == key) {
5531                            Choice::Replace(*value)
5532                        } else {
5533                            Choice::Keep
5534                        }
5535                    };
5536
5537                    // A held twin locates the child's writes. The policy decides every update
5538                    // active after them, in location order, and the floor reaches the tip of the
5539                    // writes, past the first chunk's end.
5540                    let mut live = db.live().await;
5541                    let (start, ops) = parent.operations();
5542                    replay(&mut live, start, &ops);
5543                    let held = write(parent.new_batch::<Sha256>())
5544                        .merkleize(&db, None, &mut Hold)
5545                        .await
5546                        .unwrap();
5547                    let (start, ops) = held.operations();
5548                    let tip = start + (ops.len() as u64 - 1);
5549                    assert!(*parent.bounds().tip.size < CHUNK_BITS && CHUNK_BITS < *tip);
5550                    let mut written = live.clone();
5551                    replay(&mut written, start, &ops);
5552                    let mut expected: Vec<_> = written
5553                        .iter()
5554                        .map(|(key, loc)| (*loc, *key, values[key]))
5555                        .collect();
5556                    expected.sort();
5557                    drop(held);
5558                    let mut policy = Script::new(usize::MAX, u64::MAX, choose);
5559                    let child = write(parent.new_batch::<Sha256>())
5560                        .merkleize(&db, None, &mut policy)
5561                        .await
5562                        .unwrap();
5563                    assert_eq!(policy.visited, expected);
5564                    assert_eq!(child.bounds().inactivity_floor, tip);
5565
5566                    // Before apply, the layered bitmap marks exactly the final active updates and
5567                    // the commit, and the batch serves the final values.
5568                    let mut model = values.clone();
5569                    for key in &evict {
5570                        model.remove(key);
5571                    }
5572                    model.extend(replace);
5573                    let (start, ops) = child.operations();
5574                    replay(&mut live, start, &ops);
5575                    assert!(live.keys().eq(model.keys()));
5576                    assert_eq!(Readable::<32>::len(&child.bitmap), *child.bounds().tip.size);
5577                    assert_bits(&child.bitmap, &live);
5578                    for key in values.keys() {
5579                        assert_eq!(db.read(&child, key).await, model.get(key).copied());
5580                    }
5581
5582                    // A twin merkleized after the parent applies makes the same decisions,
5583                    // operations, floor, and root.
5584                    let root = child.root();
5585                    let db = db.apply_batch(parent).await.unwrap();
5586                    let mut twin_policy = Script::new(usize::MAX, u64::MAX, choose);
5587                    let twin = write(db.new_batch())
5588                        .merkleize(&db, None, &mut twin_policy)
5589                        .await
5590                        .unwrap();
5591                    assert_eq!(twin_policy.visited, policy.visited);
5592                    assert_eq!(twin.operations(), child.operations());
5593                    assert_eq!(twin.bounds().inactivity_floor, tip);
5594                    assert_eq!(twin.root(), root);
5595                    assert_bits(&twin.bitmap, &live);
5596                    drop(twin);
5597
5598                    // After apply, the database's bitmap matches the same state, active keys prove
5599                    // against the canonical root, and evicted keys are absent.
5600                    let db = db.apply_batch(child).await.unwrap();
5601                    assert_eq!(db.root(), root);
5602                    assert_eq!(db.inactivity_floor_loc(), tip);
5603                    assert_eq!(db.live().await, live);
5604                    assert_bits(db.bitmap(), &live);
5605                    db.assert_exact().await;
5606                    db.assert_proves(&model, &evict).await;
5607
5608                    // The canonical root and the proofs survive reopen.
5609                    let db = db.sync().await.unwrap();
5610                    drop(db);
5611                    let db: $db =
5612                        <$db>::init(ctx.child("reopen"), fixed_config::<OneCap>(partition), None)
5613                            .await
5614                            .unwrap();
5615                    assert_eq!(db.root(), root);
5616                    assert_bits(db.bitmap(), &live);
5617                    db.assert_exact().await;
5618                    db.assert_proves(&model, &evict).await;
5619                    db.destroy().await.unwrap();
5620                });
5621            }
5622        };
5623    }
5624
5625    policy_consistency_test!(
5626        test_current_unordered_policy_bits_and_proofs,
5627        UnorderedFixedDb
5628    );
5629    policy_consistency_test!(test_current_ordered_policy_bits_and_proofs, OrderedFixedDb);
5630
5631    /// Instantiate the policy pruning test for one current DB kind of Merkle family `$family`.
5632    /// Once the grandparent below applies, the log ends one location past the third bitmap chunk,
5633    /// and `$pending` complete chunks remain ungrafted: MMB grafts chunk `k` only once the log
5634    /// reaches `256 * k + 383` operations, while MMR grafts each chunk as it completes. After the
5635    /// child applies, prune can remove `$pruned` chunks: MMB holds its boundary until the
5636    /// height-9 subtree over the youngest pruned chunk pair is born, and chunks 2 and 3 have none
5637    /// below 1279 operations.
5638    ///
5639    /// A child of an unapplied parent, whose own parent (the grandparent) applies and is dropped
5640    /// before the child merkleizes, evicts and replaces applied updates, updates of both ancestors,
5641    /// and its own writes, and stops at its last write. It decides every update active after its
5642    /// writes up to that stop, in location order, and makes the same operations, floor, and root as
5643    /// a twin merkleized over both unapplied ancestors. Applying it grafts every complete chunk.
5644    /// After sync, prune to the sync boundary, and reopen, the root survives, the activity bitmap
5645    /// marks exactly the model's active updates, active keys prove their values against the
5646    /// canonical root, and evicted keys are absent.
5647    macro_rules! policy_prune_test {
5648        ($name:ident, $db:ty, $family:ty, $pending:literal, $pruned:literal) => {
5649            #[test_traced("WARN")]
5650            fn $name() {
5651                deterministic::Runner::default().start(|context| async move {
5652                    let ctx = context.child("db");
5653                    let partition = stringify!($name);
5654                    let mut db: $db = <$db>::init(
5655                        ctx.child("storage"),
5656                        fixed_config::<OneCap>(partition),
5657                        None,
5658                    )
5659                    .await
5660                    .unwrap();
5661
5662                    // Seed eight policy keys and 120 fillers at 1..129, then rewrite at most ten
5663                    // fillers per batch with a held floor until the log ends at 760, 8 locations
5664                    // short of the third chunk's end.
5665                    let keys: Vec<Digest> = (0..8).map(key).collect();
5666                    let created = [key(8), key(9)];
5667                    let fillers: Vec<Digest> = (100..220).map(key).collect();
5668                    let start = 3 * CHUNK_BITS - 8;
5669                    let mut values = BTreeMap::new();
5670                    let mut round = 0;
5671                    while *db.size() < start {
5672                        let writes: Vec<Digest> = if round == 0 {
5673                            keys.iter().chain(&fillers).copied().collect()
5674                        } else {
5675                            fillers[..(start - *db.size() - 1).min(10) as usize].to_vec()
5676                        };
5677                        let batch = writes
5678                            .iter()
5679                            .fold(db.new_batch(), |batch, key| {
5680                                batch.write(*key, Some(val(round)))
5681                            })
5682                            .merkleize(&db, None, &mut Hold)
5683                            .await
5684                            .unwrap();
5685                        db = db.apply_batch(batch).await.unwrap();
5686                        for key in writes {
5687                            values.insert(key, val(round));
5688                        }
5689                        round += 1;
5690                    }
5691                    assert_eq!(*db.size(), start);
5692
5693                    // An unapplied grandparent rewrites two policy keys and six fillers at 760..768
5694                    // and commits at 768. An unapplied parent rewrites two more policy keys.
5695                    let grand_writes: Vec<_> = [keys[1], keys[2]]
5696                        .iter()
5697                        .chain(&fillers[10..16])
5698                        .zip(1000..)
5699                        .map(|(key, i)| (*key, val(i)))
5700                        .collect();
5701                    let parent_writes = [(keys[3], val(2003)), (keys[7], val(2007))];
5702                    let grandparent = grand_writes
5703                        .iter()
5704                        .fold(db.new_batch(), |batch, (key, value)| {
5705                            batch.write(*key, Some(*value))
5706                        })
5707                        .merkleize(&db, None, &mut Hold)
5708                        .await
5709                        .unwrap();
5710                    let parent = parent_writes
5711                        .iter()
5712                        .fold(grandparent.new_batch::<Sha256>(), |batch, (key, value)| {
5713                            batch.write(*key, Some(*value))
5714                        })
5715                        .merkleize(&db, None, &mut Hold)
5716                        .await
5717                        .unwrap();
5718                    values.extend(grand_writes.iter().copied());
5719                    values.extend(parent_writes);
5720
5721                    // The child rewrites three policy keys and creates two. Each of its three
5722                    // twins is created while both ancestors are unapplied.
5723                    let child_writes = [
5724                        (keys[4], val(3004)),
5725                        (keys[5], val(3005)),
5726                        (keys[6], val(3006)),
5727                        (created[0], val(3008)),
5728                        (created[1], val(3009)),
5729                    ];
5730                    values.extend(child_writes);
5731                    let [held, pending, child] = [(); 3].map(|_| {
5732                        child_writes
5733                            .iter()
5734                            .fold(parent.new_batch::<Sha256>(), |batch, (key, value)| {
5735                                batch.write(*key, Some(*value))
5736                            })
5737                    });
5738
5739                    // A held twin locates the child's writes and the updates active after them.
5740                    let mut live = db.live().await;
5741                    for batch in [&grandparent, &parent] {
5742                        let (start, ops) = batch.operations();
5743                        replay(&mut live, start, &ops);
5744                    }
5745                    let held = held.merkleize(&db, None, &mut Hold).await.unwrap();
5746                    let (child_start, ops) = held.operations();
5747                    let mut written = live.clone();
5748                    replay(&mut written, child_start, &ops);
5749                    drop(held);
5750
5751                    // The policy evicts the seeded update of the first policy key and the
5752                    // ancestors' updates of the third and fourth, and replaces the ancestors'
5753                    // updates of the second and eighth. Of the child's writes, in location order,
5754                    // it evicts the first and third, replaces the second, keeps the fourth, and
5755                    // stops at the fifth.
5756                    let mut own: Vec<_> = child_writes
5757                        .iter()
5758                        .map(|(key, _)| (written[key], *key))
5759                        .collect();
5760                    own.sort();
5761                    let (stopped, stop) = own[4];
5762                    let choices: BTreeMap<Digest, Choice> = [
5763                        (keys[0], Choice::Evict),
5764                        (keys[2], Choice::Evict),
5765                        (keys[3], Choice::Evict),
5766                        (keys[1], Choice::Replace(val(4001))),
5767                        (keys[7], Choice::Replace(val(4007))),
5768                        (own[0].1, Choice::Evict),
5769                        (own[1].1, Choice::Replace(val(4100))),
5770                        (own[2].1, Choice::Evict),
5771                        (stop, Choice::Stop),
5772                    ]
5773                    .into_iter()
5774                    .collect();
5775                    for key in [keys[0], keys[1], keys[2], keys[3], keys[7]] {
5776                        assert!(written[&key] < child_start, "the child rewrites {key}");
5777                    }
5778                    let choose = |key: &Digest| choices.get(key).copied().unwrap_or(Choice::Keep);
5779
5780                    // The walk decides every update active after the writes, up to the stop, and
5781                    // leaves the floor at the stopped write.
5782                    let mut expected: Vec<_> = written
5783                        .iter()
5784                        .filter(|(_, loc)| **loc <= stopped)
5785                        .map(|(key, loc)| (*loc, *key, values[key]))
5786                        .collect();
5787                    expected.sort();
5788                    let mut policy = Script::new(usize::MAX, u64::MAX, choose);
5789                    let pending = pending.merkleize(&db, None, &mut policy).await.unwrap();
5790                    assert_eq!(policy.visited, expected);
5791                    assert_eq!(pending.bounds().inactivity_floor, stopped);
5792
5793                    // The grandparent applies and is dropped. It completes the third chunk, which
5794                    // MMB cannot graft yet.
5795                    let ungrafted = |db: &$db| {
5796                        let complete = db.bitmap().complete_chunks() as u64;
5797                        let graftable = grafting::graftable_chunks::<$family>(
5798                            *db.size(),
5799                            grafting::height::<32>(),
5800                        );
5801                        complete - graftable.min(complete)
5802                    };
5803                    let freed = Arc::downgrade(&grandparent.inner);
5804                    let db = db.apply_batch(grandparent).await.unwrap();
5805                    assert!(freed.upgrade().is_none());
5806                    assert_eq!(*db.size(), 3 * CHUNK_BITS + 1);
5807                    assert_eq!(ungrafted(&db), $pending);
5808
5809                    // The child merkleized over the applied grandparent matches the twin merkleized
5810                    // over both unapplied ancestors.
5811                    let mut policy = Script::new(usize::MAX, u64::MAX, choose);
5812                    let child = child.merkleize(&db, None, &mut policy).await.unwrap();
5813                    assert_eq!(policy.visited, expected);
5814                    assert_eq!(child.operations(), pending.operations());
5815                    assert_eq!(child.bounds().inactivity_floor, stopped);
5816                    let root = child.root();
5817                    assert_eq!(pending.root(), root);
5818                    drop(pending);
5819
5820                    // The model applies the decisions to the written values.
5821                    let mut model = values.clone();
5822                    let mut evicted = Vec::new();
5823                    for (key, choice) in &choices {
5824                        match choice {
5825                            Choice::Evict => {
5826                                model.remove(key);
5827                                evicted.push(*key);
5828                            }
5829                            Choice::Replace(value) => {
5830                                model.insert(*key, *value);
5831                            }
5832                            Choice::Keep | Choice::Stop => {}
5833                        }
5834                    }
5835                    let (start, ops) = child.operations();
5836                    replay(&mut live, start, &ops);
5837                    assert!(live.keys().eq(model.keys()));
5838
5839                    // Applying the child with its parent still unapplied grafts every complete
5840                    // chunk, and the state matches the model.
5841                    let db = db.apply_batch(child).await.unwrap();
5842                    drop(parent);
5843                    assert_eq!(db.root(), root);
5844                    assert_eq!(db.inactivity_floor_loc(), stopped);
5845                    assert_eq!(db.bitmap().complete_chunks(), 3);
5846                    assert_eq!(ungrafted(&db), 0);
5847                    assert_eq!(db.live().await, live);
5848                    assert_bits(db.bitmap(), &live);
5849                    db.assert_exact().await;
5850                    db.assert_proves(&model, &evicted).await;
5851
5852                    // Sync and prune whole chunks without changing the root or the state.
5853                    let db = db.sync().await.unwrap();
5854                    let boundary = db.sync_boundary();
5855                    assert_eq!(*boundary, $pruned * CHUNK_BITS);
5856                    let db = db.prune(boundary).await.unwrap();
5857                    assert_eq!(db.pruned_bits(), *boundary);
5858                    assert!(*db.bounds().start > 0 && db.bounds().start <= boundary);
5859                    assert_eq!(db.root(), root);
5860                    assert_eq!(db.live().await, live);
5861                    assert_bits(db.bitmap(), &live);
5862                    db.assert_exact().await;
5863                    db.assert_proves(&model, &evicted).await;
5864
5865                    // The pruned state survives reopen.
5866                    let bounds = db.bounds();
5867                    drop(db);
5868                    let db: $db =
5869                        <$db>::init(ctx.child("reopen"), fixed_config::<OneCap>(partition), None)
5870                            .await
5871                            .unwrap();
5872                    assert_eq!(db.root(), root);
5873                    assert_eq!(db.bounds(), bounds);
5874                    assert_eq!(db.inactivity_floor_loc(), stopped);
5875                    assert_eq!(db.pruned_bits(), *boundary);
5876                    assert_eq!(db.live().await, live);
5877                    assert_bits(db.bitmap(), &live);
5878                    db.assert_exact().await;
5879                    db.assert_proves(&model, &evicted).await;
5880                    db.destroy().await.unwrap();
5881                });
5882            }
5883        };
5884    }
5885
5886    policy_prune_test!(
5887        test_current_unordered_policy_prune,
5888        UnorderedFixedDb,
5889        mmr::Family,
5890        0,
5891        3
5892    );
5893    policy_prune_test!(
5894        test_current_ordered_policy_prune,
5895        OrderedFixedDb,
5896        mmr::Family,
5897        0,
5898        3
5899    );
5900    policy_prune_test!(
5901        test_current_unordered_mmb_policy_prune,
5902        UnorderedFixedMmbDb,
5903        mmb::Family,
5904        1,
5905        2
5906    );
5907    policy_prune_test!(
5908        test_current_ordered_mmb_policy_prune,
5909        OrderedFixedMmbDb,
5910        mmb::Family,
5911        1,
5912        2
5913    );
5914
5915    /// Over the same history and an unapplied parent that supersedes applied updates, `any` and
5916    /// `current` decide the same updates, reach the same floors, and merkleize the same operations.
5917    /// This holds for policies that keep, evict, replace, stop, or mix those choices, under
5918    /// unbounded limits and under entry, skip, combined, and zero-entry limits.
5919    async fn assert_policy_matches_any<M, A, C>(mut any: A, mut current: C)
5920    where
5921        M: merkle::Graftable,
5922        A: Inspect<M>,
5923        C: Inspect<M, Update = A::Update>,
5924        Operation<M, A::Update>: PartialEq + core::fmt::Debug,
5925    {
5926        // Apply the same held-floor history to both databases.
5927        let seed: Vec<_> = (0..24).map(|i| (key(i), Some(val(i)))).collect();
5928        let churn: Vec<_> = (0..24)
5929            .step_by(4)
5930            .map(|i| (key(i), Some(val(i + 100))))
5931            .collect();
5932        for writes in [&seed, &churn] {
5933            any = hold(any, writes).await;
5934            current = hold(current, writes).await;
5935        }
5936
5937        // Each database's unapplied parent supersedes a third of its applied updates.
5938        let parent: Vec<_> = (1..24)
5939            .step_by(3)
5940            .map(|i| (key(i), Some(val(i + 200))))
5941            .collect();
5942        let any_parent = hold_batch(&any, any.new_batch(), &parent).await;
5943        let current_parent = hold_batch(&current, current.new_batch(), &parent).await;
5944
5945        // The stopping policy keeps every update before key 5's, which the parent leaves in
5946        // place, and the mixed policy chooses by the key's first byte.
5947        let stop = key(5);
5948        let choose = |action: usize| {
5949            move |k: &Digest| match action {
5950                0 => Choice::Keep,
5951                1 => Choice::Evict,
5952                2 => Choice::Replace(val(1000)),
5953                3 if *k == stop => Choice::Stop,
5954                3 => Choice::Keep,
5955                _ => match k.as_ref()[0] % 3 {
5956                    0 => Choice::Keep,
5957                    1 => Choice::Evict,
5958                    _ => Choice::Replace(val(1001)),
5959                },
5960            }
5961        };
5962        for (entries, skips) in [
5963            (usize::MAX, u64::MAX),
5964            (5, u64::MAX),
5965            (usize::MAX, 6),
5966            (3, 2),
5967            (0, 4),
5968        ] {
5969            for action in 0..5 {
5970                let label = format!("action={action} entries={entries} skips={skips}");
5971                let mut any_policy = Script::new(entries, skips, choose(action));
5972                let any_batch = A::child(&any_parent)
5973                    .merkleize(&any, None, &mut any_policy)
5974                    .await
5975                    .unwrap();
5976                let mut current_policy = Script::new(entries, skips, choose(action));
5977                let current_batch = C::child(&current_parent)
5978                    .merkleize(&current, None, &mut current_policy)
5979                    .await
5980                    .unwrap();
5981                assert_eq!(any_policy.visited, current_policy.visited, "{label}");
5982                assert_eq!(
5983                    A::span(&any_batch).inactivity_floor,
5984                    C::span(&current_batch).inactivity_floor,
5985                    "{label}"
5986                );
5987                assert_eq!(A::ops(&any_batch), C::ops(&current_batch), "{label}");
5988
5989                // Without limits, the stopping policy stops at key 5 and the mixed policy makes
5990                // every choice.
5991                if (entries, skips) == (usize::MAX, u64::MAX) {
5992                    let keys: Vec<_> = any_policy.visited.iter().map(|(_, k, _)| *k).collect();
5993                    match action {
5994                        3 => assert_eq!(keys.last(), Some(&stop)),
5995                        4 => assert!((0..3).all(|r| keys.iter().any(|k| k.as_ref()[0] % 3 == r))),
5996                        _ => assert_eq!(keys.len(), 24),
5997                    }
5998                }
5999            }
6000        }
6001
6002        drop((any_parent, current_parent));
6003        any.destroy().await.unwrap();
6004        current.destroy().await.unwrap();
6005    }
6006
6007    /// [`assert_policy_matches_any`] on unordered and ordered databases.
6008    #[test_traced("INFO")]
6009    fn test_current_policy_matches_any() {
6010        type UnorderedAny = crate::qmdb::any::unordered::fixed::Db<
6011            mmr::Family,
6012            Context,
6013            Digest,
6014            Digest,
6015            Sha256,
6016            OneCap,
6017            Sequential,
6018        >;
6019        type OrderedAny = crate::qmdb::any::ordered::fixed::Db<
6020            mmr::Family,
6021            Context,
6022            Digest,
6023            Digest,
6024            Sha256,
6025            OneCap,
6026            Sequential,
6027        >;
6028
6029        deterministic::Runner::default().start(|context| async move {
6030            let any_config = |name: &str| crate::qmdb::any::test::fixed_db_config::<OneCap>(name);
6031            let any = UnorderedAny::init(
6032                context.child("unordered_any"),
6033                any_config("unordered-any"),
6034                None,
6035            )
6036            .await
6037            .unwrap();
6038            let current = UnorderedFixedDb::init(
6039                context.child("unordered_current"),
6040                fixed_config::<OneCap>("unordered-current"),
6041                None,
6042            )
6043            .await
6044            .unwrap();
6045            assert_policy_matches_any(any, current).await;
6046
6047            let any = OrderedAny::init(
6048                context.child("ordered_any"),
6049                any_config("ordered-any"),
6050                None,
6051            )
6052            .await
6053            .unwrap();
6054            let current = OrderedFixedDb::init(
6055                context.child("ordered_current"),
6056                fixed_config::<OneCap>("ordered-current"),
6057                None,
6058            )
6059            .await
6060            .unwrap();
6061            assert_policy_matches_any(any, current).await;
6062        });
6063    }
6064
6065    fn is_send<T: Send>(_: T) {}
6066
6067    #[allow(dead_code)]
6068    fn assert_policy_futures_are_send(
6069        unordered: &UnorderedFixedDb,
6070        ordered: &OrderedFixedDb,
6071        key: Digest,
6072    ) {
6073        let mut policy = Bounded {
6074            entries: 1,
6075            skips: 1,
6076        };
6077        is_send(
6078            unordered
6079                .new_batch()
6080                .merkleize(unordered, None, &mut policy),
6081        );
6082        is_send(ordered.new_batch().merkleize(ordered, None, &mut policy));
6083        is_send(async move {
6084            let (_, staged) = unordered.new_batch().stage(&[&key], unordered).await?;
6085            staged
6086                .merkleize(Vec::new(), Vec::new(), None, unordered, &mut policy)
6087                .await
6088        });
6089        is_send(async move {
6090            let (_, staged) = ordered.new_batch().stage(&[&key], ordered).await?;
6091            staged
6092                .merkleize(Vec::new(), Vec::new(), None, ordered, &mut policy)
6093                .await
6094        });
6095    }
6096
6097    impl<F, C, I, U, const N: usize, S> Inspect<F> for db::Db<F, Context, C, I, Sha256, U, N, S>
6098    where
6099        F: merkle::Graftable,
6100        C: Mutable<Item = Operation<F, U>>,
6101        I: UnorderedIndex<Value = Location<F>> + 'static,
6102        U: Update<Key = Digest, Value = Digest>,
6103        S: Strategy,
6104        Operation<F, U>: Codec,
6105        Self: DbAny<
6106                F,
6107                Key = Digest,
6108                Value = Digest,
6109                Digest = Digest,
6110                Merkleized = Arc<batch::MerkleizedBatch<F, Digest, U, N, S>>,
6111                Batch = batch::UnmerkleizedBatch<F, Sha256, U, N, S>,
6112            >,
6113    {
6114        type Update = U;
6115
6116        async fn assert_exact(&self) {
6117            assert_exact(&self.any).await;
6118        }
6119
6120        async fn live(&self) -> BTreeMap<Digest, Location<F>> {
6121            live(&self.any).await
6122        }
6123
6124        fn child(batch: &Self::Merkleized) -> Self::Batch {
6125            batch.new_batch::<Sha256>()
6126        }
6127
6128        fn span(batch: &Self::Merkleized) -> &Bounds<F, Digest> {
6129            batch.bounds()
6130        }
6131
6132        fn ops(batch: &Self::Merkleized) -> (Location<F>, Arc<Vec<Operation<F, U>>>) {
6133            batch.operations()
6134        }
6135
6136        async fn read(&self, batch: &Self::Merkleized, key: &Digest) -> Option<Digest> {
6137            batch.get(key, self).await.unwrap()
6138        }
6139    }
6140
6141    impl<F, C, I, V, const N: usize, S> Links<F>
6142        for db::Db<F, Context, C, I, Sha256, update::Ordered<Digest, V>, N, S>
6143    where
6144        F: merkle::Graftable,
6145        C: Mutable<Item = Operation<F, update::Ordered<Digest, V>>>,
6146        I: OrderedIndex<Value = Location<F>> + 'static,
6147        V: ValueEncoding<Value = Digest>,
6148        S: Strategy,
6149        Operation<F, update::Ordered<Digest, V>>: Codec,
6150        Self: Inspect<F>,
6151    {
6152        async fn assert_link(&self, key: Digest, value: Digest, next: Digest) {
6153            assert_eq!(self.get(&key).await.unwrap(), Some(value), "{key} diverged");
6154            let proof = self.key_value_proof(key).await.unwrap();
6155            assert_eq!(proof.next_key, next, "{key} links to the wrong key");
6156            assert!(
6157                proof.verify::<Sha256, V>(key, value, &self.root()),
6158                "{key} fails to prove its link",
6159            );
6160        }
6161
6162        async fn assert_absent(&self, key: Digest) {
6163            assert_eq!(self.get(&key).await.unwrap(), None, "{key} is live");
6164            let proof = self.exclusion_proof(&key).await.unwrap();
6165            assert!(
6166                proof.verify::<Sha256>(&key, &self.root()),
6167                "{key} fails to prove its exclusion",
6168            );
6169        }
6170    }
6171
6172    impl<F, C, I, V, const N: usize, S> Neighbors<F>
6173        for db::Db<F, Context, C, I, Sha256, update::Ordered<Digest, V>, N, S>
6174    where
6175        F: merkle::Graftable,
6176        C: Mutable<Item = Operation<F, update::Ordered<Digest, V>>>,
6177        I: OrderedIndex<Value = Location<F>> + 'static,
6178        V: ValueEncoding<Value = Digest>,
6179        S: Strategy,
6180        Operation<F, update::Ordered<Digest, V>>: Codec,
6181        Self: Links<F>
6182            + DbAny<
6183                F,
6184                Merkleized = Arc<
6185                    batch::MerkleizedBatch<F, Digest, update::Ordered<Digest, V>, N, S>,
6186                >,
6187            >,
6188    {
6189        async fn neighbors(
6190            &self,
6191            batch: Option<&Self::Merkleized>,
6192            key: &Digest,
6193        ) -> (Option<Digest>, Option<Digest>) {
6194            match batch {
6195                Some(batch) => (
6196                    batch.get_next_key(key, self).await.unwrap(),
6197                    batch.get_prev_key(key, self).await.unwrap(),
6198                ),
6199                None => (
6200                    self.get_next_key(key).await.unwrap(),
6201                    self.get_prev_key(key).await.unwrap(),
6202                ),
6203            }
6204        }
6205    }
6206
6207    /// Open a current database in `partition` with `context` and return it with a function that
6208    /// reopens it.
6209    async fn current_db<C, F, Fut>(
6210        context: Context,
6211        open_db: F,
6212        partition: &'static str,
6213    ) -> (
6214        C,
6215        impl Fn(Context) -> Pin<Box<dyn Future<Output = C> + Send>> + Clone,
6216    )
6217    where
6218        F: Fn(Context, String) -> Fut + Clone + Send + 'static,
6219        Fut: Future<Output = C> + Send + 'static,
6220    {
6221        let db = open_db(context, partition.into()).await;
6222        let reopen = move |ctx: Context| -> Pin<Box<dyn Future<Output = C> + Send>> {
6223            Box::pin(open_db(ctx, partition.into()))
6224        };
6225        (db, reopen)
6226    }
6227
6228    /// Define `$name` to run the Any test `$any` on a current database bounded by `$bound`
6229    /// and opened in `$partition`, for each variant `$variants` selects. With `reopen`, the
6230    /// test also receives a function that reopens the database.
6231    macro_rules! current_test {
6232        ($variants:ident, $name:ident, $any:ident, $partition:literal, $bound:ident) => {
6233            current_test!(@define $variants, $name, $partition, $bound, |context, db, _| {
6234                $any(context, db, val)
6235            });
6236        };
6237        ($variants:ident, $name:ident, $any:ident, $partition:literal, $bound:ident, reopen) => {
6238            current_test!(@define $variants, $name, $partition, $bound, |context, db, reopen| {
6239                $any(context, db, reopen, val)
6240            });
6241        };
6242        (@define $variants:ident, $name:ident, $partition:literal, $bound:ident, $run:expr) => {
6243            async fn $name<M, C, F, Fut>(context: Context, open_db: F)
6244            where
6245                M: merkle::Graftable,
6246                C: $bound<M>,
6247                C::Merkleized: Clone,
6248                Operation<M, C::Update>: Codec,
6249                F: Fn(Context, String) -> Fut + Clone + Send + 'static,
6250                Fut: Future<Output = C> + Send + 'static,
6251            {
6252                let (db, reopen) = current_db(context.child("db"), open_db, $partition).await;
6253                ($run)(context, db, reopen).await;
6254            }
6255
6256            $variants!($name, "WARN");
6257        };
6258    }
6259
6260    // Each Any test also runs on every current variant, whose walks draw candidates from the
6261    // speculative bitmap.
6262    current_test!(
6263        test_for_all_variants,
6264        test_current_activity_depths,
6265        test_any_activity_depths,
6266        "activity",
6267        Inspect,
6268        reopen
6269    );
6270    current_test!(
6271        test_for_all_variants,
6272        test_current_proportional_bound,
6273        test_any_proportional_bound,
6274        "bound",
6275        Inspect
6276    );
6277    current_test!(
6278        test_for_all_variants,
6279        test_current_policy_matches_proportional,
6280        test_any_policy_matches_proportional,
6281        "proportional",
6282        Inspect
6283    );
6284    current_test!(
6285        test_for_all_variants,
6286        test_current_policy_decisions_match_writes,
6287        test_any_policy_decisions_match_writes,
6288        "decisions",
6289        Inspect
6290    );
6291    current_test!(
6292        test_for_all_variants,
6293        test_current_policy_limits_after_colliding_writes,
6294        test_any_policy_limits_after_colliding_writes,
6295        "colliding",
6296        Inspect
6297    );
6298    current_test!(
6299        test_for_all_variants,
6300        test_current_policy_hold,
6301        test_any_policy_hold,
6302        "hold",
6303        Inspect
6304    );
6305    current_test!(
6306        test_for_all_variants,
6307        test_current_policy_limits_with_writes,
6308        test_any_policy_limits_with_writes,
6309        "writes",
6310        Inspect
6311    );
6312    current_test!(
6313        test_for_all_variants,
6314        test_current_proportional_one_batch,
6315        test_any_proportional_one_batch,
6316        "one-batch",
6317        Inspect
6318    );
6319    current_test!(
6320        test_for_all_variants,
6321        test_current_policy_own_writes,
6322        test_any_policy_own_writes,
6323        "own",
6324        Inspect,
6325        reopen
6326    );
6327    current_test!(
6328        test_for_all_variants,
6329        test_current_policy_evicts_parent_created_key,
6330        test_any_policy_evicts_parent_created_key,
6331        "parent-created",
6332        Changes,
6333        reopen
6334    );
6335    current_test!(
6336        test_for_all_variants,
6337        test_current_policy_ancestor_twins,
6338        test_any_policy_ancestor_twins,
6339        "twins",
6340        Inspect
6341    );
6342    current_test!(
6343        test_for_all_variants,
6344        test_current_policy_keep_evict_and_recover,
6345        test_any_policy_keep_evict_and_recover,
6346        "recover",
6347        Inspect,
6348        reopen
6349    );
6350
6351    // On a current database, the ordered tests also prove each link and each evicted key's
6352    // exclusion.
6353    current_test!(
6354        test_for_ordered_variants,
6355        test_current_ordered_policy_eviction_matrix,
6356        test_any_ordered_policy_eviction_matrix,
6357        "matrix",
6358        Neighbors,
6359        reopen
6360    );
6361    current_test!(
6362        test_for_ordered_variants,
6363        test_current_ordered_policy_repair_across_ancestors,
6364        test_any_ordered_policy_repair_across_ancestors,
6365        "repair",
6366        Neighbors,
6367        reopen
6368    );
6369}