Skip to content

Commit ec4b206

Browse files
authored
feat: optimize variable length hashes (#32)
1 parent 678300e commit ec4b206

File tree

2 files changed

+50
-9
lines changed

2 files changed

+50
-9
lines changed

src/poseidon2.nr

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,23 +69,40 @@ impl Poseidon2 {
6969
state[2] += input[i * RATE + 2];
7070
state = crate::poseidon2_permutation(state, 4);
7171
}
72-
// handle remaining (<3) elements
72+
73+
// handle remaining elements after last full RATE-sized chunk
7374
let remainder_start = (in_len / RATE) * RATE;
7475
for j in remainder_start..in_len {
7576
state[j - remainder_start] += input[j];
7677
}
7778
} else {
78-
for i in 0..input.len() {
79-
if i < in_len {
80-
state[i % RATE] += input[i];
81-
if (i + 1) % RATE == 0 {
82-
state = crate::poseidon2_permutation(state, 4);
83-
}
79+
let mut states: [[Field; 4]; N / RATE + 1] = [[0; 4]; N / RATE + 1];
80+
states[0] = state;
81+
82+
// process all full RATE-sized chunks, storing state after each permutation
83+
for chunk_idx in 0..(N / RATE) {
84+
for i in 0..RATE {
85+
state[i] += input[chunk_idx * RATE + i];
86+
}
87+
state = crate::poseidon2_permutation(state, 4);
88+
states[chunk_idx + 1] = state;
89+
}
90+
91+
// get state at the last full block before in_len
92+
let first_partially_filled_chunk = in_len / RATE;
93+
state = states[first_partially_filled_chunk];
94+
95+
// handle remaining elements after last full RATE-sized chunk
96+
let remainder_start = (in_len / RATE) * RATE;
97+
for j in 0..RATE {
98+
let idx = remainder_start + j;
99+
if idx < in_len {
100+
state[j] += input[idx];
84101
}
85102
}
86103
}
87104

88-
// Always run final permutation unless we just completed a full chunk
105+
// always run final permutation unless we just completed a full chunk
89106
// still need to permute once if in_len is 0
90107
if (in_len == 0) | (in_len % RATE != 0) {
91108
state = crate::poseidon2_permutation(state, 4)

src/tests.nr

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::poseidon;
1+
use super::{poseidon, poseidon2};
22

33
#[test]
44
fn reference_impl_test_vectors() {
@@ -65,3 +65,27 @@ fn test_poseidon_hash_8() {
6565
let expected = 0x2921ab9bd0140cbc98e40395c0fefb40337a4d54fbbecd9a4d43b3d8d0c4d8d1;
6666
assert_eq(hash, expected);
6767
}
68+
69+
#[test]
70+
fn test_poseidon2_1() {
71+
let input = [1000];
72+
let hash = poseidon2::Poseidon2::hash(input, input.len());
73+
let expected = 0x16433a80e26a23547e25d61dd95fd5793d1ca2dcd78ae64cd146d3b99a35fa7c;
74+
assert_eq(hash, expected);
75+
}
76+
77+
#[test]
78+
fn test_poseidon2_2() {
79+
let input = [1000, 2000];
80+
let hash = poseidon2::Poseidon2::hash(input, input.len());
81+
let expected = 0x118d5a5ecb25dafe99eb45cb196604a23d0b7c0cbd0c2be29e0787e59b7a1d8a;
82+
assert_eq(hash, expected);
83+
}
84+
85+
#[test]
86+
fn test_poseidon2_3() {
87+
let input = [1000, 2000, 3000];
88+
let hash = poseidon2::Poseidon2::hash(input, input.len());
89+
let expected = 0x0f1badcd0d52ced816fb6e6826fdf66ada038135d53cbb993f320ca6529223cd;
90+
assert_eq(hash, expected);
91+
}

0 commit comments

Comments
 (0)