1use thiserror::{self, Error};
2
3use crate::{
4 errors::HasherError,
5 zero_bytes::{poseidon::ZERO_BYTES, ZeroBytes},
6 zero_indexed_leaf::poseidon::ZERO_INDEXED_LEAF,
7 Hash, Hasher,
8};
9
10#[derive(Debug, Error, PartialEq)]
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#[derive(Debug, Clone, Copy)]
79pub struct Poseidon;
80
81impl Hasher for Poseidon {
82 const ID: u8 = 0;
83
84 fn hash(val: &[u8]) -> Result<Hash, HasherError> {
85 Self::hashv(&[val])
86 }
87
88 fn hashv(_vals: &[&[u8]]) -> Result<Hash, HasherError> {
89 #[cfg(all(not(target_os = "solana"), feature = "poseidon"))]
92 {
93 use ark_bn254::Fr;
94 use light_poseidon::{Poseidon, PoseidonBytesHasher};
95
96 let mut hasher = Poseidon::<Fr>::new_circom(_vals.len())?;
97 let res = hasher.hash_bytes_be(_vals)?;
98
99 Ok(res)
100 }
101 #[cfg(all(not(target_os = "solana"), not(feature = "poseidon")))]
102 {
103 Err(HasherError::PoseidonFeatureNotEnabled)
104 }
105 #[cfg(target_os = "solana")]
107 {
108 use crate::HASH_BYTES;
109 let mut hash_result = [0; HASH_BYTES];
117 let result = unsafe {
118 crate::syscalls::sol_poseidon(
119 0, 0, _vals as *const _ as *const u8,
122 _vals.len() as u64,
123 &mut hash_result as *mut _ as *mut u8,
124 )
125 };
126
127 match result {
128 0 => Ok(hash_result),
129 e => Err(HasherError::from(PoseidonSyscallError::from(e))),
130 }
131 }
132 }
133
134 fn zero_bytes() -> ZeroBytes {
135 ZERO_BYTES
136 }
137
138 fn zero_indexed_leaf() -> [u8; 32] {
139 ZERO_INDEXED_LEAF
140 }
141}