diff --git a/.travis.yml b/.travis.yml index 1a633db..aeaa015 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,14 +56,14 @@ matrix: - libdw-dev - binutils-dev - libiberty-dev - - zlib1g-dev + - zlib1g-dev - env: NAME='clippy' rust: nightly-2018-07-22 before_script: - rustup component add clippy-preview script: - - cargo clippy --all -- -D clippy + - cargo clippy --all --all-features -- -D clippy env: global: diff --git a/src/merkletree.rs b/src/merkletree.rs index c2e3b00..a175448 100644 --- a/src/merkletree.rs +++ b/src/merkletree.rs @@ -160,6 +160,16 @@ impl MerkleTree { .map(|lemma| Proof::new(self.algorithm, root_hash, lemma, value)) } + /// Generate an inclusion proof for the `n`-th leaf value. + pub fn gen_nth_proof(&self, n: usize) -> Option> + where + T: Hashable + Clone, + { + let root_hash = self.root_hash().clone(); + Lemma::new_by_index(&self.root, n, self.count) + .map(|(lemma, value)| Proof::new(self.algorithm, root_hash, lemma, value.clone())) + } + /// Creates an `Iterator` over the values contained in this Merkle tree. pub fn iter(&self) -> LeavesIterator { self.root.iter() diff --git a/src/proof.rs b/src/proof.rs index 2fb7cee..db4136e 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -57,6 +57,7 @@ mod algorithm_serde { } } + #[cfg(test)] mod test { use super::*; use ring::digest::{ @@ -74,13 +75,13 @@ mod algorithm_serde { fn test_serialize_known_algorithms() { extern crate serde_json; - for alg in [SHA1, SHA256, SHA384, SHA512, SHA512_256].iter() { + for alg in &[SHA1, SHA256, SHA384, SHA512, SHA512_256] { let mut serializer = serde_json::Serializer::with_formatter( vec![], serde_json::ser::PrettyFormatter::new(), ); - let _ = serialize(alg, &mut serializer).expect(&format!("{:?}", alg)); + serialize(alg, &mut serializer).expect(&format!("{:?}", alg)); let alg_ = deserialize(&mut serde_json::Deserializer::from_slice( &serializer.into_inner()[..], )).expect(&format!("{:?}", alg)); @@ -153,6 +154,15 @@ impl Proof { self.lemma.validate(self.algorithm) } + + /// Returns the index of this proof's value, given the total number of items in the tree. + /// + /// # Panics + /// + /// Panics if the proof is malformed. Call `validate` first. + pub fn index(&self, count: usize) -> usize { + self.lemma.index(count) + } } /// A `Lemma` holds the hash of a node, the hash of its sibling node, @@ -170,7 +180,8 @@ pub struct Lemma { } impl Lemma { - /// Attempts to generate a proof that the a value with hash `needle` is a member of the given `tree`. + /// Attempts to generate a proof that the a value with hash `needle` is a + /// member of the given `tree`. pub fn new(tree: &Tree, needle: &[u8]) -> Option { match *tree { Tree::Empty { .. } => None, @@ -185,6 +196,74 @@ impl Lemma { } } + /// Attempts to generate a proof that the `idx`-th leaf is a member of + /// the given tree. The `count` must equal the number of leaves in the + /// `tree`. If `idx >= count`, `None` is returned. Otherwise it returns + /// the new `Lemma` and the `idx`-th value. + pub fn new_by_index(tree: &Tree, idx: usize, count: usize) -> Option<(Lemma, &T)> { + if idx >= count { + return None; + } + match *tree { + Tree::Empty { .. } => None, + + Tree::Leaf { + ref hash, + ref value, + .. + } => { + if count != 1 { + return None; + } + let lemma = Lemma { + node_hash: hash.clone(), + sibling_hash: None, + sub_lemma: None, + }; + Some((lemma, value)) + } + + Tree::Node { + ref hash, + ref left, + ref right, + } => { + let left_count = count.next_power_of_two() / 2; + let (sub_lem_val, sibling_hash); + if idx < left_count { + sub_lem_val = Lemma::new_by_index(left, idx, left_count); + sibling_hash = Positioned::Right(right.hash().clone()); + } else { + sub_lem_val = Lemma::new_by_index(right, idx - left_count, count - left_count); + sibling_hash = Positioned::Left(left.hash().clone()); + } + sub_lem_val.map(|(sub_lemma, value)| { + let lemma = Lemma { + node_hash: hash.clone(), + sibling_hash: Some(sibling_hash), + sub_lemma: Some(Box::new(sub_lemma)), + }; + (lemma, value) + }) + } + } + } + + /// Returns the index of this lemma's value, given the total number of items in the tree. + /// + /// # Panics + /// + /// Panics if the lemma is malformed. Call `validate_lemma` first. + pub fn index(&self, count: usize) -> usize { + let left_count = count.next_power_of_two() / 2; + match (self.sub_lemma.as_ref(), self.sibling_hash.as_ref()) { + (None, None) => 0, + (Some(l), Some(&Positioned::Left(_))) => left_count + l.index(count - left_count), + (Some(l), Some(&Positioned::Right(_))) => l.index(left_count), + (None, Some(_)) | (Some(_), None) => panic!("malformed lemma"), + } + } + fn new_leaf_proof(hash: &[u8], needle: &[u8]) -> Option { if *hash == *needle { Some(Lemma { diff --git a/src/tests.rs b/src/tests.rs index 390259f..c1e1c47 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -150,6 +150,26 @@ fn test_wrong_proof() { } } +#[test] +fn test_nth_proof() { + // Calculation depends on the total count. Try a few numbers: odd, even, powers of two... + for &count in &[1, 2, 3, 10, 15, 16, 17, 22] { + let values = (1..(count + 1)).map(|x| vec![x as u8]).collect::>(); + let tree = MerkleTree::from_vec(digest, values.clone()); + let root_hash = tree.root_hash(); + + for i in 0..count { + let proof = tree.gen_nth_proof(i).expect("gen proof by index"); + assert_eq!(vec![i as u8 + 1], proof.value); + assert!(proof.validate(&root_hash)); + assert_eq!(i, proof.index(tree.count())); + } + + assert!(tree.gen_nth_proof(count).is_none()); + assert!(tree.gen_nth_proof(count + 1000).is_none()); + } +} + #[test] fn test_mutate_proof_first_lemma() { let values = (1..10).map(|x| vec![x]).collect::>(); @@ -189,7 +209,7 @@ fn test_tree_iter() { fn test_tree_into_iter() { let values = (1..10).map(|x| vec![x]).collect::>(); let tree = MerkleTree::from_vec(digest, values.clone()); - let iter = tree.iter().cloned().collect::>(); + let iter = tree.into_iter().collect::>(); assert_eq!(values, iter); } diff --git a/tests/proto.rs b/tests/proto.rs index 3ba52d7..52ebcf8 100644 --- a/tests/proto.rs +++ b/tests/proto.rs @@ -39,8 +39,8 @@ pub struct PublicKey { impl PublicKey { pub fn new(zero_values: Vec, one_values: Vec) -> Self { PublicKey { - zero_values: zero_values, - one_values: one_values, + zero_values, + one_values, } }