Skip to content

Commit 1be5699

Browse files
committed
Merge branch 'main' into bridgetree-0.7.0
2 parents 4552f24 + a67b17f commit 1be5699

File tree

25 files changed

+1057
-509
lines changed

25 files changed

+1057
-509
lines changed

.github/workflows/lints-beta.yml

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,22 @@ on: pull_request
66
jobs:
77
clippy-beta:
88
name: Clippy (beta)
9-
timeout-minutes: 30
109
runs-on: ubuntu-latest
1110
continue-on-error: true
1211
steps:
13-
- uses: actions/checkout@v3
12+
- uses: actions/checkout@v4
13+
with:
14+
persist-credentials: false
1415
- uses: dtolnay/rust-toolchain@beta
1516
id: toolchain
16-
- run: rustup override set ${{steps.toolchain.outputs.name}}
17+
with:
18+
components: clippy
19+
- run: rustup override set "${TOOLCHAIN}"
20+
shell: sh
21+
env:
22+
TOOLCHAIN: ${{steps.toolchain.outputs.name}}
1723
- name: Run Clippy (beta)
1824
uses: actions-rs/clippy-check@v1
19-
continue-on-error: true
2025
with:
2126
name: Clippy (beta)
2227
token: ${{ secrets.GITHUB_TOKEN }}

Cargo.lock

Lines changed: 6 additions & 40 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ categories = ["algorithms", "data-structures"]
1818

1919
[workspace.dependencies]
2020
# Intra-workspace dependencies
21-
incrementalmerkletree = { version = "0.7", path = "incrementalmerkletree" }
22-
incrementalmerkletree-testing = { version = "0.1" }
21+
incrementalmerkletree = { version = "0.8.1", path = "incrementalmerkletree" }
22+
incrementalmerkletree-testing = { version = "0.3", path = "incrementalmerkletree-testing" }
2323

2424
# Testing
2525
proptest = "1"

incrementalmerkletree-testing/CHANGELOG.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,39 @@ test suite to migrate to later `incrementalmerkletree` versions.
1616
### Changed
1717
- Updated to `incrementalmerkletree 0.8`
1818

19+
## [0.3.0] - 2025-01-30
20+
21+
### Changed
22+
- Updated to `incrementalmerkletree 0.8`
23+
24+
## [0.2.0] - 2024-10-04
25+
26+
This release includes a significant refactoring and rework of several methods
27+
of the `incrementalmerkletree_testing::Tree` trait. Please read the notes for
28+
this release carefully as the semantics of important methods have changed.
29+
These changes may require changes to example tests that rely on this crate; in
30+
particular, additional checkpoints may be required in circumstances where
31+
rewind operations are being applied.
32+
33+
### Changed
34+
- `incrementalmerkletree_testing::Tree`
35+
- Added method `Tree::checkpoint_count`.
36+
- `Tree::root` now takes its `checkpoint_depth` argument as `Option<usize>`
37+
instead of `usize`. Passing `None` to this method will now compute the root
38+
given all of the leaves in the tree; if a `Some` value is passed,
39+
implementations of this method must treat the wrapped `usize` as a reverse
40+
index into the checkpoints added to the tree, or return `None` if no
41+
checkpoint exists at the specified index. This effectively modifies this
42+
method to use zero-based indexing instead of a muddled 1-based indexing
43+
scheme.
44+
- `Tree::rewind` now takes an additional `checkpoint_depth` argument, which
45+
is non-optional. Rewinding the tree may now only be performed if there is
46+
a checkpoint at the specified depth to rewind to. This depth should be
47+
treated as a zero-based reverse index into the checkpoints of the tree.
48+
Rewinding no longer removes the checkpoint being rewound to; instead, it
49+
removes the effect all state changes to the tree resulting from
50+
operations performed since the checkpoint was created, but leaves the
51+
checkpoint itself in place.
52+
1953
## [0.1.0] - 2024-09-25
2054
Initial release.

