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 ```rust
impl MerkleTree { 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 /// 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 /// 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 tree. It returns the `MerkleProof`.
/// the `MerkleProof`. fn prove(&self, key: &Value) -> Result<MerkleProof>;
fn prove(&self, key: &Value) -> Result<(Value, MerkleProof)>;
/// returns a proof of non-existence, which proves that the given `key` /// returns a proof of non-existence, which proves that the given `key`
/// does not exist in the tree /// 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.key=hash(original_key)`
- `leaf.value=hash(original_value)` - `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) - **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.key=i`
- `leaf.value=original_value`
- **set**: the value field of the leaf is unused, and the key contains the hash of the element - **set**: the value field of the leaf is unused, and the key contains the hash of the element
- `leaf.key=hash(original_value)` - `leaf.key=hash(original_value)`
- `leaf.value=0` - `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. 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> <br><br>

View file

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

View file

@ -1,7 +1,8 @@
use crate::merkletree::MerkleTree;
use crate::middleware::{ 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 anyhow::Result;
use std::any::Any; use std::any::Any;
use std::collections::HashMap; 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_SIGNER), Value(pk_hash.0));
kvs.insert(hash_str(&KEY_TYPE), Value::from(PodType::MockSigned)); kvs.insert(hash_str(&KEY_TYPE), Value::from(PodType::MockSigned));
let mt = MerkleTree::new(&kvs); let dict = Dictionary::new(&kvs);
let id = PodId(mt.root()?); let id = PodId(dict.commitment());
let signature = format!("{}_signed_by_{}", id, pk_hash); 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 { pub struct MockSignedPod {
id: PodId, id: PodId,
signature: String, signature: String,
mt: MerkleTree, dict: Dictionary,
} }
impl SignedPod for MockSignedPod { impl SignedPod for MockSignedPod {
fn verify(&self) -> bool { fn verify(&self) -> bool {
// Verify type // 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; return false;
} }
// Verify id // Verify id
let mt = MerkleTree::new(&self.mt.kvs()); let mt = MerkleTree::new(
let id = match mt.root() { &self
Ok(id) => PodId(id), .dict
Err(_) => return false, .iter()
}; .map(|(&k, &v)| (k, v))
.collect::<HashMap<Value, Value>>(),
);
let id = PodId(mt.root());
if id != self.id { if id != self.id {
return false; return false;
} }
// Verify signature // Verify signature
let pk_hash = match self.mt.kvs().get(&hash_str(&KEY_SIGNER)) { let pk_hash = match self.dict.get(&hash_str(&KEY_SIGNER).into()) {
Some(v) => v, Ok(v) => v,
None => return false, Err(_) => return false,
}; };
let signature = format!("{}_signed_by_{}", id, pk_hash); let signature = format!("{}_signed_by_{}", id, pk_hash);
if signature != self.signature { if signature != self.signature {
@ -66,7 +78,10 @@ impl SignedPod for MockSignedPod {
} }
fn kvs(&self) -> HashMap<Hash, Value> { 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> { 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_pod = pod.clone();
let mut bad_kvs = bad_pod.kvs(); let mut bad_kvs = bad_pod.kvs();
bad_kvs.insert(hash_str(KEY_SIGNER), Value(PodId(NULL).0 .0)); bad_kvs.insert(hash_str(KEY_SIGNER), Value(PodId(NULL).0 .0));
let bad_mt = MerkleTree::new(&bad_kvs); let bad_kvs_mt = &bad_kvs
bad_pod.mt = bad_mt; .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); assert_eq!(bad_pod.verify(), false);
let mut bad_pod = pod.clone(); let mut bad_pod = pod.clone();
let mut bad_kvs = bad_pod.kvs(); let mut bad_kvs = bad_pod.kvs();
bad_kvs.insert(hash_str(KEY_TYPE), Value::from(0)); bad_kvs.insert(hash_str(KEY_TYPE), Value::from(0));
let bad_mt = MerkleTree::new(&bad_kvs); let bad_kvs_mt = &bad_kvs
bad_pod.mt = bad_mt; .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); assert_eq!(bad_pod.verify(), false);
} }
} }

View file

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

View file

@ -3,14 +3,15 @@
use anyhow::Result; use anyhow::Result;
use itertools::Itertools; use itertools::Itertools;
use plonky2::field::types::Field;
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::From; use std::convert::From;
use std::fmt; use std::fmt;
use crate::middleware::{ use crate::middleware::{
self, hash_str, Hash, MainPodInputs, NativeOperation, NativeStatement, Params, PodId, self,
PodProver, PodSigner, F, SELF, containers::{Array, Dictionary, Set},
hash_str, Hash, MainPodInputs, NativeOperation, NativeStatement, Params, PodId, PodProver,
PodSigner, SELF,
}; };
/// This type is just for presentation purposes. /// This type is just for presentation purposes.
@ -25,16 +26,13 @@ pub enum PodClass {
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)] #[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct Origin(pub PodClass, pub PodId); pub struct Origin(pub PodClass, pub PodId);
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MerkleTree {
pub root: u8, // TODO
}
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub enum Value { pub enum Value {
String(String), String(String),
Int(i64), Int(i64),
MerkleTree(MerkleTree), Dictionary(Dictionary),
Set(Set),
Array(Array),
} }
impl From<&str> for Value { impl From<&str> for Value {
@ -54,13 +52,9 @@ impl From<&Value> for middleware::Value {
match v { match v {
Value::String(s) => middleware::Value(hash_str(s).0), Value::String(s) => middleware::Value(hash_str(s).0),
Value::Int(v) => middleware::Value::from(*v), Value::Int(v) => middleware::Value::from(*v),
// TODO Value::Dictionary(d) => middleware::Value(d.commitment().0),
Value::MerkleTree(mt) => middleware::Value([ Value::Set(s) => middleware::Value(s.commitment().0),
F::from_canonical_u64(mt.root as u64), Value::Array(a) => middleware::Value(a.commitment().0),
F::ZERO,
F::ZERO,
F::ZERO,
]),
} }
} }
} }
@ -70,7 +64,9 @@ impl fmt::Display for Value {
match self { match self {
Value::String(s) => write!(f, "\"{}\"", s), Value::String(s) => write!(f, "\"{}\"", s),
Value::Int(v) => write!(f, "{}", v), 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 backends;
pub mod frontend; pub mod frontend;
pub mod merkletree;
pub mod middleware; pub mod middleware;
pub mod primitives;
#[cfg(test)] #[cfg(test)]
pub mod examples; 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::any::Any;
use std::cmp::{Ord, Ordering}; use std::cmp::{Ord, Ordering};
use std::collections::HashMap; use std::collections::HashMap;
use std::{array, fmt}; use std::fmt;
use strum_macros::FromRepr; use strum_macros::FromRepr;
pub mod containers;
pub const KEY_SIGNER: &str = "_signer"; pub const KEY_SIGNER: &str = "_signer";
pub const KEY_TYPE: &str = "_type"; pub const KEY_TYPE: &str = "_type";
pub const STATEMENT_ARG_F_LEN: usize = 8; 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 { impl TryInto<i64> for Value {
type Error = Error; type Error = Error;
fn try_into(self) -> std::result::Result<i64, Self::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]); pub const NULL: Hash = Hash([F::ZERO, F::ZERO, F::ZERO, F::ZERO]);
impl fmt::Display for Hash { impl fmt::Display for Hash {

View file

@ -20,19 +20,22 @@ use crate::middleware::{Hash, Value, C, D, F};
const CAP_HEIGHT: usize = 0; const CAP_HEIGHT: usize = 0;
/// MerkleTree currently implements the MerkleTree interface with a wrapper on top of Plonky2's /// MerkleTree currently is a wrapper on top of Plonky2's MerkleTree. A future iteration will
/// MerkleTree. A future iteration will replace it by the MerkleTree specified at /// replace it by the MerkleTree specified at https://0xparc.github.io/pod2/merkletree.html .
/// https://0xparc.github.io/pod2/merkletree.html .
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct MerkleTree { pub struct MerkleTree {
tree: PlonkyMerkleTree<F, <C as GenericConfig<D>>::Hasher>, tree: PlonkyMerkleTree<F, <C as GenericConfig<D>>::Hasher>,
// keyindex: key -> index mapping. This is just for the current plonky-tree wrapper // 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 // 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 // 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 // will not be needed since the tree implementation itself will offer the hashmap
// functionality. // 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 { pub struct MerkleProof {
@ -42,9 +45,11 @@ pub struct MerkleProof {
} }
impl MerkleTree { impl MerkleTree {
pub fn new(kvs: &HashMap<Hash, Value>) -> Self { /// builds a new `MerkleTree` where the leaves contain the given key-values
let mut keyindex: HashMap<Hash, usize> = HashMap::new(); 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: 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 // 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 // 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 // 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; let leaf = PoseidonHash::hash_no_pad(&input).elements;
leaves.push(leaf.into()); leaves.push(leaf.into());
keyindex.insert(*k, i); keyindex.insert(*k, i);
leaves_map.insert(Hash(leaf), (*k, *v));
} }
// pad to a power of two if needed // pad to a power of two if needed
@ -67,17 +73,40 @@ impl MerkleTree {
tree, tree,
keyindex, keyindex,
kvs: kvs.clone(), 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() { 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 i = self.keyindex.get(&key).ok_or(anyhow!("key not in tree"))?;
let proof = self.tree.prove(*i); let proof = self.tree.prove(*i);
Ok(MerkleProof { 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 // mock method
println!("WARNING: MerkleTree::verify_nonexistence is currently a mock"); println!("WARNING: MerkleTree::verify_nonexistence is currently a mock");
Ok(MerkleProof { 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 { if !proof.existence {
return Err(anyhow!( return Err(anyhow!(
"expected proof of existence, found proof of non-existence" "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) verify_merkle_proof(leaf.into(), proof.index, root, &proof.proof)
} }
pub fn verify_nonexistence( /// verifies a non-inclusion proof for the given `key`, that is, the given
_root: Hash, /// `key` does not exist in the tree
proof: &MerkleProof, pub fn verify_nonexistence(_root: Hash, proof: &MerkleProof, _key: &Value) -> Result<()> {
_key: &Hash,
_value: &Value,
) -> Result<()> {
// mock method // mock method
if proof.existence { if proof.existence {
return Err(anyhow!( return Err(anyhow!(
@ -124,18 +153,15 @@ impl MerkleTree {
Ok(()) 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() self.kvs.iter()
} }
pub fn kvs(&self) -> &HashMap<Hash, Value> {
&self.kvs
}
} }
impl<'a> IntoIterator for &'a MerkleTree { impl<'a> IntoIterator for &'a MerkleTree {
type Item = (&'a Hash, &'a Value); type Item = (&'a Value, &'a Value);
type IntoIter = std::collections::hash_map::Iter<'a, Hash, Value>; type IntoIter = std::collections::hash_map::Iter<'a, Value, Value>;
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
self.kvs.iter() self.kvs.iter()
@ -151,15 +177,15 @@ pub mod tests {
#[test] #[test]
fn test_merkletree() -> Result<()> { fn test_merkletree() -> Result<()> {
let (k0, v0) = ( let (k0, v0) = (
hash_str("key_0".into()), Value(hash_str("key_0".into()).0),
Value(hash_str("value_0".into()).0), Value(hash_str("value_0".into()).0),
); );
let (k1, v1) = ( let (k1, v1) = (
hash_str("key_1".into()), Value(hash_str("key_1".into()).0),
Value(hash_str("value_1".into()).0), Value(hash_str("value_1".into()).0),
); );
let (k2, v2) = ( let (k2, v2) = (
hash_str("key_2".into()), Value(hash_str("key_2".into()).0),
Value(hash_str("value_2".into()).0), Value(hash_str("value_2".into()).0),
); );
@ -171,18 +197,18 @@ pub mod tests {
let tree = MerkleTree::new(&kvs); let tree = MerkleTree::new(&kvs);
let proof = tree.prove(&k2)?; 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 // expect verification to fail with different key / value
assert!(MerkleTree::verify(tree.root()?, &proof, &k2, &v0).is_err()); 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, &k0, &v2).is_err());
// non-existence proofs // non-existence proofs
let proof_ne = tree.prove_nonexistence(&k2)?; 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 // 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(()) Ok(())
} }

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

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