1use thiserror::Error;
6
7pub const HASH_BYTES: usize = 32;
9
10#[derive(Error, Debug)]
11pub enum PoseidonSyscallError {
12 #[error("Invalid parameters.")]
13 InvalidParameters,
14 #[error("Invalid endianness.")]
15 InvalidEndianness,
16 #[error("Invalid number of inputs. Maximum allowed is 12.")]
17 InvalidNumberOfInputs,
18 #[error("Input is an empty slice.")]
19 EmptyInput,
20 #[error(
21 "Invalid length of the input. The length matching the modulus of the prime field is 32."
22 )]
23 InvalidInputLength,
24 #[error("Failed to convert bytest into a prime field element.")]
25 BytesToPrimeFieldElement,
26 #[error("Input is larger than the modulus of the prime field.")]
27 InputLargerThanModulus,
28 #[error("Failed to convert a vector of bytes into an array.")]
29 VecToArray,
30 #[error("Failed to convert the number of inputs from u64 to u8.")]
31 U64Tou8,
32 #[error("Failed to convert bytes to BigInt")]
33 BytesToBigInt,
34 #[error("Invalid width. Choose a width between 2 and 16 for 1 to 15 inputs.")]
35 InvalidWidthCircom,
36 #[error("Unexpected error")]
37 Unexpected,
38}
39
40impl From<u64> for PoseidonSyscallError {
41 fn from(error: u64) -> Self {
42 match error {
43 1 => PoseidonSyscallError::InvalidParameters,
44 2 => PoseidonSyscallError::InvalidEndianness,
45 3 => PoseidonSyscallError::InvalidNumberOfInputs,
46 4 => PoseidonSyscallError::EmptyInput,
47 5 => PoseidonSyscallError::InvalidInputLength,
48 6 => PoseidonSyscallError::BytesToPrimeFieldElement,
49 7 => PoseidonSyscallError::InputLargerThanModulus,
50 8 => PoseidonSyscallError::VecToArray,
51 9 => PoseidonSyscallError::U64Tou8,
52 10 => PoseidonSyscallError::BytesToBigInt,
53 11 => PoseidonSyscallError::InvalidWidthCircom,
54 _ => PoseidonSyscallError::Unexpected,
55 }
56 }
57}
58
59impl From<PoseidonSyscallError> for u64 {
60 fn from(error: PoseidonSyscallError) -> Self {
61 match error {
62 PoseidonSyscallError::InvalidParameters => 1,
63 PoseidonSyscallError::InvalidEndianness => 2,
64 PoseidonSyscallError::InvalidNumberOfInputs => 3,
65 PoseidonSyscallError::EmptyInput => 4,
66 PoseidonSyscallError::InvalidInputLength => 5,
67 PoseidonSyscallError::BytesToPrimeFieldElement => 6,
68 PoseidonSyscallError::InputLargerThanModulus => 7,
69 PoseidonSyscallError::VecToArray => 8,
70 PoseidonSyscallError::U64Tou8 => 9,
71 PoseidonSyscallError::BytesToBigInt => 10,
72 PoseidonSyscallError::InvalidWidthCircom => 11,
73 PoseidonSyscallError::Unexpected => 12,
74 }
75 }
76}
77
78#[repr(u64)]
93pub enum Parameters {
94 Bn254X5 = 0,
105}
106
107impl TryFrom<u64> for Parameters {
108 type Error = PoseidonSyscallError;
109
110 fn try_from(value: u64) -> Result<Self, Self::Error> {
111 match value {
112 x if x == Parameters::Bn254X5 as u64 => Ok(Parameters::Bn254X5),
113 _ => Err(PoseidonSyscallError::InvalidParameters),
114 }
115 }
116}
117
118impl From<Parameters> for u64 {
119 fn from(value: Parameters) -> Self {
120 match value {
121 Parameters::Bn254X5 => 0,
122 }
123 }
124}
125
126#[repr(u64)]
128pub enum Endianness {
129 BigEndian = 0,
131 LittleEndian,
133}
134
135impl TryFrom<u64> for Endianness {
136 type Error = PoseidonSyscallError;
137
138 fn try_from(value: u64) -> Result<Self, Self::Error> {
139 match value {
140 x if x == Endianness::BigEndian as u64 => Ok(Endianness::BigEndian),
141 x if x == Endianness::LittleEndian as u64 => Ok(Endianness::LittleEndian),
142 _ => Err(PoseidonSyscallError::InvalidEndianness),
143 }
144 }
145}
146
147impl From<Endianness> for u64 {
148 fn from(value: Endianness) -> Self {
149 match value {
150 Endianness::BigEndian => 0,
151 Endianness::LittleEndian => 1,
152 }
153 }
154}
155
156#[repr(transparent)]
158pub struct PoseidonHash(pub [u8; HASH_BYTES]);
159
160impl PoseidonHash {
161 pub fn new(hash_array: [u8; HASH_BYTES]) -> Self {
162 Self(hash_array)
163 }
164
165 pub fn to_bytes(&self) -> [u8; HASH_BYTES] {
166 self.0
167 }
168}
169
170#[allow(unused_variables)]
202pub fn hashv(
203 parameters: Parameters,
206 endianness: Endianness,
207 vals: &[&[u8]],
208) -> Result<PoseidonHash, PoseidonSyscallError> {
209 #[cfg(not(target_os = "solana"))]
212 {
213 use {
214 ark_bn254::Fr,
215 light_poseidon::{Poseidon, PoseidonBytesHasher, PoseidonError},
216 };
217
218 impl From<PoseidonError> for PoseidonSyscallError {
219 fn from(error: PoseidonError) -> Self {
220 match error {
221 PoseidonError::InvalidNumberOfInputs { .. } => {
222 PoseidonSyscallError::InvalidNumberOfInputs
223 }
224 PoseidonError::EmptyInput => PoseidonSyscallError::EmptyInput,
225 PoseidonError::InvalidInputLength { .. } => {
226 PoseidonSyscallError::InvalidInputLength
227 }
228 PoseidonError::BytesToPrimeFieldElement { .. } => {
229 PoseidonSyscallError::BytesToPrimeFieldElement
230 }
231 PoseidonError::InputLargerThanModulus => {
232 PoseidonSyscallError::InputLargerThanModulus
233 }
234 PoseidonError::VecToArray => PoseidonSyscallError::VecToArray,
235 PoseidonError::U64Tou8 => PoseidonSyscallError::U64Tou8,
236 PoseidonError::BytesToBigInt => PoseidonSyscallError::BytesToBigInt,
237 PoseidonError::InvalidWidthCircom { .. } => {
238 PoseidonSyscallError::InvalidWidthCircom
239 }
240 }
241 }
242 }
243
244 let mut hasher =
245 Poseidon::<Fr>::new_circom(vals.len()).map_err(PoseidonSyscallError::from)?;
246 let res = match endianness {
247 Endianness::BigEndian => hasher.hash_bytes_be(vals),
248 Endianness::LittleEndian => hasher.hash_bytes_le(vals),
249 }
250 .map_err(PoseidonSyscallError::from)?;
251
252 Ok(PoseidonHash(res))
253 }
254 #[cfg(target_os = "solana")]
256 {
257 let mut hash_result = [0; HASH_BYTES];
258 let result = unsafe {
259 crate::syscalls::sol_poseidon(
260 parameters.into(),
261 endianness.into(),
262 vals as *const _ as *const u8,
263 vals.len() as u64,
264 &mut hash_result as *mut _ as *mut u8,
265 )
266 };
267
268 match result {
269 0 => Ok(PoseidonHash::new(hash_result)),
270 e => Err(PoseidonSyscallError::from(e)),
271 }
272 }
273}
274
275pub fn hash(
306 parameters: Parameters,
307 endianness: Endianness,
308 val: &[u8],
309) -> Result<PoseidonHash, PoseidonSyscallError> {
310 hashv(parameters, endianness, &[val])
311}
312
313#[cfg(test)]
314mod tests {
315 use super::*;
316
317 #[test]
318 fn test_poseidon_input_ones_be() {
319 let input = [1u8; 32];
320
321 let hash = hash(Parameters::Bn254X5, Endianness::BigEndian, &input).unwrap();
322 assert_eq!(
323 hash.to_bytes(),
324 [
325 5, 191, 172, 229, 129, 238, 97, 119, 204, 25, 198, 197, 99, 99, 166, 136, 130, 241,
326 30, 132, 7, 172, 99, 157, 185, 145, 224, 210, 127, 27, 117, 230
327 ]
328 );
329 }
330
331 #[test]
332 fn test_poseidon_input_ones_le() {
333 let input = [1u8; 32];
334
335 let hash = hash(Parameters::Bn254X5, Endianness::LittleEndian, &input).unwrap();
336 assert_eq!(
337 hash.to_bytes(),
338 [
339 230, 117, 27, 127, 210, 224, 145, 185, 157, 99, 172, 7, 132, 30, 241, 130, 136,
340 166, 99, 99, 197, 198, 25, 204, 119, 97, 238, 129, 229, 172, 191, 5
341 ],
342 );
343 }
344
345 #[test]
346 fn test_poseidon_input_ones_twos_be() {
347 let input1 = [1u8; 32];
348 let input2 = [2u8; 32];
349
350 let hash = hashv(
351 Parameters::Bn254X5,
352 Endianness::BigEndian,
353 &[&input1, &input2],
354 )
355 .unwrap();
356 assert_eq!(
357 hash.to_bytes(),
358 [
359 13, 84, 225, 147, 143, 138, 140, 28, 125, 235, 94, 3, 85, 242, 99, 25, 32, 123,
360 132, 254, 156, 162, 206, 27, 38, 231, 53, 200, 41, 130, 25, 144
361 ]
362 );
363 }
364
365 #[test]
366 fn test_poseidon_input_ones_twos_le() {
367 let input1 = [1u8; 32];
368 let input2 = [2u8; 32];
369
370 let hash = hashv(
371 Parameters::Bn254X5,
372 Endianness::LittleEndian,
373 &[&input1, &input2],
374 )
375 .unwrap();
376 assert_eq!(
377 hash.to_bytes(),
378 [
379 144, 25, 130, 41, 200, 53, 231, 38, 27, 206, 162, 156, 254, 132, 123, 32, 25, 99,
380 242, 85, 3, 94, 235, 125, 28, 140, 138, 143, 147, 225, 84, 13
381 ]
382 );
383 }
384
385 #[test]
386 fn test_poseidon_input_one() {
387 let input = [
388 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
389 0, 0, 1,
390 ];
391
392 let expected_hashes = [
393 [
394 41, 23, 97, 0, 234, 169, 98, 189, 193, 254, 108, 101, 77, 106, 60, 19, 14, 150,
395 164, 209, 22, 139, 51, 132, 139, 137, 125, 197, 2, 130, 1, 51,
396 ],
397 [
398 0, 122, 243, 70, 226, 211, 4, 39, 158, 121, 224, 169, 243, 2, 63, 119, 18, 148,
399 167, 138, 203, 112, 231, 63, 144, 175, 226, 124, 173, 64, 30, 129,
400 ],
401 [
402 2, 192, 6, 110, 16, 167, 42, 189, 43, 51, 195, 178, 20, 203, 62, 129, 188, 177,
403 182, 227, 9, 97, 205, 35, 194, 2, 177, 134, 115, 191, 37, 67,
404 ],
405 [
406 8, 44, 156, 55, 10, 13, 36, 244, 65, 111, 188, 65, 74, 55, 104, 31, 120, 68, 45,
407 39, 216, 99, 133, 153, 28, 23, 214, 252, 12, 75, 125, 113,
408 ],
409 [
410 16, 56, 150, 5, 174, 104, 141, 79, 20, 219, 133, 49, 34, 196, 125, 102, 168, 3,
411 199, 43, 65, 88, 156, 177, 191, 134, 135, 65, 178, 6, 185, 187,
412 ],
413 [
414 42, 115, 246, 121, 50, 140, 62, 171, 114, 74, 163, 229, 189, 191, 80, 179, 144, 53,
415 215, 114, 159, 19, 91, 151, 9, 137, 15, 133, 197, 220, 94, 118,
416 ],
417 [
418 34, 118, 49, 10, 167, 243, 52, 58, 40, 66, 20, 19, 157, 157, 169, 89, 190, 42, 49,
419 178, 199, 8, 165, 248, 25, 84, 178, 101, 229, 58, 48, 184,
420 ],
421 [
422 23, 126, 20, 83, 196, 70, 225, 176, 125, 43, 66, 51, 66, 81, 71, 9, 92, 79, 202,
423 187, 35, 61, 35, 11, 109, 70, 162, 20, 217, 91, 40, 132,
424 ],
425 [
426 14, 143, 238, 47, 228, 157, 163, 15, 222, 235, 72, 196, 46, 187, 68, 204, 110, 231,
427 5, 95, 97, 251, 202, 94, 49, 59, 138, 95, 202, 131, 76, 71,
428 ],
429 [
430 46, 196, 198, 94, 99, 120, 171, 140, 115, 48, 133, 79, 74, 112, 119, 193, 255, 146,
431 96, 228, 72, 133, 196, 184, 29, 209, 49, 173, 58, 134, 205, 150,
432 ],
433 [
434 0, 113, 61, 65, 236, 166, 53, 241, 23, 212, 236, 188, 235, 95, 58, 102, 220, 65,
435 66, 235, 112, 181, 103, 101, 188, 53, 143, 27, 236, 64, 187, 155,
436 ],
437 [
438 20, 57, 11, 224, 186, 239, 36, 155, 212, 124, 101, 221, 172, 101, 194, 229, 46,
439 133, 19, 192, 129, 193, 205, 114, 201, 128, 6, 9, 142, 154, 143, 190,
440 ],
441 ];
442
443 for (i, expected_hash) in expected_hashes.iter().enumerate() {
444 let inputs = vec![&input; i + 1]
445 .into_iter()
446 .map(|arr| &arr[..])
447 .collect::<Vec<_>>();
448 let hash = hashv(Parameters::Bn254X5, Endianness::BigEndian, &inputs).unwrap();
449 assert_eq!(hash.to_bytes(), *expected_hash);
450 }
451 }
452}