Commit 7233da5a authored by Roman Walch's avatar Roman Walch
Browse files

update implementation

parent e69d08f6
......@@ -10,26 +10,27 @@ ff = {package = "ff_ce", version = "0.13", features = ["derive"] }
rand = "0.4" # held back for ff_ce
lazy_static = "1.4"
cfg-if = "1.0"
sha3 = "0.9"
sha3 = "0.10"
sha2 = "0.10"
blake2 = "0.10"
# for sinsemilla:
group = "0.10"
halo2 = { git = "https://github.com/zcash/halo2.git", rev = "d04b532368d05b505e622f8cac4c0693574fbd93" }
pasta_curves = "0.1"
group = "0.11"
halo2 = "=0.1.0-beta.1"
pasta_curves = "0.2.1"
subtle = "2.3"
random = {package = "rand", version = "0.8"}
# for pederson_hash:
bitvec = "0.22"
byteorder = "1.4"
jubjub = "0.7"
bls12_381 = "0.5"
jubjub = "0.8"
bls12_381 = "0.6"
[dev-dependencies]
criterion = "0.3"
sha2 = "0.9"
blake2 = "0.9" # SIMD currently broke?
[features]
default = []
asm = ["ff/asm_derive", "sha2/asm"]
asm = ["ff/asm_derive", "sha2/asm", "blake2/simd_asm"]
[[bench]]
name = "rc_bls12"
......@@ -71,6 +72,22 @@ harness = false
name = "hashes"
harness = false
[[bench]]
name = "mt_bls12"
harness = false
[[bench]]
name = "mt_bn256"
harness = false
[[bench]]
name = "mt_st"
harness = false
[[bench]]
name = "mt_hashes"
harness = false
[[example]]
name = "rc_bls12"
path = "examples/rc_bls12.rs"
......
# Plain Performance Comparison of different Hash Functions for ZKP
This repository contains Rust implementations of different hash functions for Zero-Knowledge applications.
This repository contains Rust implementations of different hash functions for Zero-Knowledge applications. For benchmarks we refer to [1].
## Hash Functions
The following hash functions are already implemented:
- [ReinforcedConcrete](https://todo)
- [ReinforcedConcrete](https://eprint.iacr.org/2021/1038.pdf)
- [Poseidon](https://eprint.iacr.org/2019/458.pdf)
- [Rescue](https://eprint.iacr.org/2019/426.pdf)
- [Rescue-Prime](https://www.esat.kuleuven.be/cosic/publications/article-3259.pdf)
- [Neptune](https://eprint.iacr.org/2021/1695.pdf)
- [Feistel-MiMC](https://eprint.iacr.org/2016/492.pdf)
- [Pedersen-Hash](https://zips.z.cash/protocol/protocol.pdf#concretepedersenhash), code extracted from [Zcash](https://github.com/zcash/librustzcash)
- [Sinsemilla](https://zips.z.cash/protocol/protocol.pdf#concretesinsemillahash), code extracted from [Orchard](https://github.com/zcash/orchard)
......@@ -18,20 +19,4 @@ We also benchmark against various classical hash algorithms implemented in [Rust
We instantiate the finite-field permutations (ReinforcedConcrete, Poseidon, Rescue, Rescue-Prime) with a statesize of three field elements in a sponge with one field element reserved as the capacity. Feistel-MiMC always has a statesize of two, which is why one can only absorb one field element per permutation call when instantiated in a sponge.
## Benchmarks
Here we give benchmarks for hashing input sizes of 512-bit (i.e., two field elements for the used prime fields). We, thus, benchmark one permutation call for all symmetric hash functions, except for Feistel-MiMC for which we require two. All benchmarks where obtained on a Linux Desktop PC with an Intel i7-4790 CPU (3.9 GHz) and 16 GB RAM using stable Rust version 1.53 and the `target-cpu=native` flag. Time in ns.
| Hash | | BN | BLS | ST |
|--------------------|--------:|--------:|--------:|--------:|
| ReinforcedConcrete | - | 3 284 | 3 265 | 1 032 |
| Poseidon | - | 17 598 | 18 174 | 17 320 |
| Rescue | - | 415 230 | 446 980 | 359 510 |
| Rescue-Prime | - | 362 870 | 391 560 | 294 660 |
| Feistel-MiMC | - | 33 800 | 35 847 | 28 594 |
| Sinsemilla | 131 460 | - | - | - |
| Pedersen-Hash | 39 807 | - | - | - |
| SHA-256 | 366.5 | - | - | - |
| Blake2b | 245.1 | - | - | - |
| Blake2s | 219.5 | - | - | - |
| SHA3-256 | 392.3 | - | - | - |
[1] [https://eprint.iacr.org/2021/1038.pdf](https://eprint.iacr.org/2021/1038.pdf)
use std::iter;
use blake2::{Blake2b, Blake2s};
use bitvec::order::Lsb0;
use bitvec::view::AsBits;
use blake2::{Blake2b512, Blake2s256};
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use group::ff::Field;
use group::ff::PrimeField;
use group::ff::PrimeFieldBits;
use group::Curve;
use jubjub::ExtendedPoint;
use pasta_curves::pallas::Base;
use random::thread_rng;
use random::Rng;
use sha2::{Digest, Sha256};
use sha3::Sha3_256;
use zkhash::pedersen_hash::pedersen_hash::pedersen_hash;
use zkhash::pedersen_hash::pedersen_hash::Personalization;
use zkhash::sinsemilla::sinsemilla::{
i2lebsp_k, HashDomain, L_ORCHARD_MERKLE, MERKLE_CRH_PERSONALIZATION,
};
use zkhash::pedersen_hash::pedersen_hash::{pedersen_hash, Personalization};
use zkhash::sinsemilla::constants::L_ORCHARD_MERKLE;
use zkhash::sinsemilla::sinsemilla::{i2lebsp_k, HashDomain, MERKLE_CRH_PERSONALIZATION};
fn sha256(c: &mut Criterion) {
let input = b"hello_world";
......@@ -42,7 +44,7 @@ fn blake2s(c: &mut Criterion) {
c.bench_function("Blake2s Hash", move |bench| {
bench.iter(|| {
let hash = Blake2s::digest(black_box(input));
let hash = Blake2s256::digest(black_box(input));
black_box(hash)
});
});
......@@ -53,7 +55,7 @@ fn blake2b(c: &mut Criterion) {
c.bench_function("Blake2b Hash", move |bench| {
bench.iter(|| {
let hash = Blake2b::digest(black_box(input));
let hash = Blake2b512::digest(black_box(input));
black_box(hash)
});
});
......@@ -83,12 +85,42 @@ fn sinsemilla(c: &mut Criterion) {
fn pedersen(c: &mut Criterion) {
let mut rng = thread_rng();
let personalization = Personalization::MerkleTree(0);
let input: Vec<bool> = (0..510).map(|_| rng.gen::<bool>()).collect();
let input = [
jubjub::Base::random(&mut rng),
jubjub::Base::random(&mut rng),
];
let lhs = {
let mut tmp = [false; 256];
for (a, b) in tmp.iter_mut().zip(input[0].to_repr().as_bits::<Lsb0>()) {
*a = *b;
}
tmp
};
let rhs = {
let mut tmp = [false; 256];
for (a, b) in tmp.iter_mut().zip(input[1].to_repr().as_bits::<Lsb0>()) {
*a = *b;
}
tmp
};
let input = lhs
.iter()
.copied()
.take(bls12_381::Scalar::NUM_BITS as usize)
.chain(
rhs.iter()
.copied()
.take(bls12_381::Scalar::NUM_BITS as usize),
);
c.bench_function("Pedersen Hash", move |bench| {
bench.iter(|| {
let hash = pedersen_hash(black_box(personalization), black_box(input.clone()));
black_box(hash)
let out = ExtendedPoint::from(hash).to_affine().get_u();
black_box(out)
});
});
}
......
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use zkhash::{
feistel_mimc::{feistel_mimc::FeistelMimc, feistel_mimc_instances::FM_BLS_PARAMS},
fields::{bls12::FpBLS12, utils},
merkle_tree::merkle_tree_fp::MerkleTree,
neptune::{neptune::Neptune, neptune_instances::NEPTUNE_BLS_PARAMS},
poseidon::{poseidon::Poseidon, poseidon_instance_bls12::POSEIDON_BLS_PARAMS},
reinforced_concrete::{
reinforced_concrete::ReinforcedConcrete, reinforced_concrete_instances::RC_BLS_PARAMS,
},
rescue::{rescue::Rescue, rescue_instance_bls12::RESCUE_BLS_PARAMS},
rescue_prime::{
rescue_prime::RescuePrime, rescue_prime_instance_bls12::RESCUE_PRIME_BLS_PARAMS,
},
};
type Scalar = FpBLS12;
fn sample_set(set_size: usize) -> Vec<Scalar> {
// (0..set_size).map(|_| utils::random_scalar(true)).collect()
(0..set_size).map(|i| utils::from_u64(i as u64)).collect()
}
fn rc(c: &mut Criterion, log_set_size: usize) {
let perm = ReinforcedConcrete::new(&RC_BLS_PARAMS);
let mut mt = MerkleTree::new(perm);
let set_size = 1 << log_set_size;
let set: Vec<Scalar> = sample_set(set_size);
let id = format!(
"ReinforcedConcrete BLS12 MT (set_size = 2^{})",
log_set_size
);
c.bench_function(&id, move |bench| {
bench.iter(|| {
mt.accumulate(black_box(&set));
});
});
}
fn poseidon(c: &mut Criterion, log_set_size: usize) {
let perm = Poseidon::new(&POSEIDON_BLS_PARAMS);
let mut mt = MerkleTree::new(perm);
let set_size = 1 << log_set_size;
let set: Vec<Scalar> = sample_set(set_size);
let id = format!("Poseidon BLS12 MT (set_size = 2^{})", log_set_size);
c.bench_function(&id, move |bench| {
bench.iter(|| {
mt.accumulate(black_box(&set));
});
});
}
fn neptune(c: &mut Criterion, log_set_size: usize) {
let perm = Neptune::new(&NEPTUNE_BLS_PARAMS);
let mut mt = MerkleTree::new(perm);
let set_size = 1 << log_set_size;
let set: Vec<Scalar> = sample_set(set_size);
let id = format!("Neptune BLS12 MT (set_size = 2^{})", log_set_size);
c.bench_function(&id, move |bench| {
bench.iter(|| {
mt.accumulate(black_box(&set));
});
});
}
fn rescue(c: &mut Criterion, log_set_size: usize) {
let perm = Rescue::new(&RESCUE_BLS_PARAMS);
let mut mt = MerkleTree::new(perm);
let set_size = 1 << log_set_size;
let set: Vec<Scalar> = sample_set(set_size);
let id = format!("Rescue BLS12 MT (set_size = 2^{})", log_set_size);
c.bench_function(&id, move |bench| {
bench.iter(|| {
mt.accumulate(black_box(&set));
});
});
}
fn rescue_prime(c: &mut Criterion, log_set_size: usize) {
let perm = RescuePrime::new(&RESCUE_PRIME_BLS_PARAMS);
let mut mt = MerkleTree::new(perm);
let set_size = 1 << log_set_size;
let set: Vec<Scalar> = sample_set(set_size);
let id = format!("Rescue-Prime BLS12 MT (set_size = 2^{})", log_set_size);
c.bench_function(&id, move |bench| {
bench.iter(|| {
mt.accumulate(black_box(&set));
});
});
}
fn feistel_mimc(c: &mut Criterion, log_set_size: usize) {
let perm = FeistelMimc::new(&FM_BLS_PARAMS);
let mut mt = MerkleTree::new(perm);
let set_size = 1 << log_set_size;
let set: Vec<Scalar> = sample_set(set_size);
let id = format!("Feistel MiMC BLS12 MT (set_size = 2^{})", log_set_size);
c.bench_function(&id, move |bench| {
bench.iter(|| {
mt.accumulate(black_box(&set));
});
});
}
fn criterion_benchmark_mt_bls(c: &mut Criterion) {
let log_set_sizes = vec![20];
for log_set_size in log_set_sizes {
rc(c, log_set_size);
poseidon(c, log_set_size);
rescue(c, log_set_size);
rescue_prime(c, log_set_size);
feistel_mimc(c, log_set_size);
neptune(c, log_set_size);
}
}
criterion_group!(
name = benches;
config = Criterion::default().sample_size(10);
targets = criterion_benchmark_mt_bls
);
criterion_main!(benches);
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use zkhash::{
feistel_mimc::{feistel_mimc::FeistelMimc, feistel_mimc_instances::FM_BN_PARAMS},
fields::{bn256::FpBN256, utils},
merkle_tree::merkle_tree_fp::MerkleTree,
neptune::{neptune::Neptune, neptune_instances::NEPTUNE_BN_PARAMS},
poseidon::{poseidon::Poseidon, poseidon_instance_bn256::POSEIDON_BN_PARAMS},
reinforced_concrete::{
reinforced_concrete::ReinforcedConcrete, reinforced_concrete_instances::RC_BN_PARAMS,
},
rescue::{rescue::Rescue, rescue_instance_bn256::RESCUE_BN_PARAMS},
rescue_prime::{
rescue_prime::RescuePrime, rescue_prime_instance_bn256::RESCUE_PRIME_BN_PARAMS,
},
};
type Scalar = FpBN256;
fn sample_set(set_size: usize) -> Vec<Scalar> {
// (0..set_size).map(|_| utils::random_scalar(true)).collect()
(0..set_size).map(|i| utils::from_u64(i as u64)).collect()
}
fn rc(c: &mut Criterion, log_set_size: usize) {
let perm = ReinforcedConcrete::new(&RC_BN_PARAMS);
let mut mt = MerkleTree::new(perm);
let set_size = 1 << log_set_size;
let set: Vec<Scalar> = sample_set(set_size);
let id = format!(
"ReinforcedConcrete BN256 MT (set_size = 2^{})",
log_set_size
);
c.bench_function(&id, move |bench| {
bench.iter(|| {
mt.accumulate(black_box(&set));
});
});
}
fn poseidon(c: &mut Criterion, log_set_size: usize) {
let perm = Poseidon::new(&POSEIDON_BN_PARAMS);
let mut mt = MerkleTree::new(perm);
let set_size = 1 << log_set_size;
let set: Vec<Scalar> = sample_set(set_size);
let id = format!("Poseidon BN256 MT (set_size = 2^{})", log_set_size);
c.bench_function(&id, move |bench| {
bench.iter(|| {
mt.accumulate(black_box(&set));
});
});
}
fn neptune(c: &mut Criterion, log_set_size: usize) {
let perm = Neptune::new(&NEPTUNE_BN_PARAMS);
let mut mt = MerkleTree::new(perm);
let set_size = 1 << log_set_size;
let set: Vec<Scalar> = sample_set(set_size);
let id = format!("Neptune BN256 MT (set_size = 2^{})", log_set_size);
c.bench_function(&id, move |bench| {
bench.iter(|| {
mt.accumulate(black_box(&set));
});
});
}
fn rescue(c: &mut Criterion, log_set_size: usize) {
let perm = Rescue::new(&RESCUE_BN_PARAMS);
let mut mt = MerkleTree::new(perm);
let set_size = 1 << log_set_size;
let set: Vec<Scalar> = sample_set(set_size);
let id = format!("Rescue BN256 MT (set_size = 2^{})", log_set_size);
c.bench_function(&id, move |bench| {
bench.iter(|| {
mt.accumulate(black_box(&set));
});
});
}
fn rescue_prime(c: &mut Criterion, log_set_size: usize) {
let perm = RescuePrime::new(&RESCUE_PRIME_BN_PARAMS);
let mut mt = MerkleTree::new(perm);
let set_size = 1 << log_set_size;
let set: Vec<Scalar> = sample_set(set_size);
let id = format!("Rescue-Prime BN256 MT (set_size = 2^{})", log_set_size);
c.bench_function(&id, move |bench| {
bench.iter(|| {
mt.accumulate(black_box(&set));
});
});
}
fn feistel_mimc(c: &mut Criterion, log_set_size: usize) {
let perm = FeistelMimc::new(&FM_BN_PARAMS);
let mut mt = MerkleTree::new(perm);
let set_size = 1 << log_set_size;
let set: Vec<Scalar> = sample_set(set_size);
let id = format!("Feistel MiMC BN256 MT (set_size = 2^{})", log_set_size);
c.bench_function(&id, move |bench| {
bench.iter(|| {
mt.accumulate(black_box(&set));
});
});
}
fn criterion_benchmark_mt_bn(c: &mut Criterion) {
let log_set_sizes = vec![20];
for log_set_size in log_set_sizes {
rc(c, log_set_size);
poseidon(c, log_set_size);
rescue(c, log_set_size);
rescue_prime(c, log_set_size);
feistel_mimc(c, log_set_size);
neptune(c, log_set_size);
}
}
criterion_group!(
name = benches;
config = Criterion::default().sample_size(10);
targets = criterion_benchmark_mt_bn
);
criterion_main!(benches);
use blake2::{Blake2b512, Blake2s256};
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use group::ff::Field;
use pasta_curves::pallas::Base;
use random::{thread_rng, Rng};
use sha2::{digest::Output, Digest, Sha256};
use sha3::Sha3_256;
use zkhash::{
merkle_tree::merkle_tree_f2::MerkleTree,
pedersen_hash::PedersenHasher,
sinsemilla::{constants::MERKLE_CRH_PERSONALIZATION, sinsemilla::HashDomain},
};
fn sha256(c: &mut Criterion, log_set_size: usize) {
let mut mt = MerkleTree::<Sha256>::default();
let mut rng = thread_rng();
let set_size = 1 << log_set_size;
let set: Vec<Output<Sha256>> = (0..set_size)
.map(|_| {
(0..Sha256::output_size())
.map(|_| rng.gen::<u8>())
.collect()
})
.collect();
let id = format!("SHA256 MT (set_size = 2^{})", log_set_size);
c.bench_function(&id, move |bench| {
bench.iter(|| {
mt.accumulate(black_box(&set));
});
});
}
fn sha3_256(c: &mut Criterion, log_set_size: usize) {
let mut mt = MerkleTree::<Sha3_256>::default();
let mut rng = thread_rng();
let set_size = 1 << log_set_size;
let set: Vec<Output<Sha3_256>> = (0..set_size)
.map(|_| {
(0..Sha3_256::output_size())
.map(|_| rng.gen::<u8>())
.collect()
})
.collect();
let id = format!("SHA3-256 MT (set_size = 2^{})", log_set_size);
c.bench_function(&id, move |bench| {
bench.iter(|| {
mt.accumulate(black_box(&set));
});
});
}
fn blake2s(c: &mut Criterion, log_set_size: usize) {
let mut mt = MerkleTree::<Blake2s256>::default();
let mut rng = thread_rng();
let set_size = 1 << log_set_size;
let set: Vec<Output<Blake2s256>> = (0..set_size)
.map(|_| {
(0..Blake2s256::output_size())
.map(|_| rng.gen::<u8>())
.collect()
})
.collect();
let id = format!("Blake2s MT (set_size = 2^{})", log_set_size);
c.bench_function(&id, move |bench| {
bench.iter(|| {
mt.accumulate(black_box(&set));
});
});
}
fn blake2b(c: &mut Criterion, log_set_size: usize) {
let mut mt = MerkleTree::<Blake2b512>::default();
let mut rng = thread_rng();
let set_size = 1 << log_set_size;
let set: Vec<Output<Blake2b512>> = (0..set_size)
.map(|_| {
(0..Blake2b512::output_size())
.map(|_| rng.gen::<u8>())
.collect()
})
.collect();
let id = format!("Blake2b MT (set_size = 2^{})", log_set_size);
c.bench_function(&id, move |bench| {
bench.iter(|| {
mt.accumulate(black_box(&set));
});
});
}
fn sinsemilla(c: &mut Criterion, log_set_size: usize) {
let mut mt = zkhash::merkle_tree::merkle_tree_orchard::MerkleTree::new(HashDomain::new(
MERKLE_CRH_PERSONALIZATION,
));
let mut rng = thread_rng();
let set_size = 1 << log_set_size;
let set: Vec<Base> = (0..set_size).map(|_| Base::random(&mut rng)).collect();
let id = format!("Sinsemilla MT (set_size = 2^{})", log_set_size);
c.bench_function(&id, move |bench| {
bench.iter(|| {
mt.accumulate(black_box(&set));
});
});
}
fn pedersen(c: &mut Criterion, log_set_size: usize) {
let mut mt =
zkhash::merkle_tree::merkle_tree_sapling::MerkleTree::new(PedersenHasher::default());
let mut rng = thread_rng();
let set_size = 1 << log_set_size;
let set: Vec<jubjub::Base> = (0..set_size)
.map(|_| jubjub::Base::random(&mut rng))
.collect();
let id = format!("Pedersen MT (set_size = 2^{})", log_set_size);
c.bench_function(&id, move |bench| {
bench.iter(|| {
mt.accumulate(black_box(&set));
});
});
}