Merge branch 'bridgetree_checkpoint_id' into add_librustzcash_types

This commit is contained in:
Kris Nuttycombe 2023-03-23 16:18:32 -06:00
commit 978da59c67
1 changed files with 39 additions and 33 deletions

View File

@ -323,9 +323,9 @@ impl<'a, H: Hashable + Ord + Clone + 'a> MerkleBridge<H> {
/// bridges; instead, we use [`Checkpoint`] values to be able to rapidly restore the cache to its
/// previous state.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Checkpoint {
pub struct Checkpoint<C> {
/// The unique identifier for this checkpoint.
id: usize,
id: C,
/// The number of bridges that will be retained in a rewind.
bridges_len: usize,
/// A set of the positions that have been marked during the period that this
@ -337,10 +337,10 @@ pub struct Checkpoint {
forgotten: BTreeSet<Position>,
}
impl Checkpoint {
impl<C> Checkpoint<C> {
/// Creates a new checkpoint from its constituent parts.
pub fn from_parts(
id: usize,
id: C,
bridges_len: usize,
marked: BTreeSet<Position>,
forgotten: BTreeSet<Position>,
@ -354,7 +354,7 @@ impl Checkpoint {
}
/// Creates a new empty checkpoint for the specified [`BridgeTree`] state.
pub fn at_length(bridges_len: usize, id: usize) -> Self {
pub fn at_length(bridges_len: usize, id: C) -> Self {
Checkpoint {
id,
bridges_len,
@ -365,8 +365,8 @@ impl Checkpoint {
/// The unique identifier for the checkpoint, which is simply an automatically incrementing
/// index over all checkpoints that have ever been created in the history of the tree.
pub fn id(&self) -> usize {
self.id
pub fn id(&self) -> &C {
&self.id
}
/// Returns the length of the [`BridgeTree::prior_bridges`] vector of the [`BridgeTree`] to
@ -424,7 +424,7 @@ impl Checkpoint {
/// A sparse representation of a Merkle tree with linear appending of leaves that contains enough
/// information to produce a witness for any `mark`ed leaf.
#[derive(Clone, PartialEq, Eq)]
pub struct BridgeTree<H, const DEPTH: u8> {
pub struct BridgeTree<H, C, const DEPTH: u8> {
/// The ordered list of Merkle bridges representing the history
/// of the tree. There will be one bridge for each saved leaf.
prior_bridges: Vec<MerkleBridge<H>>,
@ -437,14 +437,14 @@ pub struct BridgeTree<H, const DEPTH: u8> {
/// This deque must be maintained to have a minimum size of 1 and a maximum
/// size of `max_checkpoints` in order to correctly maintain mark & rewind
/// semantics.
checkpoints: VecDeque<Checkpoint>,
checkpoints: VecDeque<Checkpoint<C>>,
/// The maximum number of checkpoints to retain. If this number is
/// exceeded, the oldest checkpoint will be dropped when creating
/// a new checkpoint.
max_checkpoints: usize,
}
impl<H: Hashable + Ord + Debug, const DEPTH: u8> Debug for BridgeTree<H, DEPTH> {
impl<H: Debug, C: Debug, const DEPTH: u8> Debug for BridgeTree<H, C, DEPTH> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(
f,
@ -466,12 +466,12 @@ pub enum BridgeTreeError {
CheckpointMismatch,
}
impl<H, const DEPTH: u8> BridgeTree<H, DEPTH> {
impl<H, C, const DEPTH: u8> BridgeTree<H, C, DEPTH> {
/// Construct an empty BridgeTree value with the specified maximum number of checkpoints.
///
/// Panics if `max_checkpoints < 1` because mark/rewind logic depends upon the presence
/// of checkpoints to function.
pub fn new(max_checkpoints: usize, initial_checkpoint_id: usize) -> Self {
pub fn new(max_checkpoints: usize, initial_checkpoint_id: C) -> Self {
assert!(max_checkpoints >= 1);
Self {
prior_bridges: vec![],
@ -516,7 +516,7 @@ impl<H, const DEPTH: u8> BridgeTree<H, DEPTH> {
}
/// Returns the checkpoints to which this tree may be rewound.
pub fn checkpoints(&self) -> &VecDeque<Checkpoint> {
pub fn checkpoints(&self) -> &VecDeque<Checkpoint<C>> {
&self.checkpoints
}
@ -533,7 +533,7 @@ impl<H, const DEPTH: u8> BridgeTree<H, DEPTH> {
}
}
impl<H: Hashable + Ord + Clone, const DEPTH: u8> BridgeTree<H, DEPTH> {
impl<H: Hashable + Ord + Clone, C: Clone + Ord, const DEPTH: u8> BridgeTree<H, C, DEPTH> {
/// Construct a new BridgeTree that will start recording changes from the state of
/// the specified frontier.
pub fn from_frontier(max_checkpoints: usize, frontier: NonEmptyFrontier<H>) -> Self {
@ -557,7 +557,7 @@ impl<H: Hashable + Ord + Clone, const DEPTH: u8> BridgeTree<H, DEPTH> {
prior_bridges: Vec<MerkleBridge<H>>,
current_bridge: Option<MerkleBridge<H>>,
saved: BTreeMap<Position, usize>,
checkpoints: VecDeque<Checkpoint>,
checkpoints: VecDeque<Checkpoint<C>>,
max_checkpoints: usize,
) -> Result<Self, BridgeTreeError> {
Self::check_consistency_internal(
@ -590,7 +590,7 @@ impl<H: Hashable + Ord + Clone, const DEPTH: u8> BridgeTree<H, DEPTH> {
prior_bridges: &[MerkleBridge<H>],
current_bridge: &Option<MerkleBridge<H>>,
saved: &BTreeMap<Position, usize>,
checkpoints: &VecDeque<Checkpoint>,
checkpoints: &VecDeque<Checkpoint<C>>,
max_checkpoints: usize,
) -> Result<(), BridgeTreeError> {
// check that saved values correspond to bridges
@ -760,8 +760,8 @@ impl<H: Hashable + Ord + Clone, const DEPTH: u8> BridgeTree<H, DEPTH> {
/// It is valid to have multiple checkpoints for the same tree state, and each `rewind` call
/// will remove a single checkpoint. Successive checkpoint identifiers must always be provided
/// in increasing order.
pub fn checkpoint(&mut self, id: usize) -> bool {
if Some(id) > self.checkpoints.back().map(|c| c.id) {
pub fn checkpoint(&mut self, id: C) -> bool {
if Some(&id) > self.checkpoints.back().map(|c| &c.id) {
match self.current_bridge.take() {
Some(cur_b) => {
// Do not create a duplicate bridge
@ -831,9 +831,9 @@ impl<H: Hashable + Ord + Clone, const DEPTH: u8> BridgeTree<H, DEPTH> {
checkpoint_depth: usize,
) -> Result<Vec<H>, WitnessingError> {
#[derive(Debug)]
enum AuthBase<'a> {
enum AuthBase<'a, C> {
Current,
Checkpoint(usize, &'a Checkpoint),
Checkpoint(usize, &'a Checkpoint<C>),
}
// Find the earliest checkpoint having a matching root, or the current
@ -1003,7 +1003,9 @@ mod tests {
Hashable,
};
impl<H: Hashable + Ord + Clone, const DEPTH: u8> testing::Tree<H, usize> for BridgeTree<H, DEPTH> {
impl<H: Hashable + Ord + Clone, const DEPTH: u8> testing::Tree<H, usize>
for BridgeTree<H, usize, DEPTH>
{
fn append(&mut self, value: H, retention: Retention<usize>) -> bool {
let appended = BridgeTree::append(self, value);
if appended {
@ -1056,7 +1058,7 @@ mod tests {
#[test]
fn tree_depth() {
let mut tree = BridgeTree::<String, 3>::new(100, 0);
let mut tree = BridgeTree::<String, usize, 3>::new(100, 0);
for c in 'a'..'i' {
assert!(tree.append(c.to_string()))
}
@ -1064,7 +1066,7 @@ mod tests {
}
fn check_garbage_collect<H: Hashable + Clone + Ord, const DEPTH: u8>(
mut tree: BridgeTree<H, DEPTH>,
mut tree: BridgeTree<H, usize, DEPTH>,
) {
// Add checkpoints until we're sure everything that can be gc'ed will be gc'ed
for i in 0..tree.max_checkpoints {
@ -1082,13 +1084,13 @@ mod tests {
fn arb_bridgetree<G: Strategy + Clone>(
item_gen: G,
max_count: usize,
) -> impl Strategy<Value = BridgeTree<G::Value, 8>>
) -> impl Strategy<Value = BridgeTree<G::Value, usize, 8>>
where
G::Value: Hashable + Ord + Clone + Debug + 'static,
{
proptest::collection::vec(arb_operation(item_gen, 0..max_count), 0..max_count).prop_map(
|ops| {
let mut tree: BridgeTree<G::Value, 8> = BridgeTree::new(10, 0);
let mut tree: BridgeTree<G::Value, usize, 8> = BridgeTree::new(10, 0);
for (i, op) in ops.into_iter().enumerate() {
apply_operation(&mut tree, op.map_checkpoint_id(|_| i));
}
@ -1124,29 +1126,33 @@ mod tests {
#[test]
fn root_hashes() {
check_root_hashes(|max_checkpoints| BridgeTree::<String, 4>::new(max_checkpoints, 0));
check_root_hashes(|max_checkpoints| {
BridgeTree::<String, usize, 4>::new(max_checkpoints, 0)
});
}
#[test]
fn witness() {
check_witnesses(|max_checkpoints| BridgeTree::<String, 4>::new(max_checkpoints, 0));
check_witnesses(|max_checkpoints| BridgeTree::<String, usize, 4>::new(max_checkpoints, 0));
}
#[test]
fn checkpoint_rewind() {
check_checkpoint_rewind(|max_checkpoints| BridgeTree::<String, 4>::new(max_checkpoints, 0));
check_checkpoint_rewind(|max_checkpoints| {
BridgeTree::<String, usize, 4>::new(max_checkpoints, 0)
});
}
#[test]
fn rewind_remove_mark() {
check_rewind_remove_mark(|max_checkpoints| {
BridgeTree::<String, 4>::new(max_checkpoints, 0)
BridgeTree::<String, usize, 4>::new(max_checkpoints, 0)
});
}
#[test]
fn garbage_collect() {
let mut tree: BridgeTree<String, 7> = BridgeTree::new(1000, 0);
let mut tree: BridgeTree<String, usize, 7> = BridgeTree::new(1000, 0);
let empty_root = tree.root(0);
tree.append("a".to_string());
for i in 0..100 {
@ -1157,7 +1163,7 @@ mod tests {
tree.rewind();
assert!(tree.root(0) != empty_root);
let mut t = BridgeTree::<String, 7>::new(10, 0);
let mut t = BridgeTree::<String, usize, 7>::new(10, 0);
let mut to_unmark = vec![];
let mut has_witness = vec![];
for i in 0usize..100 {
@ -1201,10 +1207,10 @@ mod tests {
// Combined tree tests
fn new_combined_tree<H: Hashable + Ord + Clone + Debug>(
max_checkpoints: usize,
) -> CombinedTree<H, usize, CompleteTree<H, usize, 4>, BridgeTree<H, 4>> {
) -> CombinedTree<H, usize, CompleteTree<H, usize, 4>, BridgeTree<H, usize, 4>> {
CombinedTree::new(
CompleteTree::<H, usize, 4>::new(max_checkpoints, 0),
BridgeTree::<H, 4>::new(max_checkpoints, 0),
BridgeTree::<H, usize, 4>::new(max_checkpoints, 0),
)
}