incrementalmerkletree-testing/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "incrementalmerkletree-testing"
33
description = "Common types, interfaces, and utilities for testing Merkle tree data structures"
4-
version = "0.2.0-backcompat.0.8"
4+
version = "0.3.0"
55
authors = [
66
"Kris Nuttycombe <[email protected]>",
77
"Sean Bowe <[email protected]>",
@@ -18,6 +18,6 @@ all-features = true
1818
rustdoc-args = ["--cfg", "docsrs"]
1919

2020
[dependencies]
21-
incrementalmerkletree = { version = "0.8", features = ["test-dependencies"] }
21+
incrementalmerkletree = { workspace = true, features = ["test-dependencies"] }
2222

2323
proptest.workspace = true

incrementalmerkletree-testing/src/complete_tree.rs

Lines changed: 58 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ impl<H: Hashable, C: Clone + Ord + core::fmt::Debug, const DEPTH: u8> CompleteTr
106106
self.mark();
107107
}
108108
Retention::Checkpoint { id, marking } => {
109-
let latest_checkpoint = self.checkpoints.keys().rev().next();
109+
let latest_checkpoint = self.checkpoints.keys().next_back();
110110
if Some(&id) > latest_checkpoint {
111111
append(&mut self.leaves, value, DEPTH)?;
112112
if marking == Marking::Marked {
@@ -145,7 +145,7 @@ impl<H: Hashable, C: Clone + Ord + core::fmt::Debug, const DEPTH: u8> CompleteTr
145145
if !self.marks.contains(&pos) {
146146
self.marks.insert(pos);
147147

148-
if let Some(checkpoint) = self.checkpoints.values_mut().rev().next() {
148+
if let Some(checkpoint) = self.checkpoints.values_mut().next_back() {
149149
checkpoint.marked.insert(pos);
150150
}
151151
}
@@ -156,11 +156,14 @@ impl<H: Hashable, C: Clone + Ord + core::fmt::Debug, const DEPTH: u8> CompleteTr
156156
}
157157
}
158158

159+
// Creates a new checkpoint with the specified identifier and the given tree position; if `pos`
160+
// is not provided, the position of the most recently appended leaf is used, or a new
161+
// checkpoint of the empty tree is added if appropriate.
159162
fn checkpoint(&mut self, id: C, pos: Option<Position>) {
160163
self.checkpoints.insert(
161164
id,
162165
Checkpoint::at_length(pos.map_or_else(
163-
|| 0,
166+
|| self.leaves.len(),
164167
|p| usize::try_from(p).expect(MAX_COMPLETE_SIZE_ERROR) + 1,
165168
)),
166169
);
@@ -170,16 +173,12 @@ impl<H: Hashable, C: Clone + Ord + core::fmt::Debug, const DEPTH: u8> CompleteTr
170173
}
171174

172175
fn leaves_at_checkpoint_depth(&self, checkpoint_depth: usize) -> Option<usize> {
173-
if checkpoint_depth == 0 {
174-
Some(self.leaves.len())
175-
} else {
176-
self.checkpoints
177-
.iter()
178-
.rev()
179-
.skip(checkpoint_depth - 1)
180-
.map(|(_, c)| c.leaves_len)
181-
.next()
182-
}
176+
self.checkpoints
177+
.iter()
178+
.rev()
179+
.skip(checkpoint_depth)
180+
.map(|(_, c)| c.leaves_len)
181+
.next()
183182
}
184183

185184
/// Removes the oldest checkpoint. Returns true if successful and false if
@@ -237,21 +236,20 @@ impl<H: Hashable + PartialEq + Clone, C: Ord + Clone + core::fmt::Debug, const D
237236
}
238237
}
239238

