Skip to content

Commit 2236918

Browse files
re-apply pyramid-mmb work onto main bitmap API
Reconstructs the policy and sync changes that were dropped when the rebase accepted main's version for the conflicting files. Adds split_root and root_bagging to qmdb::any::Config and Db, threads policy through sync (build_db, proof_spec, has_local_target_state), updates init_from_log to take + cache the policy, recomputes the cached root after rewind, and restores the floor-bound corruption check, all on top of main's shared-bitmap reorg.
1 parent 79d16d4 commit 2236918

9 files changed

Lines changed: 402 additions & 199 deletions

File tree

storage/src/qmdb/any/db.rs

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::{
1313
contiguous::{Contiguous, Mutable, Reader},
1414
Error as JournalError,
1515
},
16-
merkle::{Family, Location, Proof},
16+
merkle::{Bagging, Family, Location, Proof, RootSpec},
1717
qmdb::{
1818
bitmap::Shared, build_snapshot_from_log, delete_known_loc,
1919
operation::Operation as OperationTrait, update_known_loc, Error,
@@ -73,6 +73,15 @@ pub struct Db<
7373
/// - There is always at least one commit operation in the log.
7474
pub(crate) log: AuthenticatedLog<F, E, C, H>,
7575

76+
/// Cached operations root for this database.
77+
pub(crate) root: H::Digest,
78+
79+
/// Whether the operations root uses inactive-prefix split semantics.
80+
pub(crate) split_root: bool,
81+
82+
/// Bagging used by the operations root.
83+
pub(crate) root_bagging: Bagging,
84+
7685
/// A location before which all operations are "inactive" (that is, operations before this point
7786
/// are over keys that have been updated by some operation at or after this point).
7887
pub(crate) inactivity_floor_loc: Location<F>,
@@ -145,8 +154,15 @@ where
145154
}
146155
}
147156

148-
pub fn root(&self) -> H::Digest {
149-
self.log.root()
157+
/// Return the canonical QMDB operations root.
158+
pub const fn root(&self) -> H::Digest {
159+
self.root
160+
}
161+
162+
/// Return the operations-root spec for the given leaf count and inactivity floor.
163+
pub(crate) fn root_spec(&self, leaves: Location<F>, inactivity_floor: Location<F>) -> RootSpec {
164+
let inactive_peaks = F::inactive_peaks(F::location_to_position(leaves), inactivity_floor);
165+
RootSpec::from_split_policy(self.split_root, self.root_bagging, inactive_peaks)
150166
}
151167

152168
/// Get the value of `key` in the db, or None if it has no value.
@@ -302,14 +318,46 @@ where
302318
Ok(())
303319
}
304320

321+
/// Returns a historical proof for `historical_size` operations, anchored at `start_loc`
322+
/// and bounded by `max_ops`.
323+
///
324+
/// # Contract
325+
///
326+
/// When this database is configured with `split_root: true`, `historical_size` must be a
327+
/// commit-boundary size: the operation at `historical_size - 1` must itself be a commit
328+
/// op declaring the governing inactivity floor. With `split_root: false` the spec does
329+
/// not depend on the floor and any retained `historical_size` works.
330+
///
331+
/// # Errors
332+
///
333+
/// Returns [`crate::qmdb::Error::HistoricalFloorPruned`] (split_root only) if
334+
/// `historical_size - 1` is retained but is not a commit op, either because the caller
335+
/// passed a non-commit-boundary size or because pruning removed the commit that would
336+
/// have governed it.
305337
pub async fn historical_proof(
306338
&self,
307339
historical_size: Location<F>,
308340
start_loc: Location<F>,
309341
max_ops: NonZeroU64,
310342
) -> Result<(Proof<F, H::Digest>, Vec<Operation<F, U>>), crate::qmdb::Error<F>> {
343+
if historical_size > self.log.size().await {
344+
return Err(crate::qmdb::Error::Merkle(
345+
crate::merkle::Error::RangeOutOfBounds(historical_size),
346+
));
347+
}
348+
349+
let inactivity_floor = if self.split_root {
350+
let reader = self.log.reader().await;
351+
crate::qmdb::find_inactivity_floor_at::<F, _>(&reader, historical_size, |op| {
352+
op.has_floor()
353+
})
354+
.await?
355+
} else {
356+
Location::new(0)
357+
};
358+
let spec = self.root_spec(historical_size, inactivity_floor);
311359
self.log
312-
.historical_proof(historical_size, start_loc, max_ops)
360+
.historical_proof(historical_size, start_loc, max_ops, spec)
313361
.await
314362
.map_err(Into::into)
315363
}
@@ -484,6 +532,9 @@ where
484532
))?;
485533
self.last_commit_loc = Location::new(rewind_size - 1);
486534
self.inactivity_floor_loc = rewind_floor;
535+
self.root = self
536+
.log
537+
.root(self.root_spec(Location::new(rewind_size), rewind_floor))?;
487538

488539
Ok(())
489540
}
@@ -512,13 +563,20 @@ where
512563
mut index: I,
513564
log: AuthenticatedLog<F, E, C, H>,
514565
shared_bitmap: Option<Arc<Shared<N>>>,
566+
split_root: bool,
567+
root_bagging: Bagging,
515568
) -> Result<Self, crate::qmdb::Error<F>> {
516569
let (last_commit_loc, inactivity_floor_loc, active_keys, bitmap) = {
517570
let reader = log.reader().await;
518571
let bounds = reader.bounds();
519572
let last_commit_loc = bounds.end.checked_sub(1).expect("commit should exist");
520573
let last_commit = reader.read(last_commit_loc).await?;
521574
let inactivity_floor_loc = last_commit.has_floor().expect("should be a commit");
575+
if *inactivity_floor_loc > last_commit_loc {
576+
return Err(crate::qmdb::Error::DataCorrupted(
577+
"inactivity floor exceeds last commit",
578+
));
579+
}
522580

523581
// Seed the bitmap so its pruned prefix matches the retained log boundary. Bits in
524582
// [pruned_bits, bounds.start) correspond to pruned operations and remain 0; replay
@@ -585,8 +643,21 @@ where
585643
));
586644
}
587645

646+
let inactive_peaks = F::inactive_peaks(
647+
F::location_to_position(log.merkle.leaves()),
648+
inactivity_floor_loc,
649+
);
650+
let root = log.root(RootSpec::from_split_policy(
651+
split_root,
652+
root_bagging,
653+
inactive_peaks,
654+
))?;
655+
588656
Ok(Self {
589657
log,
658+
root,
659+
split_root,
660+
root_bagging,
590661
inactivity_floor_loc,
591662
snapshot: index,
592663
last_commit_loc,

0 commit comments

Comments
 (0)