Skip to content

Commit

Permalink
Merge pull request #130 from nuttycom/permit_invalid_legacy_witnesses
Browse files Browse the repository at this point in the history
Permit invalid legacy witnesses
  • Loading branch information
nuttycom authored Feb 1, 2025
2 parents 61c947b + 2e76b90 commit 561a6dc
Show file tree
Hide file tree
Showing 9 changed files with 50 additions and 28 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 5 additions & 7 deletions incrementalmerkletree-testing/src/complete_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ impl<H: Hashable, C: Clone + Ord + core::fmt::Debug, const DEPTH: u8> CompleteTr
self.mark();
}
Retention::Checkpoint { id, marking } => {
let latest_checkpoint = self.checkpoints.keys().rev().next();
let latest_checkpoint = self.checkpoints.keys().next_back();
if Some(&id) > latest_checkpoint {
append(&mut self.leaves, value, DEPTH)?;
if marking == Marking::Marked {
Expand Down Expand Up @@ -145,7 +145,7 @@ impl<H: Hashable, C: Clone + Ord + core::fmt::Debug, const DEPTH: u8> CompleteTr
if !self.marks.contains(&pos) {
self.marks.insert(pos);

if let Some(checkpoint) = self.checkpoints.values_mut().rev().next() {
if let Some(checkpoint) = self.checkpoints.values_mut().next_back() {
checkpoint.marked.insert(pos);
}
}
Expand Down Expand Up @@ -277,7 +277,7 @@ impl<H: Hashable + PartialEq + Clone, C: Ord + Clone + core::fmt::Debug, const D

fn remove_mark(&mut self, position: Position) -> bool {
if self.marks.contains(&position) {
if let Some(c) = self.checkpoints.values_mut().rev().next() {
if let Some(c) = self.checkpoints.values_mut().next_back() {
c.forgotten.insert(position);
} else {
self.marks.remove(&position);
Expand All @@ -289,7 +289,7 @@ impl<H: Hashable + PartialEq + Clone, C: Ord + Clone + core::fmt::Debug, const D
}

fn checkpoint(&mut self, id: C) -> bool {
if Some(&id) > self.checkpoints.keys().rev().next() {
if Some(&id) > self.checkpoints.keys().next_back() {
Self::checkpoint(self, id, self.current_position());
true
} else {
Expand Down Expand Up @@ -335,8 +335,6 @@ impl<H: Hashable + PartialEq + Clone, C: Ord + Clone + core::fmt::Debug, const D

#[cfg(test)]
mod tests {
use std::convert::TryFrom;

use super::CompleteTree;
use crate::{
check_append, check_checkpoint_rewind, check_rewind_remove_mark, check_root_hashes,
Expand Down Expand Up @@ -432,7 +430,7 @@ mod tests {
assert_eq!(tree.root(Some(0)), Some(expected.clone()));

for i in 0u64..(1 << DEPTH) {
let position = Position::try_from(i).unwrap();
let position = Position::from(i);
let path = tree.witness(position, 0).unwrap();
assert_eq!(
compute_root_from_witness(SipHashable(i), position, &path),
Expand Down
4 changes: 2 additions & 2 deletions incrementalmerkletree-testing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1062,7 +1062,7 @@ pub fn check_checkpoint_rewind<C: TestCheckpoint, T: Tree<String, C>, F: Fn(usiz
}

pub fn check_remove_mark<C: TestCheckpoint, T: Tree<String, C>, F: Fn(usize) -> T>(new_tree: F) {
let samples = vec![
let samples = [
vec![
append_str("a", Retention::Ephemeral),
append_str(
Expand Down Expand Up @@ -1126,7 +1126,7 @@ pub fn check_rewind_remove_mark<C: TestCheckpoint, T: Tree<String, C>, F: Fn(usi
// test framework itself previously did not correctly handle
// chain state restoration.

let samples = vec![
let samples = [
vec![
append_str("x", Retention::Marked),
Checkpoint(C::from_u64(1)),
Expand Down
8 changes: 8 additions & 0 deletions incrementalmerkletree/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ and this project adheres to Rust's notion of

## Unreleased

## [0.8.2] - 2025-01-31

### Added
- `incrementalmerkletree::witness::IncrementalWitness::invalid_empty_witness`
has been under the `test-dependencies` feature flag to permit use testing
against `zcashd` test vectors that depend upon appending nodes to the
(invalid) empty witness.

## [0.8.1] - 2024-12-11

### Changed
Expand Down
2 changes: 1 addition & 1 deletion incrementalmerkletree/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "incrementalmerkletree"
description = "Common types, interfaces, and utilities for Merkle tree data structures"
version = "0.8.1"
version = "0.8.2"
authors = [
"Sean Bowe <[email protected]>",
"Kris Nuttycombe <[email protected]>",
Expand Down
4 changes: 2 additions & 2 deletions incrementalmerkletree/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,13 +466,13 @@ impl Address {
/// Returns the minimum value among the range of leaf positions that are contained within the
/// tree with its root at this address.
pub fn position_range_start(&self) -> Position {
(self.index << self.level.0).try_into().unwrap()
(self.index << self.level.0).into()
}

/// Returns the (exclusive) end of the range of leaf positions that are contained within the
/// tree with its root at this address.
pub fn position_range_end(&self) -> Position {
((self.index + 1) << self.level.0).try_into().unwrap()
((self.index + 1) << self.level.0).into()
}

/// Returns the maximum value among the range of leaf positions that are contained within the
Expand Down
22 changes: 19 additions & 3 deletions incrementalmerkletree/src/witness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,32 @@ impl<H, const DEPTH: u8> IncrementalWitness<H, DEPTH> {
})
}

/// Creates an invalid empty `IncrementalWitness`. This constructor is provided for backwards
/// compatibility with the encodings of `zcashd` `IncrementalWitness` values; that type permits
/// multiple distinct encodings of the same witness state, and in some cases it is necessary to
/// create an empty witness and then append leaves in order to obtain the encoded forms
/// produced by `zcashd` (and which we must reproduce in order to demonstrate interoperability
/// with `zcashd` test vectors.) This should not be used except in a testing context.
#[cfg(feature = "test-dependencies")]
pub fn invalid_empty_witness() -> Self {
Self {
tree: CommitmentTree::empty(),
filled: vec![],
cursor_depth: 0,
cursor: None,
}
}

/// Constructs an `IncrementalWitness` from its parts.
///
/// Returns `None` if the parts do not form a valid witness, for example if `tree` is
/// empty (and thus there is no position to witness).
/// Returns `None` if the parts do not form a valid witness, for example if all of the parts
/// are empty (and thus there is no position to witness).
pub fn from_parts(
tree: CommitmentTree<H, DEPTH>,
filled: Vec<H>,
cursor: Option<CommitmentTree<H, DEPTH>>,
) -> Option<Self> {
(!tree.is_empty()).then(|| {
(!(tree.is_empty() && filled.is_empty() && cursor.is_none())).then(|| {
let mut witness = IncrementalWitness {
tree,
filled,
Expand Down
4 changes: 2 additions & 2 deletions shardtree/src/store/caching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1034,7 +1034,7 @@ mod tests {

#[test]
fn check_remove_mark() {
let samples = vec![
let samples = [
vec![
append_str("a", Retention::Ephemeral),
append_str(
Expand Down Expand Up @@ -1128,7 +1128,7 @@ mod tests {
// test framework itself previously did not correctly handle
// chain state restoration.

let samples = vec![
let samples = [
vec![
append_str("x", Retention::Marked),
Checkpoint(1),
Expand Down
20 changes: 10 additions & 10 deletions shardtree/src/testing.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::convert::TryFrom;

use assert_matches::assert_matches;
use proptest::bool::weighted;
use proptest::collection::vec;
Expand Down Expand Up @@ -96,17 +94,19 @@ where
})
}

/// A random shardtree of size up to 2^6 with shards of size 2^3, along with vectors of the
/// checkpointed and marked positions within the tree.
type ArbShardtreeParts<H> = (
ShardTree<MemoryShardStore<<H as Strategy>::Value, usize>, 6, 3>,
Vec<Position>,
Vec<Position>,
);

/// Constructs a random shardtree of size up to 2^6 with shards of size 2^3. Returns the tree,
/// along with vectors of the checkpoint and mark positions.
/// along with vectors of the checkpointed and marked positions.
pub fn arb_shardtree<H: Strategy + Clone>(
arb_leaf: H,
) -> impl Strategy<
Value = (
ShardTree<MemoryShardStore<H::Value, usize>, 6, 3>,
Vec<Position>,
Vec<Position>,
),
>
) -> impl Strategy<Value = ArbShardtreeParts<H>>
where
H::Value: Hashable + Clone + PartialEq,
{
Expand Down

0 comments on commit 561a6dc

Please sign in to comment.