-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Poseidon hashing #11
base: main
Are you sure you want to change the base?
Poseidon hashing #11
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,70 @@ use crate::poseidon::params::PARTIAL_ROUND_KEYS_OPTIMIZED; | |
use crate::utils::Mat3x3; | ||
use ark_ff::Field; | ||
use num_bigint::BigUint; | ||
use ruint::aliases::U256; | ||
|
||
pub fn poseidon_hash_single(x: Fp) -> Fp { | ||
let instance = PoseidonInstance { | ||
index: 0, | ||
input0: U256::from_limbs(x.0 .0), | ||
input1: U256::from(0), | ||
input2: U256::from(0), | ||
}; | ||
let result = InstanceTrace::new(instance); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using InstanceTrace will decrease performance since it is not optimised for speed but rather obtaining the values needed in the STARK trace. I think this PR should implement a seperate poseidon hash function that's focussed more on performance (i.e. doesn't keep track of the state of rounds in the permutation). Let's move all the added methods and tests in this file to crypto/src/hash/poseidon.rs. I'd suggest using the parameters from builtins/src/poseidon/params.rs and using |
||
|
||
result.output0 | ||
} | ||
|
||
pub fn poseidon_hash(x: Fp, y: Fp) -> Fp { | ||
let instance = PoseidonInstance { | ||
index: 0, | ||
input0: U256::from_limbs(x.0 .0), | ||
input1: U256::from_limbs(y.0 .0), | ||
input2: U256::from(0), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
}; | ||
let result = InstanceTrace::new(instance); | ||
|
||
result.output0 | ||
} | ||
|
||
pub fn poseidon_hash_many(elements: Vec<Fp>) -> Fp { | ||
let instance = PoseidonInstance { | ||
index: 0, | ||
input0: U256::from(0), | ||
input1: U256::from(0), | ||
input2: U256::from(0), | ||
}; | ||
let mut state = InstanceTrace::new(instance); | ||
|
||
let mut i = 0; | ||
loop { | ||
if i == elements.len() { | ||
let result = InstanceTrace::new(PoseidonInstance { | ||
index: state.instance.index, | ||
input0: U256::from_limbs((state.output0 + Fp::from(1)).0 .0), | ||
input1: U256::from_limbs(state.output1.0 .0), | ||
input2: U256::from_limbs(state.output2.0 .0), | ||
}); | ||
return result.output0; | ||
} else if i == elements.len() - 1 { | ||
let result = InstanceTrace::new(PoseidonInstance { | ||
index: state.instance.index, | ||
input0: U256::from_limbs((state.output0 + elements[i]).0 .0), | ||
input1: U256::from_limbs((state.output1 + Fp::from(1)).0 .0), | ||
input2: U256::from_limbs(state.output2.0 .0), | ||
}); | ||
return result.output0; | ||
} else { | ||
state = InstanceTrace::new(PoseidonInstance { | ||
index: state.instance.index, | ||
input0: U256::from_limbs((state.output0 + elements[i]).0 .0), | ||
input1: U256::from_limbs((state.output1 + elements[i + 1]).0 .0), | ||
input2: U256::from_limbs(state.output2.0 .0), | ||
}); | ||
} | ||
i += 2; | ||
} | ||
} | ||
|
||
/// Stores the states within a full round | ||
#[derive(Clone, Copy, Debug)] | ||
|
@@ -243,7 +307,8 @@ fn _calc_optimized_partial_round_keys() -> [[Fp; 3]; NUM_PARTIAL_ROUNDS] { | |
|
||
#[cfg(test)] | ||
mod tests { | ||
use crate::poseidon::permute; | ||
use crate::poseidon::{permute, poseidon_hash_many}; | ||
use ark_ff::BigInt; | ||
use ark_ff::MontFp as Fp; | ||
use ark_ff::Field; | ||
use ministark_gpu::fields::p3618502788666131213697322783095070105623107215331596699973092056135872020481::ark::Fp; | ||
|
@@ -259,4 +324,70 @@ mod tests { | |
|
||
assert_eq!(expected, permute([Fp::ZERO, Fp::ZERO, Fp::ZERO])); | ||
} | ||
|
||
#[test] | ||
fn poseidon_hash_many_3_example() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. test name can just be "hash_3_values". Let's update the other test names as well. Also we should have tests for hash_single and poseidon_hash |
||
let elements = vec![Fp!("0"), Fp!("0"), Fp!("0")]; | ||
|
||
let expected = BigInt::new([ | ||
14465169880788163794, | ||
3725699649495491964, | ||
13957675534445258432, | ||
241034705846384105, | ||
]); | ||
|
||
assert_eq!(expected, poseidon_hash_many(elements).0); | ||
} | ||
|
||
#[test] | ||
fn poseidon_hash_many_4_example() { | ||
let elements = vec![Fp!("0"), Fp!("0"), Fp!("0"), Fp!("0")]; | ||
|
||
let expected = BigInt::new([ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How are all the expected values obtained? |
||
8177194887955547932, | ||
6869494578799689646, | ||
3880460167861838970, | ||
331313552213051804, | ||
]); | ||
|
||
assert_eq!(expected, poseidon_hash_many(elements).0); | ||
} | ||
|
||
#[test] | ||
fn poseidon_hash_many_5_example() { | ||
let elements = vec![Fp!("0"), Fp!("0"), Fp!("0"), Fp!("0"), Fp!("0")]; | ||
|
||
let expected = BigInt::new([ | ||
14872330746557636911, | ||
10787657887407825984, | ||
8559225264217750252, | ||
304103888309894648, | ||
]); | ||
|
||
assert_eq!(expected, poseidon_hash_many(elements).0); | ||
} | ||
|
||
#[test] | ||
fn poseidon_hash_many_9_example() { | ||
let elements = vec![ | ||
Fp!("0"), | ||
Fp!("0"), | ||
Fp!("0"), | ||
Fp!("0"), | ||
Fp!("0"), | ||
Fp!("0"), | ||
Fp!("0"), | ||
Fp!("0"), | ||
Fp!("0"), | ||
]; | ||
|
||
let expected = BigInt::new([ | ||
6218683884595264045, | ||
16033324973008454317, | ||
15358347862863079556, | ||
557108717180864526, | ||
]); | ||
|
||
assert_eq!(expected, poseidon_hash_many(elements).0); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,74 @@ | ||||||||||||||
use std::fmt::Display; | ||||||||||||||
use std::ops::Deref; | ||||||||||||||
use std::iter::Iterator; | ||||||||||||||
use ark_serialize::CanonicalDeserialize; | ||||||||||||||
use ark_serialize::CanonicalSerialize; | ||||||||||||||
use builtins::poseidon::poseidon_hash_many; | ||||||||||||||
use digest::HashMarker; | ||||||||||||||
use ministark::hash::Digest; | ||||||||||||||
use ministark::hash::ElementHashFn; | ||||||||||||||
use ministark::hash::HashFn; | ||||||||||||||
use ministark_gpu::fields::p3618502788666131213697322783095070105623107215331596699973092056135872020481::ark::Fp; | ||||||||||||||
use num_bigint::BigUint; | ||||||||||||||
use ruint::aliases::U256; | ||||||||||||||
|
||||||||||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, CanonicalDeserialize, CanonicalSerialize)] | ||||||||||||||
pub struct PoseidonDigest(pub Fp); | ||||||||||||||
|
||||||||||||||
impl HashMarker for PoseidonDigest {} | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this can be removed |
||||||||||||||
|
||||||||||||||
impl Display for PoseidonDigest { | ||||||||||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||||||||||
self.0.fmt(f) | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
impl Digest for PoseidonDigest { | ||||||||||||||
fn as_bytes(&self) -> [u8; 32] { | ||||||||||||||
let num = U256::from(BigUint::from(self.0)); | ||||||||||||||
num.to_be_bytes::<32>() | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
impl Deref for PoseidonDigest { | ||||||||||||||
type Target = Fp; | ||||||||||||||
|
||||||||||||||
fn deref(&self) -> &Self::Target { | ||||||||||||||
&self.0 | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
impl From<Fp> for PoseidonDigest { | ||||||||||||||
fn from(value: Fp) -> Self { | ||||||||||||||
PoseidonDigest(value) | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
pub struct PoseidonHashFn; | ||||||||||||||
|
||||||||||||||
impl HashFn for PoseidonHashFn { | ||||||||||||||
type Digest = PoseidonDigest; | ||||||||||||||
const COLLISION_RESISTANCE: u32 = 125; | ||||||||||||||
|
||||||||||||||
fn hash(_bytes: impl IntoIterator<Item = u8>) -> PoseidonDigest { | ||||||||||||||
unreachable!() | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
fn hash_chunks<'a>(_chunks: impl IntoIterator<Item = &'a [u8]>) -> Self::Digest { | ||||||||||||||
unreachable!() | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
fn merge(v0: &PoseidonDigest, v1: &PoseidonDigest) -> PoseidonDigest { | ||||||||||||||
PoseidonDigest(poseidon_hash_many([**v0, **v1].to_vec())) | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Due to how frequently this function is called it has a big impact on the overall performance of the prover. Allocations on the heap every invocation makes this function much more expensive than it needs to be. Let's implement this without any Vec |
||||||||||||||
} | ||||||||||||||
|
||||||||||||||
fn merge_with_int(seed: &PoseidonDigest, value: u64) -> PoseidonDigest { | ||||||||||||||
PoseidonDigest(poseidon_hash_many([**seed, value.into()].to_vec())) | ||||||||||||||
} | ||||||||||||||
Comment on lines
+65
to
+67
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
} | ||||||||||||||
|
||||||||||||||
impl ElementHashFn<Fp> for PoseidonHashFn { | ||||||||||||||
fn hash_elements(elements: impl IntoIterator<Item = Fp>) -> PoseidonDigest { | ||||||||||||||
PoseidonDigest(poseidon_hash_many(elements.into_iter().collect())) | ||||||||||||||
} | ||||||||||||||
Comment on lines
+71
to
+73
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's make this function behave the same way as poseidon_hash_many_given_poseidon_perm and use the same padding rule |
||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might need to set
input2
to 1"the capacity element is initialized to 1"