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:
parent
f2575d1524
commit
bb865a4fea
14 changed files with 330 additions and 94 deletions
21
.github/workflows/tests.yml
vendored
Normal file
21
.github/workflows/tests.yml
vendored
Normal 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
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
¶ms,
|
¶ms,
|
||||||
[
|
[
|
||||||
|
|
@ -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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
fn main() {
|
|
||||||
println!("Hello, world!");
|
|
||||||
}
|
|
||||||
159
src/middleware/containers.rs
Normal file
159
src/middleware/containers.rs
Normal 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 {}
|
||||||
|
|
@ -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 {
|
||||||
|
|
@ -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
1
src/primitives/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
pub mod merkletree;
|
||||||
Loading…
Add table
Add a link
Reference in a new issue