Implement Containers (Dictionary,Set,Array) on top of MerkleTree. And restructure the code. (#55)

* Implement Containers (Dictionary,Set,Array) on top of MerkleTree. And restructure the code.

- Reorganize the code grouping backends, middleware, frontend, (crypto) primitives.
- Add types Dictionary,Set,Array at the middleware layer, so that
  it can be used both by the backend and frontend. The Dictionary, Set,
  Array use the merkletree differently as specified at
f2575d1524/book/src/values.md (dictionary-array-set)
	- The containers introduce the trait Container, which has the
	  method 'cm()'. At the current version this uses a merkletree
	  under the hood, and the method 'cm' returns the merkle root.
- Ideally neither frontend nor backend use the MerkleTree type, and they
  use the wrappers {Dictionary,Set,Array}. Note that the current commit
  the MerkleTree is used at the mock-backend to check internal values, but
  not at the struct types.
- updated the spec's merkletree section updating the defined interface
- add github ci to run the tests

---------

Co-authored-by: Ahmad Afuni <root@ahmadafuni.com>
Co-authored-by: Eduard S. <eduardsanou@posteo.net>
This commit is contained in:
arnaucube 2025-02-12 12:06:40 +01:00 committed by GitHub
parent f2575d1524
commit bb865a4fea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 330 additions and 94 deletions

21
.github/workflows/tests.yml vendored Normal file
View file

@ -0,0 +1,21 @@
name: Rust Tests
on:
pull_request:
branches: [ main ]
push:
branches: [ main ]
jobs:
test:
if: github.event.pull_request.draft == false
name: Rust tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Run tests
run: cargo test

View file

@ -145,16 +145,18 @@ For the current use cases, we don't need to prove that the key exists but the va
```rust
impl MerkleTree {
/// builds a new `MerkleTree` where the leaves contain the given key-values
fn new(kvs: HashMap<Value, Value>) -> Self;
/// returns the root of the tree
fn root(&self) -> Result<Hash>;
fn root(&self) -> Hash;
/// returns the value at the given key
fn get(&self, key: &Value) -> Result<Value>;
/// returns a boolean indicating whether the key exists in the tree
fn contains(&self, key: &Value) -> bool;
/// returns a proof of existence, which proves that the given key exists in
/// the tree. It returns the `value` of the leaf at the given `key`, and
/// the `MerkleProof`.
fn prove(&self, key: &Value) -> Result<(Value, MerkleProof)>;
/// the tree. It returns the `MerkleProof`.
fn prove(&self, key: &Value) -> Result<MerkleProof>;
/// returns a proof of non-existence, which proves that the given `key`
/// does not exist in the tree

View file

@ -51,14 +51,15 @@ The array, set and dictionary types are similar types. While all of them use [a
- `leaf.key=hash(original_key)`
- `leaf.value=hash(original_value)`
- **array**: the elements are placed at the value field of each leaf, and the key field is just the array index (integer)
- `leaf.value=original_value`
- `leaf.key=i`
- `leaf.value=original_value`
- **set**: the value field of the leaf is unused, and the key contains the hash of the element
- `leaf.key=hash(original_value)`
- `leaf.value=0`
In the three types, the merkletree under the hood allows to prove inclusion & non-inclusion of the particular entry of the {dictionary/array/set} element.
A concrete implementation of dictionary, array, set can be found at [pod2/src/middleware/containers.rs](https://github.com/0xPARC/pod2/blob/main/src/middleware/containers.rs).
<br><br>

View file

@ -9,7 +9,6 @@ use plonky2::hash::poseidon::PoseidonHash;
use plonky2::plonk::config::Hasher;
use std::any::Any;
use std::fmt;
use std::io::{self, Write};
pub struct MockProver {}

View file

@ -1,7 +1,8 @@
use crate::merkletree::MerkleTree;
use crate::middleware::{
hash_str, Hash, Params, PodId, PodSigner, PodType, SignedPod, Value, KEY_SIGNER, KEY_TYPE,
containers::Dictionary, hash_str, Hash, Params, PodId, PodSigner, PodType, SignedPod, Value,
KEY_SIGNER, KEY_TYPE,
};
use crate::primitives::merkletree::MerkleTree;
use anyhow::Result;
use std::any::Any;
use std::collections::HashMap;
@ -17,10 +18,14 @@ impl PodSigner for MockSigner {
kvs.insert(hash_str(&KEY_SIGNER), Value(pk_hash.0));
kvs.insert(hash_str(&KEY_TYPE), Value::from(PodType::MockSigned));
let mt = MerkleTree::new(&kvs);
let id = PodId(mt.root()?);
let dict = Dictionary::new(&kvs);
let id = PodId(dict.commitment());
let signature = format!("{}_signed_by_{}", id, pk_hash);
Ok(Box::new(MockSignedPod { mt, id, signature }))
Ok(Box::new(MockSignedPod {
dict,
id,
signature,
}))
}
}
@ -28,30 +33,37 @@ impl PodSigner for MockSigner {
pub struct MockSignedPod {
id: PodId,
signature: String,
mt: MerkleTree,
dict: Dictionary,
}
impl SignedPod for MockSignedPod {
fn verify(&self) -> bool {
// Verify type
if Some(&Value::from(PodType::MockSigned)) != self.mt.kvs().get(&hash_str(&KEY_TYPE)) {
let value_at_type = match self.dict.get(&hash_str(&KEY_TYPE).into()) {
Ok(v) => v,
Err(_) => return false,
};
if Value::from(PodType::MockSigned) != value_at_type {
return false;
}
// Verify id
let mt = MerkleTree::new(&self.mt.kvs());
let id = match mt.root() {
Ok(id) => PodId(id),
Err(_) => return false,
};
let mt = MerkleTree::new(
&self
.dict
.iter()
.map(|(&k, &v)| (k, v))
.collect::<HashMap<Value, Value>>(),
);
let id = PodId(mt.root());
if id != self.id {
return false;
}
// Verify signature
let pk_hash = match self.mt.kvs().get(&hash_str(&KEY_SIGNER)) {
Some(v) => v,
None => return false,
let pk_hash = match self.dict.get(&hash_str(&KEY_SIGNER).into()) {
Ok(v) => v,
Err(_) => return false,
};
let signature = format!("{}_signed_by_{}", id, pk_hash);
if signature != self.signature {
@ -66,7 +78,10 @@ impl SignedPod for MockSignedPod {
}
fn kvs(&self) -> HashMap<Hash, Value> {
self.mt.kvs().clone()
self.dict
.into_iter()
.map(|(&k, &v)| (Hash(k.0), v))
.collect()
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
@ -108,15 +123,23 @@ pub mod tests {
let mut bad_pod = pod.clone();
let mut bad_kvs = bad_pod.kvs();
bad_kvs.insert(hash_str(KEY_SIGNER), Value(PodId(NULL).0 .0));
let bad_mt = MerkleTree::new(&bad_kvs);
bad_pod.mt = bad_mt;
let bad_kvs_mt = &bad_kvs
.into_iter()
.map(|(k, v)| (Value(k.0), v))
.collect::<HashMap<Value, Value>>();
let bad_mt = MerkleTree::new(&bad_kvs_mt);
bad_pod.dict.mt = bad_mt;
assert_eq!(bad_pod.verify(), false);
let mut bad_pod = pod.clone();
let mut bad_kvs = bad_pod.kvs();
bad_kvs.insert(hash_str(KEY_TYPE), Value::from(0));
let bad_mt = MerkleTree::new(&bad_kvs);
bad_pod.mt = bad_mt;
let bad_kvs_mt = &bad_kvs
.into_iter()
.map(|(k, v)| (Value(k.0), v))
.collect::<HashMap<Value, Value>>();
let bad_mt = MerkleTree::new(&bad_kvs_mt);
bad_pod.dict.mt = bad_mt;
assert_eq!(bad_pod.verify(), false);
}
}

View file

@ -1,5 +1,7 @@
use crate::frontend::{MainPodBuilder, MerkleTree, SignedPod, SignedPodBuilder, Value};
use crate::middleware::{Params, PodType, KEY_SIGNER, KEY_TYPE};
use std::collections::HashMap;
use crate::frontend::{MainPodBuilder, SignedPod, SignedPodBuilder, Value};
use crate::middleware::{containers::Dictionary, Params, PodType, KEY_SIGNER, KEY_TYPE};
use crate::op;
// ZuKYC
@ -22,7 +24,7 @@ pub fn zu_kyc_pod_builder(
gov_id: &SignedPod,
pay_stub: &SignedPod,
) -> MainPodBuilder {
let sanction_list = Value::MerkleTree(MerkleTree { root: 1 });
let sanction_list = Value::Dictionary(Dictionary::new(&HashMap::new())); // empty dictionary
let now_minus_18y: i64 = 1169909388;
let now_minus_1y: i64 = 1706367566;
@ -178,7 +180,7 @@ pub fn great_boy_pod_full_flow() -> MainPodBuilder {
alice_friend_pods.push(friend.sign(&mut bob_signer).unwrap());
alice_friend_pods.push(friend.sign(&mut charlie_signer).unwrap());
let good_boy_issuers_mt = Value::MerkleTree(MerkleTree { root: 33 });
let good_boy_issuers_dict = Value::Dictionary(Dictionary::new(&HashMap::new())); // empty
great_boy_pod_builder(
&params,
[
@ -188,7 +190,7 @@ pub fn great_boy_pod_full_flow() -> MainPodBuilder {
&charlie_good_boys[1],
],
[&alice_friend_pods[0], &alice_friend_pods[1]],
&good_boy_issuers_mt,
&good_boy_issuers_dict,
alice,
)
}

View file

@ -3,14 +3,15 @@
use anyhow::Result;
use itertools::Itertools;
use plonky2::field::types::Field;
use std::collections::HashMap;
use std::convert::From;
use std::fmt;
use crate::middleware::{
self, hash_str, Hash, MainPodInputs, NativeOperation, NativeStatement, Params, PodId,
PodProver, PodSigner, F, SELF,
self,
containers::{Array, Dictionary, Set},
hash_str, Hash, MainPodInputs, NativeOperation, NativeStatement, Params, PodId, PodProver,
PodSigner, SELF,
};
/// This type is just for presentation purposes.
@ -25,16 +26,13 @@ pub enum PodClass {
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct Origin(pub PodClass, pub PodId);
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MerkleTree {
pub root: u8, // TODO
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Value {
String(String),
Int(i64),
MerkleTree(MerkleTree),
Dictionary(Dictionary),
Set(Set),
Array(Array),
}
impl From<&str> for Value {
@ -54,13 +52,9 @@ impl From<&Value> for middleware::Value {
match v {
Value::String(s) => middleware::Value(hash_str(s).0),
Value::Int(v) => middleware::Value::from(*v),
// TODO
Value::MerkleTree(mt) => middleware::Value([
F::from_canonical_u64(mt.root as u64),
F::ZERO,
F::ZERO,
F::ZERO,
]),
Value::Dictionary(d) => middleware::Value(d.commitment().0),
Value::Set(s) => middleware::Value(s.commitment().0),
Value::Array(a) => middleware::Value(a.commitment().0),
}
}
}
@ -70,7 +64,9 @@ impl fmt::Display for Value {
match self {
Value::String(s) => write!(f, "\"{}\"", s),
Value::Int(v) => write!(f, "{}", v),
Value::MerkleTree(mt) => write!(f, "mt:{}", mt.root),
Value::Dictionary(d) => write!(f, "dict:{}", d.commitment()),
Value::Set(s) => write!(f, "set:{}", s.commitment()),
Value::Array(a) => write!(f, "arr:{}", a.commitment()),
}
}
}

View file

@ -1,7 +1,7 @@
pub mod backends;
pub mod frontend;
pub mod merkletree;
pub mod middleware;
pub mod primitives;
#[cfg(test)]
pub mod examples;

View file

@ -1,3 +0,0 @@
fn main() {
println!("Hello, world!");
}

View file

@ -0,0 +1,159 @@
/// This file implements the types defined at
/// https://0xparc.github.io/pod2/values.html#dictionary-array-set .
use anyhow::Result;
use plonky2::hash::poseidon::PoseidonHash;
use plonky2::plonk::config::Hasher;
use std::collections::HashMap;
use super::{Hash, Value, EMPTY};
use crate::primitives::merkletree::{MerkleProof, MerkleTree};
/// Dictionary: the user original keys and values are hashed to be used in the leaf.
/// leaf.key=hash(original_key)
/// leaf.value=hash(original_value)
#[derive(Clone, Debug)]
pub struct Dictionary {
// exposed with pub(crate) so that it can be modified at tests
pub(crate) mt: MerkleTree,
}
impl Dictionary {
pub fn new(kvs: &HashMap<Hash, Value>) -> Self {
let kvs: HashMap<Value, Value> = kvs.into_iter().map(|(&k, &v)| (Value(k.0), v)).collect();
Self {
mt: MerkleTree::new(&kvs),
}
}
pub fn commitment(&self) -> Hash {
self.mt.root()
}
pub fn get(&self, key: &Value) -> Result<Value> {
self.mt.get(key)
}
pub fn prove(&self, key: &Value) -> Result<MerkleProof> {
self.mt.prove(key)
}
pub fn prove_nonexistence(&self, key: &Value) -> Result<MerkleProof> {
self.mt.prove_nonexistence(key)
}
pub fn verify(root: Hash, proof: &MerkleProof, key: &Value, value: &Value) -> Result<()> {
MerkleTree::verify(root, proof, key, value)
}
pub fn verify_nonexistence(root: Hash, proof: &MerkleProof, key: &Value) -> Result<()> {
MerkleTree::verify_nonexistence(root, proof, key)
}
pub fn iter(&self) -> std::collections::hash_map::Iter<Value, Value> {
self.mt.iter()
}
}
impl<'a> IntoIterator for &'a Dictionary {
type Item = (&'a Value, &'a Value);
type IntoIter = std::collections::hash_map::Iter<'a, Value, Value>;
fn into_iter(self) -> Self::IntoIter {
self.mt.iter()
}
}
impl PartialEq for Dictionary {
fn eq(&self, other: &Self) -> bool {
self.mt.root() == other.mt.root()
}
}
impl Eq for Dictionary {}
/// Set: the value field of the leaf is unused, and the key contains the hash of the element.
/// leaf.key=hash(original_value)
/// leaf.value=0
#[derive(Clone, Debug)]
pub struct Set {
mt: MerkleTree,
}
impl Set {
pub fn new(set: &Vec<Value>) -> Self {
let kvs: HashMap<Value, Value> = set
.into_iter()
.map(|e| {
let h = PoseidonHash::hash_no_pad(&e.0).elements;
(Value(h), EMPTY)
})
.collect();
Self {
mt: MerkleTree::new(&kvs),
}
}
pub fn commitment(&self) -> Hash {
self.mt.root()
}
pub fn contains(&self, value: &Value) -> bool {
self.mt.contains(value)
}
pub fn prove(&self, value: &Value) -> Result<MerkleProof> {
self.mt.prove(value)
}
pub fn prove_nonexistence(&self, value: &Value) -> Result<MerkleProof> {
self.mt.prove_nonexistence(value)
}
pub fn verify(root: Hash, proof: &MerkleProof, value: &Value) -> Result<()> {
MerkleTree::verify(root, proof, value, &EMPTY)
}
pub fn verify_nonexistence(root: Hash, proof: &MerkleProof, value: &Value) -> Result<()> {
MerkleTree::verify_nonexistence(root, proof, value)
}
pub fn iter(&self) -> std::collections::hash_map::Iter<Value, Value> {
self.mt.iter()
}
}
impl PartialEq for Set {
fn eq(&self, other: &Self) -> bool {
self.mt.root() == other.mt.root()
}
}
impl Eq for Set {}
/// Array: the elements are placed at the value field of each leaf, and the key field is just the
/// array index (integer).
/// leaf.key=i
/// leaf.value=original_value
#[derive(Clone, Debug)]
pub struct Array {
mt: MerkleTree,
}
impl Array {
pub fn new(array: &Vec<Value>) -> Self {
let kvs: HashMap<Value, Value> = array
.into_iter()
.enumerate()
.map(|(i, &e)| (Value::from(i as i64), e))
.collect();
Self {
mt: MerkleTree::new(&kvs),
}
}
pub fn commitment(&self) -> Hash {
self.mt.root()
}
pub fn get(&self, i: usize) -> Result<Value> {
self.mt.get(&Value::from(i as i64))
}
pub fn prove(&self, i: usize) -> Result<MerkleProof> {
self.mt.prove(&Value::from(i as i64))
}
pub fn verify(root: Hash, proof: &MerkleProof, i: usize, value: &Value) -> Result<()> {
MerkleTree::verify(root, proof, &Value::from(i as i64), value)
}
pub fn iter(&self) -> std::collections::hash_map::Iter<Value, Value> {
self.mt.iter()
}
}
impl PartialEq for Array {
fn eq(&self, other: &Self) -> bool {
self.mt.root() == other.mt.root()
}
}
impl Eq for Array {}

View file

@ -12,9 +12,11 @@ use plonky2::plonk::config::{Hasher, PoseidonGoldilocksConfig};
use std::any::Any;
use std::cmp::{Ord, Ordering};
use std::collections::HashMap;
use std::{array, fmt};
use std::fmt;
use strum_macros::FromRepr;
pub mod containers;
pub const KEY_SIGNER: &str = "_signer";
pub const KEY_TYPE: &str = "_type";
pub const STATEMENT_ARG_F_LEN: usize = 8;
@ -57,6 +59,12 @@ impl From<i64> for Value {
}
}
impl From<Hash> for Value {
fn from(h: Hash) -> Self {
Value(h.0)
}
}
impl TryInto<i64> for Value {
type Error = Error;
fn try_into(self) -> std::result::Result<i64, Self::Error> {
@ -109,6 +117,7 @@ impl PartialOrd for Hash {
}
}
pub const EMPTY: Value = Value([F::ZERO, F::ZERO, F::ZERO, F::ZERO]);
pub const NULL: Hash = Hash([F::ZERO, F::ZERO, F::ZERO, F::ZERO]);
impl fmt::Display for Hash {

View file

@ -20,19 +20,22 @@ use crate::middleware::{Hash, Value, C, D, F};
const CAP_HEIGHT: usize = 0;
/// MerkleTree currently implements the MerkleTree interface with a wrapper on top of Plonky2's
/// MerkleTree. A future iteration will replace it by the MerkleTree specified at
/// https://0xparc.github.io/pod2/merkletree.html .
/// MerkleTree currently is a wrapper on top of Plonky2's MerkleTree. A future iteration will
/// replace it by the MerkleTree specified at https://0xparc.github.io/pod2/merkletree.html .
#[derive(Clone, Debug)]
pub struct MerkleTree {
tree: PlonkyMerkleTree<F, <C as GenericConfig<D>>::Hasher>,
// keyindex: key -> index mapping. This is just for the current plonky-tree wrapper
keyindex: HashMap<Hash, usize>,
keyindex: HashMap<Value, usize>,
// kvs are a field in the MerkleTree in order to be able to iterate over the keyvalues. This is
// specific of the current implementation (Plonky2's tree wrapper), in the next iteration this
// will not be needed since the tree implementation itself will offer the hashmap
// functionality.
kvs: HashMap<Hash, Value>,
pub kvs: HashMap<Value, Value>,
// leaves_map is a map between the leaf (leaf=Hash(key,value)) and the actual (key, value). It
// is used to get the actual value from a leaf for a given key (through the method
// `MerkleTree.get`.
leaves_map: HashMap<Hash, (Value, Value)>,
}
pub struct MerkleProof {
@ -42,9 +45,11 @@ pub struct MerkleProof {
}
impl MerkleTree {
pub fn new(kvs: &HashMap<Hash, Value>) -> Self {
let mut keyindex: HashMap<Hash, usize> = HashMap::new();
/// builds a new `MerkleTree` where the leaves contain the given key-values
pub fn new(kvs: &HashMap<Value, Value>) -> Self {
let mut keyindex: HashMap<Value, usize> = HashMap::new();
let mut leaves: Vec<Vec<F>> = Vec::new();
let mut leaves_map: HashMap<Hash, (Value, Value)> = HashMap::new();
// Note: current version iterates sorting by keys of the kvs, but the merkletree defined at
// https://0xparc.github.io/pod2/merkletree.html will not need it since it will be
// deterministic based on the keys values not on the order of the keys when added into the
@ -54,6 +59,7 @@ impl MerkleTree {
let leaf = PoseidonHash::hash_no_pad(&input).elements;
leaves.push(leaf.into());
keyindex.insert(*k, i);
leaves_map.insert(Hash(leaf), (*k, *v));
}
// pad to a power of two if needed
@ -67,17 +73,40 @@ impl MerkleTree {
tree,
keyindex,
kvs: kvs.clone(),
leaves_map,
}
}
}
pub fn root(&self) -> Result<Hash> {
impl MerkleTree {
/// returns the root of the tree
pub fn root(&self) -> Hash {
if self.tree.cap.is_empty() {
return Err(anyhow!("empty tree"));
return crate::middleware::NULL;
}
Ok(Hash(self.tree.cap.0[0].elements))
Hash(self.tree.cap.0[0].elements)
}
pub fn prove(&self, key: &Hash) -> Result<MerkleProof> {
/// returns the value at the given key
pub fn get(&self, key: &Value) -> Result<Value> {
let i = self.keyindex.get(&key).ok_or(anyhow!("key not in tree"))?;
let leaf_hash_raw = self.tree.get(*i);
let leaf_hash_f: [F; 4] = leaf_hash_raw
.try_into()
.map_err(|_| anyhow!("unexpected length (len!=4)"))?;
let leaf_hash: Hash = Hash(leaf_hash_f);
let (_, value) = self.leaves_map.get(&leaf_hash).unwrap();
Ok(*value)
}
/// returns a boolean indicating whether the key exists in the tree
pub fn contains(&self, key: &Value) -> bool {
self.keyindex.get(&key).is_some()
}
/// returns a proof of existence, which proves that the given key exists in
/// the tree. It returns the `MerkleProof`.
pub fn prove(&self, key: &Value) -> Result<MerkleProof> {
let i = self.keyindex.get(&key).ok_or(anyhow!("key not in tree"))?;
let proof = self.tree.prove(*i);
Ok(MerkleProof {
@ -87,7 +116,9 @@ impl MerkleTree {
})
}
pub fn prove_nonexistence(&self, _key: &Hash) -> Result<MerkleProof> {
/// returns a proof of non-existence, which proves that the given `key`
/// does not exist in the tree
pub fn prove_nonexistence(&self, _key: &Value) -> Result<MerkleProof> {
// mock method
println!("WARNING: MerkleTree::verify_nonexistence is currently a mock");
Ok(MerkleProof {
@ -97,7 +128,8 @@ impl MerkleTree {
})
}
pub fn verify(root: Hash, proof: &MerkleProof, key: &Hash, value: &Value) -> Result<()> {
/// verifies an inclusion proof for the given `key` and `value`
pub fn verify(root: Hash, proof: &MerkleProof, key: &Value, value: &Value) -> Result<()> {
if !proof.existence {
return Err(anyhow!(
"expected proof of existence, found proof of non-existence"
@ -108,12 +140,9 @@ impl MerkleTree {
verify_merkle_proof(leaf.into(), proof.index, root, &proof.proof)
}
pub fn verify_nonexistence(
_root: Hash,
proof: &MerkleProof,
_key: &Hash,
_value: &Value,
) -> Result<()> {
/// verifies a non-inclusion proof for the given `key`, that is, the given
/// `key` does not exist in the tree
pub fn verify_nonexistence(_root: Hash, proof: &MerkleProof, _key: &Value) -> Result<()> {
// mock method
if proof.existence {
return Err(anyhow!(
@ -124,18 +153,15 @@ impl MerkleTree {
Ok(())
}
pub fn iter(&self) -> std::collections::hash_map::Iter<Hash, Value> {
/// returns an iterator over the leaves of the tree
pub fn iter(&self) -> std::collections::hash_map::Iter<Value, Value> {
self.kvs.iter()
}
pub fn kvs(&self) -> &HashMap<Hash, Value> {
&self.kvs
}
}
impl<'a> IntoIterator for &'a MerkleTree {
type Item = (&'a Hash, &'a Value);
type IntoIter = std::collections::hash_map::Iter<'a, Hash, Value>;
type Item = (&'a Value, &'a Value);
type IntoIter = std::collections::hash_map::Iter<'a, Value, Value>;
fn into_iter(self) -> Self::IntoIter {
self.kvs.iter()
@ -151,15 +177,15 @@ pub mod tests {
#[test]
fn test_merkletree() -> Result<()> {
let (k0, v0) = (
hash_str("key_0".into()),
Value(hash_str("key_0".into()).0),
Value(hash_str("value_0".into()).0),
);
let (k1, v1) = (
hash_str("key_1".into()),
Value(hash_str("key_1".into()).0),
Value(hash_str("value_1".into()).0),
);
let (k2, v2) = (
hash_str("key_2".into()),
Value(hash_str("key_2".into()).0),
Value(hash_str("value_2".into()).0),
);
@ -171,18 +197,18 @@ pub mod tests {
let tree = MerkleTree::new(&kvs);
let proof = tree.prove(&k2)?;
MerkleTree::verify(tree.root()?, &proof, &k2, &v2)?;
MerkleTree::verify(tree.root(), &proof, &k2, &v2)?;
// expect verification to fail with different key / value
assert!(MerkleTree::verify(tree.root()?, &proof, &k2, &v0).is_err());
assert!(MerkleTree::verify(tree.root()?, &proof, &k0, &v2).is_err());
assert!(MerkleTree::verify(tree.root(), &proof, &k2, &v0).is_err());
assert!(MerkleTree::verify(tree.root(), &proof, &k0, &v2).is_err());
// non-existence proofs
let proof_ne = tree.prove_nonexistence(&k2)?;
let _ = MerkleTree::verify_nonexistence(tree.root()?, &proof_ne, &k2, &v2)?;
let _ = MerkleTree::verify_nonexistence(tree.root(), &proof_ne, &k2)?;
// expect verification of existence fail for nonexistence proof
let _ = MerkleTree::verify(tree.root()?, &proof_ne, &k2, &v2).is_err();
let _ = MerkleTree::verify(tree.root(), &proof_ne, &k2, &v2).is_err();
Ok(())
}

1
src/primitives/mod.rs Normal file
View file

@ -0,0 +1 @@
pub mod merkletree;