Skip to content

Commit eb82947

Browse files
authored
Implement Poseidon, Poseidon2 (#1618)
### What Implements Poseidon, Poseidon: - two new hash functions under `Crypto` which performs Poseidon/Poseidon2 hashing. Underneath they wrap the permutation host function call with a sponge implementation - provide reference parameters for Poseidon/Poseidon2 for bn254 - results match both circom (for Poseidon) and noir (for Poseidon2) with test cases - also expose the two permutation host functions in `CryptoHazmat`, to support custom sponge implementation / parameter sets. ### Why [TODO: Why this change is being made. Include any context required to understand the why.] ### Known limitations For Poseidon only supports `hash2` (t=3, hashing two inputs into one output) for now. Only support BN254 for now. To add support for the rest, need to generate and import those parameters, which will be done in a followup.
1 parent 3cf10a9 commit eb82947

File tree

7 files changed

+1260
-1
lines changed

7 files changed

+1260
-1
lines changed

soroban-sdk/src/crypto.rs

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,19 @@
33
use crate::{
44
env::internal::{self, BytesObject},
55
unwrap::UnwrapInfallible,
6-
Bytes, BytesN, ConversionError, Env, IntoVal, TryFromVal, Val,
6+
Bytes, BytesN, ConversionError, Env, IntoVal, Symbol, TryFromVal, TryIntoVal, Val, Vec, U256,
77
};
88

99
pub mod bls12_381;
1010
pub mod bn254;
11+
pub(crate) mod poseidon2_params;
12+
pub mod poseidon2_sponge;
13+
pub(crate) mod poseidon_params;
14+
pub mod poseidon_sponge;
15+
pub use bn254::Fr as BnScalar;
16+
pub use poseidon2_sponge::Poseidon2Sponge;
17+
pub use poseidon_sponge::PoseidonSponge;
18+
1119
/// A `BytesN<N>` generated by a cryptographic hash function.
1220
///
1321
/// The `Hash<N>` type contains a `BytesN<N>` and can only be constructed in
@@ -189,6 +197,28 @@ impl Crypto {
189197
pub fn bn254(&self) -> bn254::Bn254 {
190198
bn254::Bn254::new(self.env())
191199
}
200+
201+
/// Performs a Poseidon hash using a sponge construction
202+
pub fn poseidon_hash(&self, inputs: &Vec<U256>, field: Symbol) -> U256 {
203+
// The initial value for the capacity element initialized with 0 for standard Poseidon
204+
let iv = U256::from_u32(&self.env, 0);
205+
let mut sponge = PoseidonSponge::new(&self.env, iv, field);
206+
for input in inputs.iter() {
207+
sponge.absorb(input);
208+
}
209+
sponge.squeeze()
210+
}
211+
212+
/// Performs a poseidon2 hash with a sponge construction equivalent to the one in the Barretenberg proving system
213+
pub fn poseidon2_hash(&self, inputs: &Vec<U256>, field: Symbol) -> U256 {
214+
// The initial value for the capacity element initialized with `input.len() * 2^24` for Poseidon2
215+
let iv = U256::from_u128(&self.env, (inputs.len() as u128) << 64);
216+
let mut sponge = Poseidon2Sponge::new(&self.env, iv, field);
217+
for input in inputs.iter() {
218+
sponge.absorb(input);
219+
}
220+
sponge.squeeze()
221+
}
192222
}
193223

194224
/// # ⚠️ Hazardous Materials
@@ -265,4 +295,68 @@ impl CryptoHazmat {
265295
)
266296
.unwrap_infallible();
267297
}
298+
299+
/// Performs a Poseidon permutation on the input state vector.
300+
///
301+
/// WARNING: This is a low-level permutation function. Most users should use
302+
/// the higher-level `poseidon_hash` function instead.
303+
pub fn poseidon_permutation(
304+
&self,
305+
input: &Vec<U256>,
306+
field: Symbol,
307+
t: u32,
308+
d: u32,
309+
rounds_f: u32,
310+
rounds_p: u32,
311+
mds: &Vec<Vec<U256>>,
312+
round_constants: &Vec<Vec<U256>>,
313+
) -> Vec<U256> {
314+
let env = self.env();
315+
let result = internal::Env::poseidon_permutation(
316+
env,
317+
input.to_object(),
318+
field.to_symbol_val(),
319+
t.into(),
320+
d.into(),
321+
rounds_f.into(),
322+
rounds_p.into(),
323+
mds.to_object(),
324+
round_constants.to_object(),
325+
)
326+
.unwrap_infallible();
327+
328+
result.try_into_val(env).unwrap_infallible()
329+
}
330+
331+
/// Performs a Poseidon2 permutation on the input state vector.
332+
///
333+
/// WARNING: This is a low-level permutation function. Most users should use
334+
/// the higher-level `poseidon2_hash` function instead.
335+
pub fn poseidon2_permutation(
336+
&self,
337+
input: &Vec<U256>,
338+
field: Symbol,
339+
t: u32,
340+
d: u32,
341+
rounds_f: u32,
342+
rounds_p: u32,
343+
mat_internal_diag_m_1: &Vec<U256>,
344+
round_constants: &Vec<Vec<U256>>,
345+
) -> Vec<U256> {
346+
let env = self.env();
347+
let result = internal::Env::poseidon2_permutation(
348+
env,
349+
input.to_object(),
350+
field.to_symbol_val(),
351+
t.into(),
352+
d.into(),
353+
rounds_f.into(),
354+
rounds_p.into(),
355+
mat_internal_diag_m_1.to_object(),
356+
round_constants.to_object(),
357+
)
358+
.unwrap_infallible();
359+
360+
result.try_into_val(env).unwrap_infallible()
361+
}
268362
}

0 commit comments

Comments
 (0)