Add optional mimc support

This commit is contained in:
Remco Bloemen 2022-03-01 16:44:25 -08:00 committed by psippl
parent 43d564448c
commit 52f1a9ba5d
6 changed files with 447 additions and 17 deletions

160
Cargo.lock generated
View File

@ -23,7 +23,7 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom",
"getrandom 0.2.4",
"once_cell",
"version_check",
]
@ -249,6 +249,12 @@ dependencies = [
"rayon",
]
[[package]]
name = "arrayvec"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
name = "arrayvec"
version = "0.7.2"
@ -320,6 +326,16 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitvec"
version = "0.17.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41262f11d771fd4a61aa3ce019fca363b4b6c282fca9da2a31186d3965a47a5c"
dependencies = [
"either",
"radium 0.3.0",
]
[[package]]
name = "bitvec"
version = "0.20.4"
@ -327,7 +343,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7774144344a4faa177370406a7ff5f1da24303817368584c6206c8303eb07848"
dependencies = [
"funty",
"radium",
"radium 0.6.2",
"tap",
"wyz",
]
@ -386,6 +402,12 @@ version = "3.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
[[package]]
name = "byte-slice-cast"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0a5e3906bcbf133e33c1d4d95afc664ad37fbdb9f6568d8043e7ea8c27d93d3"
[[package]]
name = "byte-slice-cast"
version = "1.2.0"
@ -583,7 +605,7 @@ dependencies = [
"criterion-plot",
"csv",
"futures",
"itertools",
"itertools 0.10.3",
"lazy_static",
"num-traits",
"oorandom",
@ -606,7 +628,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d00996de9f2f7559f7f4dc286073197f83e92256a59ed395f9aac01fe717da57"
dependencies = [
"cast",
"itertools",
"itertools 0.10.3",
]
[[package]]
@ -979,7 +1001,7 @@ name = "ethers-core"
version = "0.6.0"
source = "git+https://github.com/gakonst/ethers-rs#dc1565c0149e03ce20c2cdf76c1aaaf9fc2e3deb"
dependencies = [
"arrayvec",
"arrayvec 0.7.2",
"bytes",
"ecdsa 0.13.4",
"elliptic-curve 0.11.12",
@ -1003,7 +1025,7 @@ version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f15e1a2a54bc6bc3f8ea94afafbb374264f8322fcacdae06fefda80a206739ac"
dependencies = [
"arrayvec",
"arrayvec 0.7.2",
"bytes",
"ecdsa 0.12.4",
"elliptic-curve 0.11.12",
@ -1193,6 +1215,17 @@ dependencies = [
"version_check",
]
[[package]]
name = "getrandom"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
]
[[package]]
name = "getrandom"
version = "0.2.4"
@ -1201,7 +1234,7 @@ checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c"
dependencies = [
"cfg-if",
"libc",
"wasi",
"wasi 0.10.2+wasi-snapshot-preview1",
]
[[package]]
@ -1301,7 +1334,7 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "161ebdfec3c8e3b52bf61c4f3550a1eea4f9579d10dc1b936f3171ebdcd6c443"
dependencies = [
"parity-scale-codec",
"parity-scale-codec 2.3.1",
]
[[package]]
@ -1359,6 +1392,15 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "itertools"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.10.3"
@ -1536,6 +1578,12 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389"
[[package]]
name = "no-std-compat"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c"
[[package]]
name = "num"
version = "0.4.0"
@ -1678,15 +1726,27 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2386b4ebe91c2f7f51082d4cefa145d030e33a1842a96b12e4885cc3c01f7a55"
[[package]]
name = "parity-scale-codec"
version = "1.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4b26b16c7687c3075982af47719e481815df30bc544f7a6690763a25ca16e9d"
dependencies = [
"arrayvec 0.5.2",
"bitvec 0.17.4",
"byte-slice-cast 0.3.5",
"serde",
]
[[package]]
name = "parity-scale-codec"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "373b1a4c1338d9cd3d1fa53b3a11bdab5ab6bd80a20f7f7becd76953ae2be909"
dependencies = [
"arrayvec",
"bitvec",
"byte-slice-cast",
"arrayvec 0.7.2",
"bitvec 0.20.4",
"byte-slice-cast 1.2.0",
"impl-trait-for-tuples",
"parity-scale-codec-derive",
"serde",
@ -1868,7 +1928,7 @@ dependencies = [
"num-traits",
"quick-error 2.0.1",
"rand 0.8.4",
"rand_chacha",
"rand_chacha 0.3.1",
"rand_xorshift",
"regex-syntax",
"rusty-fork",
@ -1916,6 +1976,12 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "radium"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac"
[[package]]
name = "radium"
version = "0.6.2"
@ -1935,6 +2001,19 @@ dependencies = [
"winapi",
]
[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"getrandom 0.1.16",
"libc",
"rand_chacha 0.2.2",
"rand_core 0.5.1",
"rand_hc 0.2.0",
]
[[package]]
name = "rand"
version = "0.8.4"
@ -1942,9 +2021,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
dependencies = [
"libc",
"rand_chacha",
"rand_chacha 0.3.1",
"rand_core 0.6.3",
"rand_hc",
"rand_hc 0.3.1",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86",
"rand_core 0.5.1",
]
[[package]]
@ -1972,13 +2061,31 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
"getrandom 0.1.16",
]
[[package]]
name = "rand_core"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
"getrandom",
"getrandom 0.2.4",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core 0.5.1",
]
[[package]]
@ -2289,6 +2396,7 @@ dependencies = [
"tempfile",
"tiny-keccak",
"tracing-test",
"zkp-u256",
]
[[package]]
@ -2746,6 +2854,12 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
@ -3100,6 +3214,22 @@ dependencies = [
"synstructure",
]
[[package]]
name = "zkp-u256"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9931b419a40ffca971d538c4b5edca60d1d82d77fd6bce73fb4bcb9639776c4"
dependencies = [
"crunchy",
"hex",
"itertools 0.9.0",
"no-std-compat",
"num-traits",
"parity-scale-codec 1.3.7",
"rand 0.7.3",
"serde",
]
[[patch.unused]]
name = "wasmer"
version = "2.2.0-rc1"

View File

@ -16,6 +16,7 @@ license-file = "mit-license.md"
[features]
default = []
bench = [ "criterion", "proptest" ]
mimc = [ "tiny-keccak", "zkp-u256" ]
[[bench]]
name = "criterion"
@ -44,6 +45,8 @@ proptest = { version = "1.0", optional = true }
rayon = "1.5.1"
serde = "1.0"
sha2 = "0.10.1"
tiny-keccak = { version = "2.0.2", optional = true }
zkp-u256 = { version = "0.2", optional = true }
[dev-dependencies]
pretty_assertions = "1.0"

View File

@ -5,6 +5,11 @@ pub mod poseidon_tree;
pub mod protocol;
pub mod util;
#[cfg(feature = "mimc")]
pub mod mimc_hash;
#[cfg(feature = "mimc")]
pub mod mimc_tree;
use ark_bn254::Parameters;
use ark_ec::bn::Bn;
@ -68,12 +73,16 @@ pub mod bench {
hash::Hash,
identity::Identity,
poseidon_tree::PoseidonTree,
protocol::{generate_nullifier_hash, generate_proof, verify_proof, SnarkFileConfig},
protocol::{generate_proof, SnarkFileConfig},
};
use criterion::Criterion;
use hex_literal::hex;
pub fn group(criterion: &mut Criterion) {
#[cfg(feature = "mimc")]
crate::mimc_hash::bench::group(criterion);
#[cfg(feature = "mimc")]
crate::mimc_tree::bench::group(criterion);
bench_proof(criterion);
}

View File

@ -8,7 +8,6 @@ use std::{
fmt::Debug,
iter::{once, repeat, successors},
};
use num_bigint::BigInt;
use serde::{Deserialize, Serialize};

183
src/mimc_hash.rs Normal file
View File

@ -0,0 +1,183 @@
//! Hash function compatible with Semaphore's Merkle tree hash function
//!
//! See <https://github.com/appliedzkp/semaphore/blob/master/circuits/circom/semaphore-base.circom#L10>
//! See <https://github.com/kobigurk/circomlib/blob/4284dc1ef984a204db08864f5da530c97f9376ef/circuits/mimcsponge.circom>
//! See <https://github.com/iden3/circomlibjs/blob/main/src/mimcsponge.js>
//!
//! # To do
//!
//! * Instantiate a `PrimeField` to use Montgomery form.
use once_cell::sync::Lazy;
use zkp_u256::U256;
use tiny_keccak::{Hasher as _, Keccak};
const NUM_ROUNDS: usize = 220;
static MODULUS: Lazy<U256> = Lazy::new(|| {
U256::from_decimal_str(
"21888242871839275222246405745257275088548364400416034343698204186575808495617",
)
.unwrap()
});
fn keccak256(bytes: &[u8]) -> [u8;32] {
let mut output = [0; 32];
let mut hasher = Keccak::v256();
hasher.update(bytes);
hasher.finalize(&mut output);
output
}
static ROUND_CONSTANTS: Lazy<[U256; NUM_ROUNDS]> = Lazy::new(|| {
const SEED: &str = "mimcsponge";
let mut result = [U256::ZERO; NUM_ROUNDS];
let mut bytes = keccak256(SEED.as_bytes());
for constant in result[1..NUM_ROUNDS - 1].iter_mut() {
bytes = keccak256(&bytes);
*constant = U256::from_bytes_be(&bytes);
*constant %= &*MODULUS;
}
result
});
/// See <https://github.com/iden3/circomlibjs/blob/main/src/mimcsponge.js#L44>
fn mix(left: &mut U256, right: &mut U256) {
debug_assert!(*left < *MODULUS);
debug_assert!(*right < *MODULUS);
for round_constant in &*ROUND_CONSTANTS {
// Modulus is less than 2**252, so addition doesn't overflow
let t = (&*left + round_constant) % &*MODULUS;
let t2 = t.mulmod(&t, &*MODULUS);
let t4 = t2.mulmod(&t2, &*MODULUS);
let t5 = t.mulmod(&t4, &*MODULUS);
*right += t5;
*right %= &*MODULUS;
std::mem::swap(left, right);
}
std::mem::swap(left, right);
}
pub fn hash(values: &[U256]) -> U256 {
let mut left = U256::ZERO;
let mut right = U256::ZERO;
for value in values {
let value = value % &*MODULUS;
left += value;
left %= &*MODULUS;
mix(&mut left, &mut right);
}
left
}
#[cfg(test)]
pub mod test {
use super::*;
use hex_literal::hex;
#[test]
fn test_round_constants() {
// See <https://github.com/kobigurk/circomlib/blob/4284dc1ef984a204db08864f5da530c97f9376ef/circuits/mimcsponge.circom#L44>
assert_eq!(ROUND_CONSTANTS[0], U256::ZERO);
assert_eq!(
ROUND_CONSTANTS[1],
U256::from_decimal_str(
"7120861356467848435263064379192047478074060781135320967663101236819528304084"
)
.unwrap()
);
assert_eq!(
ROUND_CONSTANTS[2],
U256::from_decimal_str(
"5024705281721889198577876690145313457398658950011302225525409148828000436681"
)
.unwrap()
);
assert_eq!(
ROUND_CONSTANTS[218],
U256::from_decimal_str(
"2119542016932434047340813757208803962484943912710204325088879681995922344971"
)
.unwrap()
);
assert_eq!(ROUND_CONSTANTS[219], U256::ZERO);
}
#[test]
fn test_mix() {
let mut left = U256::ONE;
let mut right = U256::ZERO;
mix(&mut left, &mut right);
assert_eq!(
left,
U256::from_decimal_str(
"8792246410719720074073794355580855662772292438409936688983564419486782556587"
)
.unwrap()
);
assert_eq!(
right,
U256::from_decimal_str(
"7326554092124867281481480523863654579712861994895051796475958890524736238844"
)
.unwrap()
);
left += U256::from(2);
mix(&mut left, &mut right);
assert_eq!(
left,
U256::from_decimal_str(
"19814528709687996974327303300007262407299502847885145507292406548098437687919"
)
.unwrap()
);
assert_eq!(
right,
U256::from_decimal_str(
"3888906192024793285683241274210746486868893421288515595586335488978789653213"
)
.unwrap()
);
}
#[test]
fn test_hash() {
// See <https://github.com/iden3/circomlibjs/blob/3f84f4fbf77bebdf1722d851c1ad9b62cbf3d120/test/mimcsponge.js#L6>
assert_eq!(
hash(&[U256::from(1_u64), U256::from(2_u64)]),
U256::from_bytes_be(&hex!(
"2bcea035a1251603f1ceaf73cd4ae89427c47075bb8e3a944039ff1e3d6d2a6f"
))
);
assert_eq!(
hash(&[
U256::from(1_u64),
U256::from(2_u64),
U256::from(3_u64),
U256::from(4_u64)
]),
U256::from_bytes_be(&hex!(
"03e86bdc4eac70bd601473c53d8233b145fe8fd8bf6ef25f0b217a1da305665c"
))
);
}
}
#[cfg(feature = "bench")]
pub mod bench {
#[allow(clippy::wildcard_imports)]
use super::*;
use criterion::Criterion;
pub fn group(criterion: &mut Criterion) {
bench_mix(criterion);
}
fn bench_mix(criterion: &mut Criterion) {
let mut left = U256::ONE;
let mut right = U256::ZERO;
criterion.bench_function("mimc_mix", move |bencher| {
bencher.iter(|| mix(&mut left, &mut right));
});
}
}

106
src/mimc_tree.rs Normal file
View File

@ -0,0 +1,106 @@
use crate::{
hash::Hash,
merkle_tree::{self, Hasher, MerkleTree},
mimc_hash::hash,
};
use serde::Serialize;
use zkp_u256::U256;
pub type MimcTree = MerkleTree<MimcHash>;
#[allow(dead_code)]
pub type Branch = merkle_tree::Branch<MimcHash>;
#[allow(dead_code)]
pub type Proof = merkle_tree::Proof<MimcHash>;
#[derive(Clone, Copy, PartialEq, Eq, Serialize)]
pub struct MimcHash;
impl Hasher for MimcHash {
type Hash = Hash;
fn hash_node(left: &Self::Hash, right: &Self::Hash) -> Self::Hash {
let left = U256::from_bytes_be(left.as_bytes_be());
let right = U256::from_bytes_be(right.as_bytes_be());
Hash::from_bytes_be(hash(&[left, right]).to_bytes_be())
}
}
#[cfg(test)]
pub mod test {
use super::*;
use hex_literal::hex;
#[test]
fn test_tree_4() {
const LEAF: Hash = Hash::from_bytes_be(hex!(
"1c4823575d154474ee3e5ac838d002456a815181437afd14f126da58a9912bbe"
));
let tree = MimcTree::new(3, LEAF);
assert_eq!(tree.num_leaves(), 4);
assert_eq!(
tree.root(),
Hash::from_bytes_be(hex!(
"250de92bd4bcf4fb684fdf64923cb3b20ef4118b41c6ffb8c36b606468d6be57"
))
);
let proof = tree.proof(3).expect("proof should exist");
assert_eq!(
proof,
crate::merkle_tree::Proof(vec![
Branch::Right(LEAF),
Branch::Right(Hash::from_bytes_be(hex!(
"19f1cba77f27301df4ce3391f9b0d766cfd304d0f069cec6c0e55dfda6aba924"
))),
])
);
}
}
#[cfg(feature = "bench")]
pub mod bench {
#[allow(clippy::wildcard_imports)]
use super::*;
use criterion::{black_box, Criterion};
use hex_literal::hex;
// TODO: Randomize trees and indices
// TODO: Bench over a range of depths
const DEPTH: usize = 20;
const LEAF: Hash = Hash::from_bytes_be(hex!(
"352aa0818e138060d93b80393828ef8cdc104f331799b3ea647907481e51cce9"
));
pub fn group(criterion: &mut Criterion) {
bench_set(criterion);
bench_proof(criterion);
bench_verify(criterion);
}
fn bench_set(criterion: &mut Criterion) {
let mut tree = MimcTree::new(DEPTH, LEAF);
let index = 354_184;
let hash = Hash::from_bytes_be([0_u8; 32]);
criterion.bench_function("mimc_tree_set", move |bencher| {
bencher.iter(|| tree.set(index, black_box(hash)));
});
}
fn bench_proof(criterion: &mut Criterion) {
let tree = MimcTree::new(DEPTH, LEAF);
let index = 354_184;
criterion.bench_function("mimc_tree_proof", move |bencher| {
bencher.iter(|| tree.proof(black_box(index)));
});
}
fn bench_verify(criterion: &mut Criterion) {
let tree = MimcTree::new(DEPTH, LEAF);
let index = 354_184;
let proof = tree.proof(index).expect("proof should exist");
let hash = Hash::from_bytes_be([0_u8; 32]);
criterion.bench_function("mimc_verfiy", move |bencher| {
bencher.iter(|| proof.root(black_box(hash)));
});
}
}