This commit is contained in:
Romain Ruetschi
2018-05-15 18:17:36 +02:00
parent c647bf82df
commit 5e3ed116f1
9 changed files with 93 additions and 108 deletions

View File

@ -1,15 +1,14 @@
#![feature(test)]
#![feature(rand)]
extern crate test;
extern crate rand;
extern crate test;
extern crate merkle;
extern crate ring;
use test::Bencher;
use rand::Rng;
use test::Bencher;
use ring::digest::{Algorithm, SHA512};
@ -30,9 +29,11 @@ fn bench_small_str_proof_gen(b: &mut Bencher) {
let values = vec!["one", "two", "three", "four"];
let tree = MerkleTree::from_vec(digest, values.clone());
b.iter(|| for value in &values {
let proof = tree.gen_proof(value);
test::black_box(proof);
b.iter(|| {
for value in &values {
let proof = tree.gen_proof(value);
test::black_box(proof);
}
});
}
@ -45,8 +46,10 @@ fn bench_small_str_proof_check(b: &mut Bencher) {
.map(|v| tree.gen_proof(v).unwrap())
.collect::<Vec<_>>();
b.iter(|| for proof in &proofs {
test::black_box(proof.validate(tree.root_hash()));
b.iter(|| {
for proof in &proofs {
test::black_box(proof.validate(tree.root_hash()));
}
});
}
@ -76,9 +79,11 @@ fn bench_big_rnd_proof_gen(b: &mut Bencher) {
let tree = MerkleTree::from_vec(digest, values.clone());
b.iter(|| for value in &values {
let proof = tree.gen_proof(value.clone());
test::black_box(proof);
b.iter(|| {
for value in &values {
let proof = tree.gen_proof(value.clone());
test::black_box(proof);
}
});
}
@ -97,8 +102,10 @@ fn bench_big_rnd_proof_check(b: &mut Bencher) {
.map(|v| tree.gen_proof(v).unwrap())
.collect::<Vec<_>>();
b.iter(|| for proof in &proofs {
test::black_box(proof.validate(tree.root_hash()));
b.iter(|| {
for proof in &proofs {
test::black_box(proof.validate(tree.root_hash()));
}
});
}
@ -113,7 +120,9 @@ fn bench_big_rnd_iter(b: &mut Bencher) {
let tree = MerkleTree::from_vec(digest, values.clone());
b.iter(|| for value in &tree {
test::black_box(value);
b.iter(|| {
for value in &tree {
test::black_box(value);
}
});
}

View File

@ -1,5 +1,4 @@
use ring::digest::{Algorithm, Context, Digest, digest};
use ring::digest::{digest, Algorithm, Context, Digest};
/// The type of values stored in a `MerkleTree` must implement
/// this trait, in order for them to be able to be fed

View File

@ -1,10 +1,6 @@
#![deny(
missing_docs, unused_qualifications,
missing_debug_implementations, missing_copy_implementations,
trivial_casts, trivial_numeric_casts,
unsafe_code, unstable_features,
unused_import_braces
)]
#![deny(missing_docs, unused_qualifications, missing_debug_implementations,
missing_copy_implementations, trivial_casts, trivial_numeric_casts, unsafe_code,
unstable_features, unused_import_braces)]
//! *merkle* implements a Merkle Tree in Rust.
@ -29,7 +25,7 @@ mod hashutils;
pub use hashutils::Hashable;
mod tree;
pub use tree::{LeavesIterator, LeavesIntoIterator};
pub use tree::{LeavesIntoIterator, LeavesIterator};
#[cfg(feature = "serialization-protobuf")]
#[allow(unused_qualifications)]

View File

@ -1,13 +1,12 @@
use std::hash::{Hash, Hasher};
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
use ring::digest::Algorithm;
use tree::{Tree, LeavesIterator, LeavesIntoIterator};
use hashutils::{Hashable, HashUtils};
use hashutils::{HashUtils, Hashable};
use tree::{LeavesIntoIterator, LeavesIterator, Tree};
use proof::{Proof, Lemma};
use proof::{Lemma, Proof};
/// A Merkle tree is a binary tree, with values of type `T` at the leafs,
/// and where every internal node holds the hash of the concatenation of the hashes of its children nodes.
@ -29,8 +28,8 @@ pub struct MerkleTree<T> {
impl<T: PartialEq> PartialEq for MerkleTree<T> {
#[allow(trivial_casts)]
fn eq(&self, other: &MerkleTree<T>) -> bool {
self.root == other.root && self.height == other.height && self.count == other.count &&
(self.algorithm as *const Algorithm) == (other.algorithm as *const Algorithm)
self.root == other.root && self.height == other.height && self.count == other.count
&& (self.algorithm as *const Algorithm) == (other.algorithm as *const Algorithm)
}
}
@ -48,10 +47,7 @@ impl<T: Ord> Ord for MerkleTree<T> {
self.height
.cmp(&other.height)
.then(self.count.cmp(&other.count))
.then((self.algorithm as *const Algorithm).cmp(
&(other.algorithm as
*const Algorithm),
))
.then((self.algorithm as *const Algorithm).cmp(&(other.algorithm as *const Algorithm)))
.then_with(|| self.root.cmp(&other.root))
}
}
@ -73,7 +69,6 @@ impl<T> MerkleTree<T> {
where
T: Hashable,
{
if values.is_empty() {
return MerkleTree {
algorithm: algorithm,
@ -156,13 +151,11 @@ impl<T> MerkleTree<T> {
where
T: Hashable,
{
let root_hash = self.root_hash().clone();
let leaf_hash = self.algorithm.hash_leaf(&value);
Lemma::new(&self.root, leaf_hash.as_ref()).map(|lemma| {
Proof::new(self.algorithm, root_hash, lemma, value)
})
Lemma::new(&self.root, leaf_hash.as_ref())
.map(|lemma| Proof::new(self.algorithm, root_hash, lemma, value))
}
/// Creates an `Iterator` over the values contained in this Merkle tree.

View File

@ -1,11 +1,10 @@
use std::hash::{Hash, Hasher};
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
use ring::digest::Algorithm;
use tree::Tree;
use hashutils::HashUtils;
use tree::Tree;
/// An inclusion proof represent the fact that a `value` is a member
/// of a `MerkleTree` with root hash `root_hash`, and hash function `algorithm`.
@ -29,11 +28,13 @@ pub struct Proof<T> {
#[cfg(feature = "serialization-serde")]
mod algorithm_serde {
use ring::digest::{self, Algorithm};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub fn serialize<S: Serializer>(algorithm: &&'static Algorithm, se: S)
-> Result<S::Ok, S::Error> {
pub fn serialize<S: Serializer>(
algorithm: &&'static Algorithm,
se: S,
) -> Result<S::Ok, S::Error> {
// The `Debug` implementation of `Algorithm` prints its ID.
format!("{:?}", algorithm).serialize(se)
}
@ -104,32 +105,27 @@ impl<T> Proof<T> {
fn validate_lemma(&self, lemma: &Lemma) -> bool {
match lemma.sub_lemma {
None => lemma.sibling_hash.is_none(),
Some(ref sub) => {
match lemma.sibling_hash {
None => false,
Some(Positioned::Left(ref hash)) => {
let combined = self.algorithm.hash_nodes(hash, &sub.node_hash);
let hashes_match = combined.as_ref() == lemma.node_hash.as_slice();
hashes_match && self.validate_lemma(sub)
}
Some(Positioned::Right(ref hash)) => {
let combined = self.algorithm.hash_nodes(&sub.node_hash, hash);
let hashes_match = combined.as_ref() == lemma.node_hash.as_slice();
hashes_match && self.validate_lemma(sub)
}
Some(ref sub) => match lemma.sibling_hash {
None => false,
Some(Positioned::Left(ref hash)) => {
let combined = self.algorithm.hash_nodes(hash, &sub.node_hash);
let hashes_match = combined.as_ref() == lemma.node_hash.as_slice();
hashes_match && self.validate_lemma(sub)
}
}
Some(Positioned::Right(ref hash)) => {
let combined = self.algorithm.hash_nodes(&sub.node_hash, hash);
let hashes_match = combined.as_ref() == lemma.node_hash.as_slice();
hashes_match && self.validate_lemma(sub)
}
},
}
}
}
/// A `Lemma` holds the hash of a node, the hash of its sibling node,
/// and a sub lemma, whose `node_hash`, when combined with this `sibling_hash`
/// must be equal to this `node_hash`.
@ -189,12 +185,10 @@ impl Lemma {
(lemma, sub_lemma)
})
})
.map(|(sub_lemma, sibling_hash)| {
Lemma {
node_hash: hash.into(),
sibling_hash: sibling_hash,
sub_lemma: Some(Box::new(sub_lemma)),
}
.map(|(sub_lemma, sibling_hash)| Lemma {
node_hash: hash.into(),
sibling_hash: sibling_hash,
sub_lemma: Some(Box::new(sub_lemma)),
})
}
}

View File

@ -1,10 +1,9 @@
mod proof;
use ring::digest::Algorithm;
use proof::{Proof, Lemma, Positioned};
pub use self::proof::{ProofProto, LemmaProto};
pub use self::proof::{LemmaProto, ProofProto};
use proof::{Lemma, Positioned, Proof};
use protobuf::Message;
use protobuf::error::ProtobufResult;
@ -16,7 +15,6 @@ impl<T> Proof<T> {
where
T: From<Vec<u8>>,
{
proto.into_proof(algorithm)
}
@ -25,7 +23,6 @@ impl<T> Proof<T> {
where
T: Into<Vec<u8>>,
{
ProofProto::from_proof(self)
}
@ -37,7 +34,6 @@ impl<T> Proof<T> {
where
T: From<Vec<u8>>,
{
parse_from_bytes::<ProofProto>(bytes).map(|proto| proto.into_proof(algorithm))
}
@ -46,7 +42,6 @@ impl<T> Proof<T> {
where
T: Into<Vec<u8>>,
{
self.into_protobuf().write_to_bytes()
}
}
@ -56,7 +51,6 @@ impl ProofProto {
where
T: Into<Vec<u8>>,
{
let mut proto = Self::new();
match proof {
@ -79,7 +73,6 @@ impl ProofProto {
where
T: From<Vec<u8>>,
{
if self.root_hash.is_empty() || !self.has_lemma() {
return None;
}
@ -143,12 +136,10 @@ impl LemmaProto {
// If a `sub_lemma` is present is the Protobuf,
// then we expect it to unserialize to a valid `Lemma`,
// otherwise we return `None`
self.take_sub_lemma().into_lemma().map(|sub_lemma| {
Lemma {
node_hash: node_hash,
sibling_hash: sibling_hash,
sub_lemma: Some(Box::new(sub_lemma)),
}
self.take_sub_lemma().into_lemma().map(|sub_lemma| Lemma {
node_hash: node_hash,
sibling_hash: sibling_hash,
sub_lemma: Some(Box::new(sub_lemma)),
})
} else {
// We might very well not have a sub_lemma,

View File

@ -1,10 +1,9 @@
#![cfg(test)]
use ring::digest::{Algorithm, Context, SHA512};
use hashutils::{HashUtils, Hashable};
use merkletree::MerkleTree;
use hashutils::{Hashable, HashUtils};
use proof::Positioned;
#[allow(non_upper_case_globals)]
@ -34,7 +33,6 @@ fn test_from_str_vec() {
assert_eq!(tree.root_hash().as_slice(), root_hash.as_ref());
}
#[test]
fn test_from_vec_empty() {
let values: Vec<Vec<u8>> = vec![];
@ -57,7 +55,6 @@ fn test_from_vec1() {
assert_eq!(tree.root_hash().as_slice(), root_hash.as_ref());
}
#[test]
fn test_from_vec3() {
let values = vec![vec![1], vec![2], vec![3]];
@ -228,7 +225,6 @@ fn test_tree_into_iter_loop_borrow() {
assert_eq!(refs, collected);
}
pub struct PublicKey {
zero_values: Vec<Vec<u8>>,
one_values: Vec<Vec<u8>>,
@ -243,13 +239,13 @@ impl PublicKey {
}
pub fn to_bytes(&self) -> Vec<u8> {
self.zero_values.iter().chain(self.one_values.iter()).fold(
Vec::new(),
|mut acc, i| {
self.zero_values
.iter()
.chain(self.one_values.iter())
.fold(Vec::new(), |mut acc, i| {
acc.append(&mut i.clone());
acc
},
)
})
}
}
@ -285,5 +281,8 @@ fn test_serialize_proof_with_serde() {
let tree = MerkleTree::from_vec(digest, values);
let proof = tree.gen_proof(vec![5]);
let serialized = serde_json::to_string(&proof).expect("serialize proof");
assert_eq!(proof, serde_json::from_str(&serialized).expect("deserialize proof"));
assert_eq!(
proof,
serde_json::from_str(&serialized).expect("deserialize proof")
);
}

View File

@ -1,16 +1,20 @@
use ring::digest::{Algorithm, Digest};
use hashutils::{Hashable, HashUtils};
use hashutils::{HashUtils, Hashable};
pub use proof::{Proof, Lemma, Positioned};
pub use proof::{Lemma, Positioned, Proof};
/// Binary Tree where leaves hold a stand-alone value.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Tree<T> {
Empty { hash: Vec<u8> },
Empty {
hash: Vec<u8>,
},
Leaf { hash: Vec<u8>, value: T },
Leaf {
hash: Vec<u8>,
value: T,
},
Node {
hash: Vec<u8>,
@ -22,7 +26,9 @@ pub enum Tree<T> {
impl<T> Tree<T> {
/// Create an empty tree
pub fn empty(hash: Digest) -> Self {
Tree::Empty { hash: hash.as_ref().into() }
Tree::Empty {
hash: hash.as_ref().into(),
}
}
/// Create a new tree
@ -38,7 +44,6 @@ impl<T> Tree<T> {
where
T: Hashable,
{
let hash = algo.hash_leaf(&value);
Tree::new(hash, value)
}

View File

@ -1,13 +1,12 @@
#![cfg(feature = "serialization-protobuf")]
#![cfg(feature="serialization-protobuf")]
extern crate ring;
extern crate merkle;
extern crate protobuf;
extern crate ring;
use ring::digest::{Algorithm, Context, SHA512};
use merkle::{MerkleTree, Proof, Hashable};
use merkle::{Hashable, MerkleTree, Proof};
#[allow(non_upper_case_globals)]
static digest: &'static Algorithm = &SHA512;