light_hasher/
poseidon.rs

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        // Perform the calculation inline, calling this from within a program is
90        // not supported.
91        #[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        // Call via a system call to perform the calculation.
106        #[cfg(target_os = "solana")]
107        {
108            use crate::HASH_BYTES;
109            // TODO: reenable once LightHasher refactor is merged
110            // solana_program::msg!("remove len check onchain.");
111            // for val in vals {
112            //     if val.len() != 32 {
113            //         return Err(HasherError::InvalidInputLength(val.len()));
114            //     }
115            // }
116            let mut hash_result = [0; HASH_BYTES];
117            let result = unsafe {
118                crate::syscalls::sol_poseidon(
119                    0, // bn254
120                    0, // big-endian
121                    _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}