mirror of https://github.com/vacp2p/zerokit.git
refactor(rln): use semaphore-rs as dep
Instead of manually importing moving target Assumes public functions etc are exposed and that code bases / priorities end up developing in same way 1) Upstream changes 2) Keep fork up to date 3) Keep option open to embed it as appropriate
This commit is contained in:
parent
3314aba6cc
commit
9cb37a229d
|
@ -62,3 +62,5 @@ blake2 = "0.8.1"
|
|||
# TODO Remove this and use arkworks instead
|
||||
sapling-crypto = { package = "sapling-crypto_ce", version = "0.1.3", default-features = false }
|
||||
bellman = { package = "bellman_ce", version = "0.3.4", default-features = false }
|
||||
|
||||
semaphore = { git = "https://github.com/oskarth/semaphore-rs" }
|
||||
|
|
216
rln/src/hash.rs
216
rln/src/hash.rs
|
@ -1,216 +0,0 @@
|
|||
// Adapted from https://github.com/worldcoin/semaphore-rs/blob/main/src/hash.rs
|
||||
//
|
||||
use ethers_core::types::U256;
|
||||
use num_bigint::{BigInt, Sign};
|
||||
use serde::{
|
||||
de::{Error as DeError, Visitor},
|
||||
ser::Error as _,
|
||||
Deserialize, Serialize,
|
||||
};
|
||||
use std::{
|
||||
fmt::{Debug, Display, Formatter, Result as FmtResult},
|
||||
str::{from_utf8, FromStr},
|
||||
};
|
||||
|
||||
/// Container for 256-bit hash values.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub struct Hash(pub [u8; 32]);
|
||||
|
||||
impl Hash {
|
||||
#[must_use]
|
||||
pub const fn from_bytes_be(bytes: [u8; 32]) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn as_bytes_be(&self) -> &[u8; 32] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Debug print hashes using `hex!(..)` literals.
|
||||
impl Debug for Hash {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||
write!(f, "Hash(hex!(\"{}\"))", hex::encode(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
/// Display print hashes as `0x...`.
|
||||
impl Display for Hash {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||
write!(f, "0x{}", hex::encode(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
/// Conversion from Ether U256
|
||||
impl From<&Hash> for U256 {
|
||||
fn from(hash: &Hash) -> Self {
|
||||
Self::from_big_endian(hash.as_bytes_be())
|
||||
}
|
||||
}
|
||||
|
||||
/// Conversion to Ether U256
|
||||
impl From<U256> for Hash {
|
||||
fn from(u256: U256) -> Self {
|
||||
let mut bytes = [0_u8; 32];
|
||||
u256.to_big_endian(&mut bytes);
|
||||
Self::from_bytes_be(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
/// Conversion from vec
|
||||
impl From<Vec<u8>> for Hash {
|
||||
fn from(vec: Vec<u8>) -> Self {
|
||||
let mut bytes = [0_u8; 32];
|
||||
bytes.copy_from_slice(&vec[0..32]);
|
||||
Self::from_bytes_be(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
/// Conversion to BigInt
|
||||
impl From<Hash> for BigInt {
|
||||
fn from(hash: Hash) -> Self {
|
||||
Self::from_bytes_be(Sign::Plus, hash.as_bytes_be())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Hash> for BigInt {
|
||||
fn from(hash: &Hash) -> Self {
|
||||
Self::from_bytes_be(Sign::Plus, hash.as_bytes_be())
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse Hash from hex string.
|
||||
/// Hex strings can be upper/lower/mixed case and have an optional `0x` prefix
|
||||
/// but they must always be exactly 32 bytes.
|
||||
impl FromStr for Hash {
|
||||
type Err = hex::FromHexError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let str = trim_hex_prefix(s);
|
||||
let mut out = [0_u8; 32];
|
||||
hex::decode_to_slice(str, &mut out)?;
|
||||
Ok(Self(out))
|
||||
}
|
||||
}
|
||||
|
||||
/// Serialize hashes into human readable hex strings or byte arrays.
|
||||
/// Hex strings are lower case without prefix and always 32 bytes.
|
||||
impl Serialize for Hash {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
if serializer.is_human_readable() {
|
||||
let mut hex_ascii = [0_u8; 64];
|
||||
hex::encode_to_slice(self.0, &mut hex_ascii)
|
||||
.map_err(|e| S::Error::custom(format!("Error hex encoding: {}", e)))?;
|
||||
from_utf8(&hex_ascii)
|
||||
.map_err(|e| S::Error::custom(format!("Invalid hex encoding: {}", e)))?
|
||||
.serialize(serializer)
|
||||
} else {
|
||||
self.0.serialize(serializer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Deserialize human readable hex strings or byte arrays into hashes.
|
||||
/// Hex strings can be upper/lower/mixed case and have an optional `0x` prefix
|
||||
/// but they must always be exactly 32 bytes.
|
||||
impl<'de> Deserialize<'de> for Hash {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
if deserializer.is_human_readable() {
|
||||
deserializer.deserialize_str(HashStrVisitor)
|
||||
} else {
|
||||
<[u8; 32]>::deserialize(deserializer).map(Hash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct HashStrVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for HashStrVisitor {
|
||||
type Value = Hash;
|
||||
|
||||
fn expecting(&self, formatter: &mut Formatter) -> FmtResult {
|
||||
formatter.write_str("a 32 byte hex string")
|
||||
}
|
||||
|
||||
fn visit_borrowed_str<E>(self, value: &'de str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: DeError,
|
||||
{
|
||||
Hash::from_str(value).map_err(|e| E::custom(format!("Error in hex: {}", e)))
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: DeError,
|
||||
{
|
||||
Hash::from_str(value).map_err(|e| E::custom(format!("Error in hex: {}", e)))
|
||||
}
|
||||
|
||||
fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
|
||||
where
|
||||
E: DeError,
|
||||
{
|
||||
Hash::from_str(&value).map_err(|e| E::custom(format!("Error in hex: {}", e)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function to optionally remove `0x` prefix from hex strings.
|
||||
fn trim_hex_prefix(str: &str) -> &str {
|
||||
if str.len() >= 2 && (&str[..2] == "0x" || &str[..2] == "0X") {
|
||||
&str[2..]
|
||||
} else {
|
||||
str
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
use hex_literal::hex;
|
||||
use serde_json::{from_str, to_string};
|
||||
|
||||
#[test]
|
||||
fn test_serialize() {
|
||||
let hash = Hash([0; 32]);
|
||||
assert_eq!(
|
||||
to_string(&hash).unwrap(),
|
||||
"\"0000000000000000000000000000000000000000000000000000000000000000\""
|
||||
);
|
||||
let hash = Hash(hex!(
|
||||
"1c4823575d154474ee3e5ac838d002456a815181437afd14f126da58a9912bbe"
|
||||
));
|
||||
assert_eq!(
|
||||
to_string(&hash).unwrap(),
|
||||
"\"1c4823575d154474ee3e5ac838d002456a815181437afd14f126da58a9912bbe\""
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deserialize() {
|
||||
assert_eq!(
|
||||
from_str::<Hash>(
|
||||
"\"0x1c4823575d154474ee3e5ac838d002456a815181437afd14f126da58a9912bbe\""
|
||||
)
|
||||
.unwrap(),
|
||||
Hash(hex!(
|
||||
"1c4823575d154474ee3e5ac838d002456a815181437afd14f126da58a9912bbe"
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
from_str::<Hash>(
|
||||
"\"0X1C4823575d154474EE3e5ac838d002456a815181437afd14f126da58a9912bbe\""
|
||||
)
|
||||
.unwrap(),
|
||||
Hash(hex!(
|
||||
"1c4823575d154474ee3e5ac838d002456a815181437afd14f126da58a9912bbe"
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
// Adapted from
|
||||
// https://github.com/worldcoin/semaphore-rs/blob/main/src/identity.rs
|
||||
|
||||
use num_bigint::{BigInt, Sign};
|
||||
use once_cell::sync::Lazy;
|
||||
use poseidon_rs::Poseidon;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
use crate::util::{bigint_to_fr, fr_to_bigint};
|
||||
|
||||
static POSEIDON: Lazy<Poseidon> = Lazy::new(Poseidon::new);
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Identity {
|
||||
pub trapdoor: BigInt,
|
||||
pub nullifier: BigInt,
|
||||
}
|
||||
|
||||
// todo: improve
|
||||
fn sha(msg: &[u8]) -> [u8; 32] {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(msg);
|
||||
let result = hasher.finalize();
|
||||
let res: [u8; 32] = result.into();
|
||||
res
|
||||
}
|
||||
|
||||
impl Identity {
|
||||
pub fn new(seed: &[u8]) -> Self {
|
||||
let seed_hash = &sha(seed);
|
||||
|
||||
// https://github.com/appliedzkp/zk-kit/blob/1ea410456fc2b95877efa7c671bc390ffbfb5d36/packages/identity/src/identity.ts#L58
|
||||
let trapdoor = BigInt::from_bytes_be(
|
||||
Sign::Plus,
|
||||
&sha(format!("{}identity_trapdoor", hex::encode(seed_hash)).as_bytes()),
|
||||
);
|
||||
let nullifier = BigInt::from_bytes_be(
|
||||
Sign::Plus,
|
||||
&sha(format!("{}identity_nullifier", hex::encode(seed_hash)).as_bytes()),
|
||||
);
|
||||
|
||||
Self {
|
||||
trapdoor,
|
||||
nullifier,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn secret_hash(&self) -> BigInt {
|
||||
let res = POSEIDON
|
||||
.hash(vec![
|
||||
bigint_to_fr(&self.nullifier),
|
||||
bigint_to_fr(&self.trapdoor),
|
||||
])
|
||||
.unwrap();
|
||||
fr_to_bigint(res)
|
||||
}
|
||||
|
||||
pub fn commitment(&self) -> BigInt {
|
||||
let res = POSEIDON
|
||||
.hash(vec![bigint_to_fr(&self.secret_hash())])
|
||||
.unwrap();
|
||||
fr_to_bigint(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn test_identity() {
|
||||
let id = Identity::new(b"message");
|
||||
//println!("Commitment: {:#}", id.commitment());
|
||||
// From https://github.com/appliedzkp/zk-kit/blob/1ea410456fc2b95877efa7c671bc390ffbfb5d36/packages/identity/tests/index.test.ts#L63-L70
|
||||
let s = "1720349790382552497189398984241859233944354304766757200361065203741879866188";
|
||||
|
||||
let x = BigInt::from_str(s).unwrap();
|
||||
|
||||
assert!(id.commitment() == x);
|
||||
}
|
||||
}
|
|
@ -2,69 +2,55 @@
|
|||
#![allow(unused_imports)]
|
||||
|
||||
pub mod ffi;
|
||||
pub mod hash;
|
||||
pub mod identity;
|
||||
pub mod merkle_tree;
|
||||
pub mod poseidon_tree;
|
||||
pub mod public;
|
||||
pub mod util;
|
||||
|
||||
use ark_bn254::{Fr, Parameters};
|
||||
use ark_ec::bn::Bn;
|
||||
|
||||
pub type Field = Fr;
|
||||
pub type Groth16Proof = ark_groth16::Proof<Bn<Parameters>>;
|
||||
pub type EthereumGroth16Proof = ark_circom::ethereum::Proof;
|
||||
|
||||
// RLN lib
|
||||
pub mod merkle;
|
||||
pub mod poseidon;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use hash::*;
|
||||
use identity::*;
|
||||
use poseidon_tree::*;
|
||||
// use protocol::*;
|
||||
use hex_literal::hex;
|
||||
use num_bigint::BigInt;
|
||||
use semaphore::{hash::Hash, identity::Identity, poseidon_tree::PoseidonTree, protocol::*};
|
||||
|
||||
// Adapted from https://github.com/worldcoin/semaphore-rs/blob/main/src/lib.rs
|
||||
#[test]
|
||||
fn test_merkle_proof() {
|
||||
//fn test_end_to_end() {
|
||||
const LEAF: Hash = Hash::from_bytes_be(hex!(
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
));
|
||||
|
||||
// generate identity
|
||||
let id = Identity::new(b"secret");
|
||||
let id = Identity::new(b"hello");
|
||||
|
||||
// generate merkle tree
|
||||
const LEAF: Hash = Hash::from_bytes_be([0u8; 32]);
|
||||
|
||||
let mut tree = PoseidonTree::new(21, LEAF);
|
||||
let (_, leaf) = id.commitment().to_bytes_be();
|
||||
tree.set(0, leaf.into());
|
||||
tree.set(0, id.commitment().into());
|
||||
|
||||
let merkle_proof = tree.proof(0).expect("proof should exist");
|
||||
let root = tree.root();
|
||||
let root: Hash = tree.root().into();
|
||||
|
||||
println!("Root: {:#}", root);
|
||||
println!("Merkle proof: {:#?}", merkle_proof);
|
||||
|
||||
// change signal and external_nullifier here
|
||||
// let signal = "xxx".as_bytes();
|
||||
// let external_nullifier = "appId".as_bytes();
|
||||
// // change signal and external_nullifier here
|
||||
// let signal = b"xxx";
|
||||
// let external_nullifier = b"appId";
|
||||
|
||||
// let external_nullifier_hash = hash_external_nullifier(external_nullifier);
|
||||
// let nullifier_hash = generate_nullifier_hash(&id, &external_nullifier_hash);
|
||||
// let nullifier_hash = generate_nullifier_hash(&id, external_nullifier_hash);
|
||||
|
||||
// let config = SnarkFileConfig {
|
||||
// zkey: "./semaphore/build/snark/semaphore_final.zkey".to_string(),
|
||||
// wasm: "./semaphore/build/snark/semaphore.wasm".to_string(),
|
||||
// };
|
||||
// let proof = generate_proof(&id, &merkle_proof, external_nullifier, signal).unwrap();
|
||||
|
||||
// let proof =
|
||||
// generate_proof(&config, &id, &merkle_proof, &external_nullifier_hash, signal).unwrap();
|
||||
|
||||
// let success = verify_proof(
|
||||
// &config,
|
||||
// &root.into(),
|
||||
// &nullifier_hash,
|
||||
// signal,
|
||||
// &external_nullifier_hash,
|
||||
// &proof,
|
||||
// )
|
||||
// .unwrap();
|
||||
|
||||
// assert!(success);
|
||||
// let success =
|
||||
// verify_proof(root, nullifier_hash, signal, external_nullifier, &proof).unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,355 +0,0 @@
|
|||
// Adapted from https://github.com/worldcoin/semaphore-rs/blob/main/src/merkle_tree.rs
|
||||
//
|
||||
//! Implements basic binary Merkle trees
|
||||
//!
|
||||
//! # To do
|
||||
//!
|
||||
//! * Disk based storage backend (using mmaped files should be easy)
|
||||
|
||||
use num_bigint::BigInt;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
iter::{once, repeat, successors},
|
||||
};
|
||||
|
||||
/// Hash types, values and algorithms for a Merkle tree
|
||||
pub trait Hasher {
|
||||
/// Type of the leaf and node hashes
|
||||
type Hash: Clone + Eq + Serialize;
|
||||
|
||||
/// Compute the hash of an intermediate node
|
||||
fn hash_node(left: &Self::Hash, right: &Self::Hash) -> Self::Hash;
|
||||
}
|
||||
|
||||
/// Merkle tree with all leaf and intermediate hashes stored
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct MerkleTree<H: Hasher> {
|
||||
/// Depth of the tree, # of layers including leaf layer
|
||||
depth: usize,
|
||||
|
||||
/// Hash value of empty subtrees of given depth, starting at leaf level
|
||||
empty: Vec<H::Hash>,
|
||||
|
||||
/// Hash values of tree nodes and leaves, breadth first order
|
||||
nodes: Vec<H::Hash>,
|
||||
}
|
||||
|
||||
/// Element of a Merkle proof
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum Branch<H: Hasher> {
|
||||
/// Left branch taken, value is the right sibling hash.
|
||||
Left(H::Hash),
|
||||
|
||||
/// Right branch taken, value is the left sibling hash.
|
||||
Right(H::Hash),
|
||||
}
|
||||
|
||||
/// Merkle proof path, bottom to top.
|
||||
#[derive(Clone, PartialEq, Eq, Serialize)]
|
||||
pub struct Proof<H: Hasher>(pub Vec<Branch<H>>);
|
||||
|
||||
/// For a given node index, return the parent node index
|
||||
/// Returns None if there is no parent (root node)
|
||||
const fn parent(index: usize) -> Option<usize> {
|
||||
if index == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(((index + 1) >> 1) - 1)
|
||||
}
|
||||
}
|
||||
|
||||
/// For a given node index, return index of the first (left) child.
|
||||
const fn first_child(index: usize) -> usize {
|
||||
(index << 1) + 1
|
||||
}
|
||||
|
||||
const fn depth(index: usize) -> usize {
|
||||
// `n.next_power_of_two()` will return `n` iff `n` is a power of two.
|
||||
// The extra offset corrects this.
|
||||
(index + 2).next_power_of_two().trailing_zeros() as usize - 1
|
||||
}
|
||||
|
||||
impl<H: Hasher> MerkleTree<H> {
|
||||
/// Creates a new `MerkleTree`
|
||||
/// * `depth` - The depth of the tree, including the root. This is 1 greater
|
||||
/// than the `treeLevels` argument to the Semaphore contract.
|
||||
pub fn new(depth: usize, initial_leaf: H::Hash) -> Self {
|
||||
// Compute empty node values, leaf to root
|
||||
let empty = successors(Some(initial_leaf), |prev| Some(H::hash_node(prev, prev)))
|
||||
.take(depth)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Compute node values
|
||||
let nodes = empty
|
||||
.iter()
|
||||
.rev()
|
||||
.enumerate()
|
||||
.flat_map(|(depth, hash)| repeat(hash).take(1 << depth))
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
debug_assert!(nodes.len() == (1 << depth) - 1);
|
||||
|
||||
Self {
|
||||
depth,
|
||||
empty,
|
||||
nodes,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn num_leaves(&self) -> usize {
|
||||
self.depth
|
||||
.checked_sub(1)
|
||||
.map(|n| 1 << n)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn root(&self) -> H::Hash {
|
||||
self.nodes[0].clone()
|
||||
}
|
||||
|
||||
pub fn set(&mut self, leaf: usize, hash: H::Hash) {
|
||||
self.set_range(leaf, once(hash));
|
||||
}
|
||||
|
||||
pub fn set_range<I: IntoIterator<Item = H::Hash>>(&mut self, start: usize, hashes: I) {
|
||||
let index = self.num_leaves() + start - 1;
|
||||
let mut count = 0;
|
||||
// TODO: Error/panic when hashes is longer than available leafs
|
||||
for (leaf, hash) in self.nodes[index..].iter_mut().zip(hashes) {
|
||||
*leaf = hash;
|
||||
count += 1;
|
||||
}
|
||||
if count != 0 {
|
||||
self.update_nodes(index, index + (count - 1));
|
||||
}
|
||||
}
|
||||
|
||||
fn update_nodes(&mut self, start: usize, end: usize) {
|
||||
debug_assert_eq!(depth(start), depth(end));
|
||||
if let (Some(start), Some(end)) = (parent(start), parent(end)) {
|
||||
for parent in start..=end {
|
||||
let child = first_child(parent);
|
||||
self.nodes[parent] = H::hash_node(&self.nodes[child], &self.nodes[child + 1]);
|
||||
}
|
||||
self.update_nodes(start, end);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn proof(&self, leaf: usize) -> Option<Proof<H>> {
|
||||
if leaf >= self.num_leaves() {
|
||||
return None;
|
||||
}
|
||||
let mut index = self.num_leaves() + leaf - 1;
|
||||
let mut path = Vec::with_capacity(self.depth);
|
||||
while let Some(parent) = parent(index) {
|
||||
// Add proof for node at index to parent
|
||||
path.push(match index & 1 {
|
||||
1 => Branch::Left(self.nodes[index + 1].clone()),
|
||||
0 => Branch::Right(self.nodes[index - 1].clone()),
|
||||
_ => unreachable!(),
|
||||
});
|
||||
index = parent;
|
||||
}
|
||||
Some(Proof(path))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn verify(&self, hash: H::Hash, proof: &Proof<H>) -> bool {
|
||||
proof.root(hash) == self.root()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn leaves(&self) -> &[H::Hash] {
|
||||
&self.nodes[(self.num_leaves() - 1)..]
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Hasher> Proof<H> {
|
||||
/// Compute the leaf index for this proof
|
||||
#[allow(dead_code)]
|
||||
pub fn leaf_index(&self) -> usize {
|
||||
self.0.iter().rev().fold(0, |index, branch| match branch {
|
||||
Branch::Left(_) => index << 1,
|
||||
Branch::Right(_) => (index << 1) + 1,
|
||||
})
|
||||
}
|
||||
|
||||
/// Compute path index (TODO: do we want to keep this here?)
|
||||
#[allow(dead_code)]
|
||||
pub fn path_index(&self) -> Vec<BigInt> {
|
||||
self.0
|
||||
.iter()
|
||||
.map(|branch| match branch {
|
||||
Branch::Left(_) => BigInt::from(0),
|
||||
Branch::Right(_) => BigInt::from(1),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Compute the Merkle root given a leaf hash
|
||||
#[allow(dead_code)]
|
||||
pub fn root(&self, hash: H::Hash) -> H::Hash {
|
||||
self.0.iter().fold(hash, |hash, branch| match branch {
|
||||
Branch::Left(sibling) => H::hash_node(&hash, sibling),
|
||||
Branch::Right(sibling) => H::hash_node(sibling, &hash),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<H> Debug for Branch<H>
|
||||
where
|
||||
H: Hasher,
|
||||
H::Hash: Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Left(arg0) => f.debug_tuple("Left").field(arg0).finish(),
|
||||
Self::Right(arg0) => f.debug_tuple("Right").field(arg0).finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<H> Debug for Proof<H>
|
||||
where
|
||||
H: Hasher,
|
||||
H::Hash: Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple("Proof").field(&self.0).finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
use hex_literal::hex;
|
||||
use tiny_keccak::{Hasher as _, Keccak};
|
||||
|
||||
struct Keccak256;
|
||||
|
||||
impl Hasher for Keccak256 {
|
||||
type Hash = [u8; 32];
|
||||
|
||||
fn hash_node(left: &Self::Hash, right: &Self::Hash) -> Self::Hash {
|
||||
let mut output = [0; 32];
|
||||
let mut hasher = Keccak::v256();
|
||||
hasher.update(left);
|
||||
hasher.update(right);
|
||||
hasher.finalize(&mut output);
|
||||
output
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_index_calculus() {
|
||||
assert_eq!(parent(0), None);
|
||||
assert_eq!(parent(1), Some(0));
|
||||
assert_eq!(parent(2), Some(0));
|
||||
assert_eq!(parent(3), Some(1));
|
||||
assert_eq!(parent(4), Some(1));
|
||||
assert_eq!(parent(5), Some(2));
|
||||
assert_eq!(parent(6), Some(2));
|
||||
assert_eq!(first_child(0), 1);
|
||||
assert_eq!(first_child(2), 5);
|
||||
assert_eq!(depth(0), 0);
|
||||
assert_eq!(depth(1), 1);
|
||||
assert_eq!(depth(2), 1);
|
||||
assert_eq!(depth(3), 2);
|
||||
assert_eq!(depth(6), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_root() {
|
||||
let mut tree = MerkleTree::<Keccak256>::new(3, [0; 32]);
|
||||
assert_eq!(
|
||||
tree.root(),
|
||||
hex!("b4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30")
|
||||
);
|
||||
tree.set(
|
||||
0,
|
||||
hex!("0000000000000000000000000000000000000000000000000000000000000001"),
|
||||
);
|
||||
assert_eq!(
|
||||
tree.root(),
|
||||
hex!("c1ba1812ff680ce84c1d5b4f1087eeb08147a4d510f3496b2849df3a73f5af95")
|
||||
);
|
||||
tree.set(
|
||||
1,
|
||||
hex!("0000000000000000000000000000000000000000000000000000000000000002"),
|
||||
);
|
||||
assert_eq!(
|
||||
tree.root(),
|
||||
hex!("893760ec5b5bee236f29e85aef64f17139c3c1b7ff24ce64eb6315fca0f2485b")
|
||||
);
|
||||
tree.set(
|
||||
2,
|
||||
hex!("0000000000000000000000000000000000000000000000000000000000000003"),
|
||||
);
|
||||
assert_eq!(
|
||||
tree.root(),
|
||||
hex!("222ff5e0b5877792c2bc1670e2ccd0c2c97cd7bb1672a57d598db05092d3d72c")
|
||||
);
|
||||
tree.set(
|
||||
3,
|
||||
hex!("0000000000000000000000000000000000000000000000000000000000000004"),
|
||||
);
|
||||
assert_eq!(
|
||||
tree.root(),
|
||||
hex!("a9bb8c3f1f12e9aa903a50c47f314b57610a3ab32f2d463293f58836def38d36")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_proof() {
|
||||
let mut tree = MerkleTree::<Keccak256>::new(3, [0; 32]);
|
||||
tree.set(
|
||||
0,
|
||||
hex!("0000000000000000000000000000000000000000000000000000000000000001"),
|
||||
);
|
||||
tree.set(
|
||||
1,
|
||||
hex!("0000000000000000000000000000000000000000000000000000000000000002"),
|
||||
);
|
||||
tree.set(
|
||||
2,
|
||||
hex!("0000000000000000000000000000000000000000000000000000000000000003"),
|
||||
);
|
||||
tree.set(
|
||||
3,
|
||||
hex!("0000000000000000000000000000000000000000000000000000000000000004"),
|
||||
);
|
||||
|
||||
let proof = tree.proof(2).expect("proof should exist");
|
||||
assert_eq!(proof.leaf_index(), 2);
|
||||
assert!(tree.verify(
|
||||
hex!("0000000000000000000000000000000000000000000000000000000000000003"),
|
||||
&proof
|
||||
));
|
||||
assert!(!tree.verify(
|
||||
hex!("0000000000000000000000000000000000000000000000000000000000000001"),
|
||||
&proof
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_position() {
|
||||
let mut tree = MerkleTree::<Keccak256>::new(3, [0; 32]);
|
||||
tree.set(
|
||||
0,
|
||||
hex!("0000000000000000000000000000000000000000000000000000000000000001"),
|
||||
);
|
||||
tree.set(
|
||||
1,
|
||||
hex!("0000000000000000000000000000000000000000000000000000000000000002"),
|
||||
);
|
||||
tree.set(
|
||||
2,
|
||||
hex!("0000000000000000000000000000000000000000000000000000000000000003"),
|
||||
);
|
||||
tree.set(
|
||||
3,
|
||||
hex!("0000000000000000000000000000000000000000000000000000000000000004"),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
// Adapted from https://github.com/worldcoin/semaphore-rs/blob/main/src/poseidon_tree.rs
|
||||
//
|
||||
use crate::{
|
||||
hash::Hash,
|
||||
merkle_tree::{self, Hasher, MerkleTree},
|
||||
};
|
||||
use ff::{PrimeField, PrimeFieldRepr};
|
||||
use once_cell::sync::Lazy;
|
||||
use poseidon_rs::{Fr, FrRepr, Poseidon};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
static POSEIDON: Lazy<Poseidon> = Lazy::new(Poseidon::new);
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub type PoseidonTree = MerkleTree<PoseidonHash>;
|
||||
#[allow(dead_code)]
|
||||
pub type Branch = merkle_tree::Branch<PoseidonHash>;
|
||||
#[allow(dead_code)]
|
||||
pub type Proof = merkle_tree::Proof<PoseidonHash>;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PoseidonHash;
|
||||
|
||||
#[allow(clippy::fallible_impl_from)] // TODO
|
||||
impl From<&Hash> for Fr {
|
||||
fn from(hash: &Hash) -> Self {
|
||||
let mut repr = FrRepr::default();
|
||||
repr.read_be(&hash.as_bytes_be()[..]).unwrap();
|
||||
Self::from_repr(repr).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::fallible_impl_from)] // TODO
|
||||
impl From<Fr> for Hash {
|
||||
fn from(fr: Fr) -> Self {
|
||||
let mut bytes = [0_u8; 32];
|
||||
fr.into_repr().write_be(&mut bytes[..]).unwrap();
|
||||
Self::from_bytes_be(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hasher for PoseidonHash {
|
||||
type Hash = Hash;
|
||||
|
||||
fn hash_node(left: &Self::Hash, right: &Self::Hash) -> Self::Hash {
|
||||
POSEIDON
|
||||
.hash(vec![left.into(), right.into()])
|
||||
.unwrap() // TODO
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
use hex_literal::hex;
|
||||
|
||||
#[test]
|
||||
fn test_tree_4() {
|
||||
const LEAF: Hash = Hash::from_bytes_be(hex!(
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
));
|
||||
|
||||
let tree = PoseidonTree::new(3, LEAF);
|
||||
assert_eq!(tree.num_leaves(), 4);
|
||||
assert_eq!(
|
||||
tree.root(),
|
||||
Hash::from_bytes_be(hex!(
|
||||
"1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1"
|
||||
))
|
||||
);
|
||||
let proof = tree.proof(3).expect("proof should exist");
|
||||
|
||||
//println!("Proof {:#?}", proof);
|
||||
//println!("Tree {:#?}", tree);
|
||||
|
||||
assert_eq!(
|
||||
proof,
|
||||
crate::merkle_tree::Proof(vec![
|
||||
Branch::Right(LEAF),
|
||||
Branch::Right(Hash::from_bytes_be(hex!(
|
||||
"2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864"
|
||||
))),
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
use crate::merkle::IncrementalMerkleTree;
|
||||
use crate::poseidon::{Poseidon as PoseidonHasher, PoseidonParams};
|
||||
use crate::poseidon_tree::PoseidonTree;
|
||||
use crate::hash::Hash;
|
||||
use semaphore::hash::Hash;
|
||||
use semaphore::poseidon_tree::PoseidonTree;
|
||||
|
||||
use ark_circom::{CircomBuilder, CircomCircuit, CircomConfig};
|
||||
use ark_std::rand::thread_rng;
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
// Adapted from
|
||||
// https://github.com/worldcoin/semaphore-rs/blob/main/src/util.rs
|
||||
|
||||
use ff::{PrimeField, PrimeFieldRepr};
|
||||
use num_bigint::{BigInt, Sign};
|
||||
use poseidon_rs::{Fr, FrRepr};
|
||||
|
||||
pub fn fr_to_bigint(fr: Fr) -> BigInt {
|
||||
let mut bytes = [0_u8; 32];
|
||||
fr.into_repr().write_be(&mut bytes[..]).unwrap();
|
||||
BigInt::from_bytes_be(Sign::Plus, &bytes)
|
||||
}
|
||||
|
||||
pub fn bigint_to_fr(bi: &BigInt) -> Fr {
|
||||
// dirty: have to force the point into the field manually, otherwise you get an
|
||||
// error if bi not in field
|
||||
let q = BigInt::parse_bytes(
|
||||
b"21888242871839275222246405745257275088548364400416034343698204186575808495617",
|
||||
10,
|
||||
)
|
||||
.unwrap();
|
||||
let m = bi.modpow(&BigInt::from(1), &q);
|
||||
|
||||
let mut repr = FrRepr::default();
|
||||
let (_, mut res) = m.to_bytes_be();
|
||||
|
||||
// prepend zeros
|
||||
res.reverse();
|
||||
res.resize(32, 0);
|
||||
res.reverse();
|
||||
|
||||
repr.read_be(&res[..]).unwrap();
|
||||
Fr::from_repr(repr).unwrap()
|
||||
}
|
Loading…
Reference in New Issue