Skip to content
5 changes: 5 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Generated by `cargo xtask generate-poseidon-parameters`. Marking it generated
# collapses it in GitHub diffs and excludes it from language statistics, so
# review attention lands on xtask/src/generate_parameters.rs, where the logic
# actually lives.
light-poseidon/src/parameters/bn254_x5.rs linguist-generated=true
5 changes: 4 additions & 1 deletion light-poseidon/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ readme = "../README.md"
keywords = ["cryptography", "hash", "poseidon", "zero-knowledge", "zkSNARK"]
license = "Apache-2.0"
edition = "2021"
# `slice::as_chunks` / `as_chunks_mut`, used in the byte serialization path,
# are stable since 1.88.0. Without this a downstream on an older toolchain
# gets "no method named `as_chunks_mut`" with no hint that it is an MSRV issue.
rust-version = "1.88"

[dependencies]
ark-bn254 = "0.5.0"
ark-ff = "0.5.0"
num-bigint = "0.4.4"
thiserror = "1.0"

[dev-dependencies]
Expand Down
69 changes: 67 additions & 2 deletions light-poseidon/benches/bn254_x5.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ use ark_ff::PrimeField;
use criterion::{criterion_group, criterion_main, Criterion};
use rand::Rng;

use light_poseidon::{Poseidon, PoseidonHasher};
use light_poseidon::{Poseidon, PoseidonBytesHasher, PoseidonHasher};

/// Hashing field elements with a hasher that is built once and reused.
pub fn bench_poseidon_bn254_x5(c: &mut Criterion) {
let mut inputs = Vec::new();
for i in 1..13 {
Expand All @@ -17,5 +18,69 @@ pub fn bench_poseidon_bn254_x5(c: &mut Criterion) {
}
}

criterion_group!(benches, bench_poseidon_bn254_x5);
/// Produces a random 32-byte value that is below the BN254 modulus when read as
/// either big-endian or little-endian.
///
/// Zeroing only one end is a trap: the byte that is most significant depends on
/// the endianness, and the modulus starts at 0x30, so a random most significant
/// byte is above it roughly 81% of the time. Zeroing both ends makes the value
/// valid whichever way it is read.
fn random_input() -> [u8; 32] {
let mut bytes = rand::thread_rng().gen::<[u8; 32]>();
if let Some(byte) = bytes.first_mut() {
*byte = 0;
}
if let Some(byte) = bytes.last_mut() {
*byte = 0;
}
bytes
}

/// Hashing byte inputs with a hasher that is built once and reused.
///
/// The byte path has its own parser and serializer, neither of which the field
/// benchmark above exercises.
///
/// Every result is unwrapped. A benchmark that discards the `Result` will
/// happily report the timing of an early error return as if it were a hash.
pub fn bench_poseidon_bn254_x5_bytes(c: &mut Criterion) {
let mut storage: Vec<[u8; 32]> = Vec::new();
for i in 1..13 {
storage.push(random_input());
let inputs: Vec<&[u8]> = storage.iter().map(|b| b.as_slice()).collect();

let mut hasher = Poseidon::<Fr>::new_circom(i).unwrap();
let name = [String::from("poseidon_bn254_x5_bytes_be_"), i.to_string()].concat();
c.bench_function(&name, |b| b.iter(|| hasher.hash_bytes_be(&inputs).unwrap()));

let mut hasher = Poseidon::<Fr>::new_circom(i).unwrap();
let name = [String::from("poseidon_bn254_x5_bytes_le_"), i.to_string()].concat();
c.bench_function(&name, |b| b.iter(|| hasher.hash_bytes_le(&inputs).unwrap()));
}
}

/// The shape `solana_poseidon::hashv` actually uses: a hasher is constructed on
/// every call, so parameter construction is timed alongside the permutation.
pub fn bench_poseidon_bn254_x5_syscall_shape(c: &mut Criterion) {
let mut storage: Vec<[u8; 32]> = Vec::new();
for i in 1..13 {
storage.push(random_input());
let inputs: Vec<&[u8]> = storage.iter().map(|b| b.as_slice()).collect();

let name = [String::from("poseidon_bn254_x5_syscall_"), i.to_string()].concat();
c.bench_function(&name, |b| {
b.iter(|| {
let mut hasher = Poseidon::<Fr>::new_circom(i).unwrap();
hasher.hash_bytes_be(&inputs).unwrap()
})
});
}
}

criterion_group!(
benches,
bench_poseidon_bn254_x5,
bench_poseidon_bn254_x5_bytes,
bench_poseidon_bn254_x5_syscall_shape
);
criterion_main!(benches);
Loading
Loading