240-
fn root(&self, checkpoint_depth: usize) -> Option<H> {
241-
self.leaves_at_checkpoint_depth(checkpoint_depth)
242-
.and_then(|len| root(&self.leaves[0..len], DEPTH))
239+
fn root(&self, checkpoint_depth: Option<usize>) -> Option<H> {
240+
checkpoint_depth.map_or_else(
241+
|| root(&self.leaves[..], DEPTH),
242+
|depth| {
243+
self.leaves_at_checkpoint_depth(depth)
244+
.and_then(|len| root(&self.leaves[0..len], DEPTH))
245+
},
246+
)
243247
}
244248

245249
fn witness(&self, position: Position, checkpoint_depth: usize) -> Option<Vec<H>> {
246-
if self.marks.contains(&position) && checkpoint_depth <= self.checkpoints.len() {
250+
if self.marks.contains(&position) {
247251
let leaves_len = self.leaves_at_checkpoint_depth(checkpoint_depth)?;
248-
let c_idx = self.checkpoints.len() - checkpoint_depth;
249-
if self
250-
.checkpoints
251-
.iter()
252-
.skip(c_idx)
253-
.any(|(_, c)| c.marked.contains(&position))
254-
{
252+
if u64::from(position) >= u64::try_from(leaves_len).unwrap() {
255253
// The requested position was marked after the checkpoint was created, so we
256254
// cannot create a witness.
257255
None
@@ -279,7 +277,7 @@ impl<H: Hashable + PartialEq + Clone, C: Ord + Clone + core::fmt::Debug, const D
279277

280278
fn remove_mark(&mut self, position: Position) -> bool {
281279
if self.marks.contains(&position) {
282-
if let Some(c) = self.checkpoints.values_mut().rev().next() {
280+
if let Some(c) = self.checkpoints.values_mut().next_back() {
283281
c.forgotten.insert(position);
284282
} else {
285283
self.marks.remove(&position);
@@ -291,22 +289,43 @@ impl<H: Hashable + PartialEq + Clone, C: Ord + Clone + core::fmt::Debug, const D
291289
}
292290

293291
fn checkpoint(&mut self, id: C) -> bool {
294-
if Some(&id) > self.checkpoints.keys().rev().next() {
292+
if Some(&id) > self.checkpoints.keys().next_back() {
295293
Self::checkpoint(self, id, self.current_position());
296294
true
297295
} else {
298296
false
299297
}
300298
}
301299

302-
fn rewind(&mut self) -> bool {
303-
if let Some((id, c)) = self.checkpoints.iter().rev().next() {
304-
self.leaves.truncate(c.leaves_len);
305-
for pos in c.marked.iter() {
306-
self.marks.remove(pos);
300+
fn checkpoint_count(&self) -> usize {
301+
self.checkpoints.len()
302+
}
303+
304+
fn rewind(&mut self, depth: usize) -> bool {
305+
if self.checkpoints.len() > depth {
306+
let mut to_delete = vec![];
307+
for (idx, (id, c)) in self
308+
.checkpoints
309+
.iter_mut()
310+
.rev()
311+
.enumerate()
312+
.take(depth + 1)
313+
{
314+
for pos in c.marked.iter() {
315+
self.marks.remove(pos);
316+
}
317+
if idx < depth {
318+
to_delete.push(id.clone());
319+
} else {
320+
self.leaves.truncate(c.leaves_len);
321+
c.marked.clear();
322+
c.forgotten.clear();
323+
}
307324
}
308-
let id = id.clone(); // needed to avoid mutable/immutable borrow conflict
309-
self.checkpoints.remove(&id);
325+
for cid in to_delete.iter() {
326+
self.checkpoints.remove(cid);
327+
}
328+
310329
true
311330
} else {
312331
false
@@ -316,8 +335,6 @@ impl<H: Hashable + PartialEq + Clone, C: Ord + Clone + core::fmt::Debug, const D
316335

317336
#[cfg(test)]
318337
mod tests {
319-
use std::convert::TryFrom;
320-
321338
use super::CompleteTree;
322339
use crate::{
323340
check_append, check_checkpoint_rewind, check_rewind_remove_mark, check_root_hashes,
@@ -334,7 +351,7 @@ mod tests {
334351
}
335352

336353
let tree = CompleteTree::<SipHashable, (), DEPTH>::new(100);
337-
assert_eq!(tree.root(0).unwrap(), expected);
354+
assert_eq!(tree.root(None), Some(expected));
338355
}
339356

340357
#[test]
@@ -362,7 +379,7 @@ mod tests {
362379
),
363380
);
364381

365-
assert_eq!(tree.root(0).unwrap(), expected);
382+
assert_eq!(tree.root(None), Some(expected));
366383
}
367384

368385
#[test]
@@ -408,10 +425,12 @@ mod tests {
408425
),
409426
);
410427

411-
assert_eq!(tree.root(0).unwrap(), expected);
428+
assert_eq!(tree.root(None), Some(expected.clone()));
429+
tree.checkpoint((), None);
430+
assert_eq!(tree.root(Some(0)), Some(expected.clone()));
412431

413432
for i in 0u64..(1 << DEPTH) {
414-
let position = Position::try_from(i).unwrap();
433+
let position = Position::from(i);
415434
let path = tree.witness(position, 0).unwrap();
416435
assert_eq!(
417436
compute_root_from_witness(SipHashable(i), position, &path),

0 commit comments

Comments
 (0)