Skip to main content

filecoin_hashers/
poseidon.rs

1use std::cmp::Ordering;
2use std::hash::{Hash as StdHash, Hasher as StdHasher};
3use std::panic::panic_any;
4
5use anyhow::ensure;
6use bellperson::{
7    gadgets::{boolean::Boolean, num::AllocatedNum},
8    ConstraintSystem, SynthesisError,
9};
10use blstrs::Scalar as Fr;
11use ff::{Field, PrimeField};
12use generic_array::typenum::{marker_traits::Unsigned, U2};
13use merkletree::{
14    hash::{Algorithm as LightAlgorithm, Hashable},
15    merkle::Element,
16};
17use neptune::{circuit::poseidon_hash, poseidon::Poseidon};
18use rand::RngCore;
19use serde::{Deserialize, Serialize};
20
21use crate::types::{
22    Domain, HashFunction, Hasher, PoseidonArity, PoseidonMDArity, POSEIDON_CONSTANTS_16,
23    POSEIDON_CONSTANTS_2, POSEIDON_CONSTANTS_4, POSEIDON_CONSTANTS_8, POSEIDON_MD_CONSTANTS,
24};
25
26#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
27pub struct PoseidonHasher {}
28
29impl Hasher for PoseidonHasher {
30    type Domain = PoseidonDomain;
31    type Function = PoseidonFunction;
32
33    fn name() -> String {
34        "poseidon_hasher".into()
35    }
36}
37
38#[derive(Copy, Clone, Debug, PartialEq, Eq)]
39pub struct PoseidonFunction(Fr);
40
41impl Default for PoseidonFunction {
42    fn default() -> PoseidonFunction {
43        PoseidonFunction(Fr::ZERO)
44    }
45}
46
47impl Hashable<PoseidonFunction> for Fr {
48    fn hash(&self, state: &mut PoseidonFunction) {
49        state.write(&self.to_repr());
50    }
51}
52
53impl Hashable<PoseidonFunction> for PoseidonDomain {
54    fn hash(&self, state: &mut PoseidonFunction) {
55        state.write(&self.0);
56    }
57}
58
59#[derive(Default, Copy, Clone, Debug, Serialize, Deserialize)]
60pub struct PoseidonDomain(<Fr as PrimeField>::Repr);
61
62impl AsRef<PoseidonDomain> for PoseidonDomain {
63    fn as_ref(&self) -> &PoseidonDomain {
64        self
65    }
66}
67
68impl StdHash for PoseidonDomain {
69    fn hash<H: StdHasher>(&self, state: &mut H) {
70        StdHash::hash(&self.0, state);
71    }
72}
73
74impl PartialEq for PoseidonDomain {
75    fn eq(&self, other: &Self) -> bool {
76        self.0 == other.0
77    }
78}
79
80impl Eq for PoseidonDomain {}
81
82impl Ord for PoseidonDomain {
83    #[inline(always)]
84    fn cmp(&self, other: &PoseidonDomain) -> Ordering {
85        (self.0).cmp(&other.0)
86    }
87}
88
89impl PartialOrd for PoseidonDomain {
90    #[inline(always)]
91    fn partial_cmp(&self, other: &PoseidonDomain) -> Option<Ordering> {
92        Some(self.cmp(other))
93    }
94}
95
96impl AsRef<[u8]> for PoseidonDomain {
97    #[inline]
98    fn as_ref(&self) -> &[u8] {
99        &self.0
100    }
101}
102
103impl Domain for PoseidonDomain {
104    fn into_bytes(&self) -> Vec<u8> {
105        self.0.to_vec()
106    }
107
108    fn try_from_bytes(raw: &[u8]) -> anyhow::Result<Self> {
109        ensure!(
110            raw.len() == PoseidonDomain::byte_len(),
111            "invalid amount of bytes"
112        );
113        let mut repr = <Fr as PrimeField>::Repr::default();
114        repr.copy_from_slice(raw);
115        Ok(PoseidonDomain(repr))
116    }
117
118    fn write_bytes(&self, dest: &mut [u8]) -> anyhow::Result<()> {
119        ensure!(
120            dest.len() == PoseidonDomain::byte_len(),
121            "invalid amount of bytes"
122        );
123        dest.copy_from_slice(&self.0);
124        Ok(())
125    }
126
127    fn random<R: RngCore>(rng: &mut R) -> Self {
128        // generating an Fr and converting it, to ensure we stay in the field
129        Fr::random(rng).into()
130    }
131}
132
133impl Element for PoseidonDomain {
134    fn byte_len() -> usize {
135        32
136    }
137
138    fn from_slice(bytes: &[u8]) -> Self {
139        match PoseidonDomain::try_from_bytes(bytes) {
140            Ok(res) => res,
141            Err(err) => panic_any(err),
142        }
143    }
144
145    fn copy_to_slice(&self, bytes: &mut [u8]) {
146        bytes.copy_from_slice(&self.0);
147    }
148}
149
150impl StdHasher for PoseidonFunction {
151    #[inline]
152    fn write(&mut self, msg: &[u8]) {
153        self.0 = Fr::from_repr_vartime(shared_hash(msg).0).expect("from_repr failure");
154    }
155
156    #[inline]
157    fn finish(&self) -> u64 {
158        unimplemented!()
159    }
160}
161
162fn shared_hash(data: &[u8]) -> PoseidonDomain {
163    // FIXME: We shouldn't unwrap here, but doing otherwise will require an interface change.
164    // We could truncate so `bytes_into_frs` cannot fail, then ensure `data` is always `fr_safe`.
165    let preimage = data
166        .chunks(32)
167        .map(|chunk| {
168            Fr::from_repr_vartime(PoseidonDomain::from_slice(chunk).0).expect("from_repr failure")
169        })
170        .collect::<Vec<_>>();
171
172    shared_hash_frs(&preimage).into()
173}
174
175fn shared_hash_frs(preimage: &[Fr]) -> Fr {
176    match preimage.len() {
177        2 => {
178            let mut p = Poseidon::new_with_preimage(preimage, &POSEIDON_CONSTANTS_2);
179            p.hash()
180        }
181        4 => {
182            let mut p = Poseidon::new_with_preimage(preimage, &POSEIDON_CONSTANTS_4);
183            p.hash()
184        }
185        8 => {
186            let mut p = Poseidon::new_with_preimage(preimage, &POSEIDON_CONSTANTS_8);
187            p.hash()
188        }
189        16 => {
190            let mut p = Poseidon::new_with_preimage(preimage, &POSEIDON_CONSTANTS_16);
191            p.hash()
192        }
193
194        _ => panic_any(format!(
195            "Unsupported arity for Poseidon hasher: {}",
196            preimage.len()
197        )),
198    }
199}
200
201impl HashFunction<PoseidonDomain> for PoseidonFunction {
202    fn hash(data: &[u8]) -> PoseidonDomain {
203        shared_hash(data)
204    }
205
206    fn hash2(a: &PoseidonDomain, b: &PoseidonDomain) -> PoseidonDomain {
207        let mut p =
208            Poseidon::new_with_preimage(&[(*a).into(), (*b).into()][..], &*POSEIDON_CONSTANTS_2);
209        let fr: Fr = p.hash();
210        fr.into()
211    }
212
213    fn hash_md(input: &[PoseidonDomain]) -> PoseidonDomain {
214        assert!(input.len() > 1, "hash_md needs more than one element.");
215        let arity = PoseidonMDArity::to_usize();
216
217        let mut p = Poseidon::new(&*POSEIDON_MD_CONSTANTS);
218
219        let fr_input = input
220            .iter()
221            .map(|x| Fr::from_repr_vartime(x.0).expect("from_repr failure"))
222            .collect::<Vec<_>>();
223
224        fr_input[1..]
225            .chunks(arity - 1)
226            .fold(fr_input[0], |acc, elts| {
227                p.reset();
228                p.input(acc).expect("input failure"); // These unwraps will panic iff arity is incorrect, but it was checked above.
229                elts.iter().for_each(|elt| {
230                    let _ = p.input(*elt).expect("input failure");
231                });
232                p.hash()
233            })
234            .into()
235    }
236
237    fn hash_leaf_circuit<CS: ConstraintSystem<Fr>>(
238        cs: CS,
239        left: &AllocatedNum<Fr>,
240        right: &AllocatedNum<Fr>,
241        _height: usize,
242    ) -> Result<AllocatedNum<Fr>, SynthesisError> {
243        let preimage = vec![left.clone(), right.clone()];
244
245        poseidon_hash::<CS, Fr, U2>(cs, preimage, U2::PARAMETERS())
246    }
247
248    fn hash_multi_leaf_circuit<Arity: 'static + PoseidonArity, CS: ConstraintSystem<Fr>>(
249        cs: CS,
250        leaves: &[AllocatedNum<Fr>],
251        _height: usize,
252    ) -> Result<AllocatedNum<Fr>, SynthesisError> {
253        let params = Arity::PARAMETERS();
254        poseidon_hash::<CS, Fr, Arity>(cs, leaves.to_vec(), params)
255    }
256
257    fn hash_md_circuit<CS: ConstraintSystem<Fr>>(
258        cs: &mut CS,
259        elements: &[AllocatedNum<Fr>],
260    ) -> Result<AllocatedNum<Fr>, SynthesisError> {
261        let params = PoseidonMDArity::PARAMETERS();
262        let arity = PoseidonMDArity::to_usize();
263
264        let mut hash = elements[0].clone();
265        let mut preimage = vec![hash.clone(); arity]; // Allocate. This will be overwritten.
266        for (hash_num, elts) in elements[1..].chunks(arity - 1).enumerate() {
267            preimage[0] = hash;
268            for (i, elt) in elts.iter().enumerate() {
269                preimage[i + 1] = elt.clone();
270            }
271            // any terminal padding
272            #[allow(clippy::needless_range_loop)]
273            for i in (elts.len() + 1)..arity {
274                preimage[i] =
275                    AllocatedNum::alloc(cs.namespace(|| format!("padding {}", i)), || Ok(Fr::ZERO))
276                        .expect("alloc failure");
277            }
278            let cs = cs.namespace(|| format!("hash md {}", hash_num));
279            hash = poseidon_hash::<_, Fr, PoseidonMDArity>(cs, preimage.clone(), params)?.clone();
280        }
281
282        Ok(hash)
283    }
284
285    fn hash_circuit<CS: ConstraintSystem<Fr>>(
286        _cs: CS,
287        _bits: &[Boolean],
288    ) -> Result<AllocatedNum<Fr>, SynthesisError> {
289        unimplemented!();
290    }
291
292    fn hash2_circuit<CS>(
293        cs: CS,
294        a: &AllocatedNum<Fr>,
295        b: &AllocatedNum<Fr>,
296    ) -> Result<AllocatedNum<Fr>, SynthesisError>
297    where
298        CS: ConstraintSystem<Fr>,
299    {
300        let preimage = vec![a.clone(), b.clone()];
301        poseidon_hash::<CS, Fr, U2>(cs, preimage, U2::PARAMETERS())
302    }
303}
304
305impl LightAlgorithm<PoseidonDomain> for PoseidonFunction {
306    #[inline]
307    fn hash(&mut self) -> PoseidonDomain {
308        self.0.into()
309    }
310
311    #[inline]
312    fn reset(&mut self) {
313        self.0 = Fr::ZERO;
314    }
315
316    fn leaf(&mut self, leaf: PoseidonDomain) -> PoseidonDomain {
317        leaf
318    }
319
320    fn node(
321        &mut self,
322        left: PoseidonDomain,
323        right: PoseidonDomain,
324        _height: usize,
325    ) -> PoseidonDomain {
326        shared_hash_frs(&[
327            Fr::from_repr_vartime(left.0).expect("from_repr failure"),
328            Fr::from_repr_vartime(right.0).expect("from_repr failure"),
329        ])
330        .into()
331    }
332
333    fn multi_node(&mut self, parts: &[PoseidonDomain], _height: usize) -> PoseidonDomain {
334        match parts.len() {
335            1 | 2 | 4 | 8 | 16 => shared_hash_frs(
336                &parts
337                    .iter()
338                    .enumerate()
339                    .map(|(i, x)| {
340                        if let Some(fr) = Fr::from_repr_vartime(x.0) {
341                            fr
342                        } else {
343                            panic_any(format!("from_repr failure at {}", i));
344                        }
345                    })
346                    .collect::<Vec<_>>(),
347            )
348            .into(),
349            arity => panic_any(format!("unsupported arity {}", arity)),
350        }
351    }
352}
353
354impl From<Fr> for PoseidonDomain {
355    #[inline]
356    fn from(val: Fr) -> Self {
357        PoseidonDomain(val.to_repr())
358    }
359}
360
361impl From<[u8; 32]> for PoseidonDomain {
362    #[inline]
363    fn from(val: [u8; 32]) -> Self {
364        PoseidonDomain(val)
365    }
366}
367
368impl From<PoseidonDomain> for [u8; 32] {
369    #[inline]
370    fn from(val: PoseidonDomain) -> Self {
371        val.0
372    }
373}
374
375impl From<PoseidonDomain> for Fr {
376    #[inline]
377    fn from(val: PoseidonDomain) -> Self {
378        Fr::from_repr_vartime(val.0).expect("from_repr failure")
379    }
380}
381
382#[cfg(test)]
383mod tests {
384    use super::*;
385
386    use bellperson::util_cs::test_cs::TestConstraintSystem;
387    use merkletree::{merkle::MerkleTree, store::VecStore};
388
389    fn u64s_to_u8s(u64s: [u64; 4]) -> [u8; 32] {
390        let mut bytes = [0u8; 32];
391        bytes[..8].copy_from_slice(&u64s[0].to_le_bytes());
392        bytes[8..16].copy_from_slice(&u64s[1].to_le_bytes());
393        bytes[16..24].copy_from_slice(&u64s[2].to_le_bytes());
394        bytes[24..].copy_from_slice(&u64s[3].to_le_bytes());
395        bytes
396    }
397
398    #[test]
399    fn test_path() {
400        let values = [
401            PoseidonDomain(Fr::ONE.to_repr()),
402            PoseidonDomain(Fr::ONE.to_repr()),
403            PoseidonDomain(Fr::ONE.to_repr()),
404            PoseidonDomain(Fr::ONE.to_repr()),
405        ];
406
407        let t = MerkleTree::<PoseidonDomain, PoseidonFunction, VecStore<_>, U2>::new(
408            values.iter().copied(),
409        )
410        .expect("merkle tree new failure");
411
412        let p = t.gen_proof(0).expect("gen_proof failure"); // create a proof for the first value =k Fr::one()
413
414        assert_eq!(*p.path(), vec![0, 0]);
415        assert!(p
416            .validate::<PoseidonFunction>()
417            .expect("failed to validate"));
418    }
419
420    // #[test]
421    // fn test_poseidon_quad() {
422    //     let leaves = [Fr::one(), Fr::zero(), Fr::zero(), Fr::one()];
423
424    //     assert_eq!(Fr::zero().to_repr(), shared_hash_frs(&leaves[..]).0);
425    // }
426
427    #[test]
428    fn test_poseidon_hasher() {
429        let leaves = [
430            PoseidonDomain(Fr::ONE.to_repr()),
431            PoseidonDomain(Fr::ZERO.to_repr()),
432            PoseidonDomain(Fr::ZERO.to_repr()),
433            PoseidonDomain(Fr::ONE.to_repr()),
434        ];
435
436        let t = MerkleTree::<PoseidonDomain, PoseidonFunction, VecStore<_>, U2>::new(
437            leaves.iter().copied(),
438        )
439        .expect("merkle tree new failure");
440
441        assert_eq!(t.leafs(), 4);
442
443        let mut a = PoseidonFunction::default();
444
445        assert_eq!(t.read_at(0).expect("read_at failure"), leaves[0]);
446        assert_eq!(t.read_at(1).expect("read_at failure"), leaves[1]);
447        assert_eq!(t.read_at(2).expect("read_at failure"), leaves[2]);
448        assert_eq!(t.read_at(3).expect("read_at failure"), leaves[3]);
449
450        let i1 = a.node(leaves[0], leaves[1], 0);
451        a.reset();
452        let i2 = a.node(leaves[2], leaves[3], 0);
453        a.reset();
454
455        assert_eq!(t.read_at(4).expect("read_at failure"), i1);
456        assert_eq!(t.read_at(5).expect("read_at failure"), i2);
457
458        let root = a.node(i1, i2, 1);
459        a.reset();
460
461        assert_eq!(
462            t.read_at(4).expect("read_at failure").0,
463            u64s_to_u8s([
464                0xb339ff6079800b5e,
465                0xec5907b3dc3094af,
466                0x93c003cc74a24f26,
467                0x042f94ffbe786bc3,
468            ]),
469        );
470
471        let expected = u64s_to_u8s([
472            0xefbb8be3e291e671,
473            0x77cc72b8cb2b5ad2,
474            0x30eb6385ae6b74ae,
475            0x1effebb7b26ad9eb,
476        ]);
477        let actual = t.read_at(6).expect("read_at failure").0;
478
479        assert_eq!(actual, expected);
480        assert_eq!(t.read_at(6).expect("read_at failure"), root);
481    }
482
483    #[test]
484    fn test_as_ref() {
485        let cases: Vec<[u64; 4]> = vec![
486            [0, 0, 0, 0],
487            [
488                14963070332212552755,
489                2414807501862983188,
490                16116531553419129213,
491                6357427774790868134,
492            ],
493        ];
494
495        for case in cases.into_iter() {
496            let val = PoseidonDomain(u64s_to_u8s(case));
497
498            for _ in 0..100 {
499                assert_eq!(val.into_bytes(), val.into_bytes());
500            }
501
502            let raw: &[u8] = val.as_ref();
503
504            for (limb, bytes) in case.iter().zip(raw.chunks(8)) {
505                assert_eq!(&limb.to_le_bytes(), bytes);
506            }
507        }
508    }
509
510    #[test]
511    fn test_serialize() {
512        let val = PoseidonDomain(u64s_to_u8s([1, 2, 3, 4]));
513
514        let ser = serde_json::to_string(&val)
515            .expect("Failed to serialize `PoseidonDomain` element to JSON string");
516        let val_back = serde_json::from_str(&ser)
517            .expect("Failed to deserialize JSON string to `PoseidonnDomain`");
518
519        assert_eq!(val, val_back);
520    }
521
522    #[test]
523    fn test_hash_md() {
524        // let arity = PoseidonMDArity::to_usize();
525        let n = 71;
526        let data = vec![PoseidonDomain(Fr::ONE.to_repr()); n];
527        let hashed = PoseidonFunction::hash_md(&data);
528
529        assert_eq!(
530            hashed,
531            PoseidonDomain(u64s_to_u8s([
532                0x351c54133b332c90,
533                0xc26f6d625f4e8195,
534                0x5fd9623643ed9622,
535                0x59f42220e09ff6f7,
536            ]))
537        );
538    }
539    #[test]
540    fn test_hash_md_circuit() {
541        // let arity = PoseidonMDArity::to_usize();
542        let n = 71;
543        let data = vec![PoseidonDomain(Fr::ONE.to_repr()); n];
544
545        let mut cs = TestConstraintSystem::<Fr>::new();
546        let circuit_data = (0..n)
547            .map(|n| {
548                AllocatedNum::alloc(cs.namespace(|| format!("input {}", n)), || Ok(Fr::ONE))
549                    .expect("alloc failure")
550            })
551            .collect::<Vec<_>>();
552
553        let hashed = PoseidonFunction::hash_md(&data);
554        let hashed_fr = Fr::from_repr_vartime(hashed.0).expect("from_repr failure");
555
556        let circuit_hashed = PoseidonFunction::hash_md_circuit(&mut cs, circuit_data.as_slice())
557            .expect("hash_md_circuit failure");
558
559        assert!(cs.is_satisfied());
560        let expected_constraints = 2_770;
561        let actual_constraints = cs.num_constraints();
562
563        assert_eq!(expected_constraints, actual_constraints);
564
565        assert_eq!(
566            hashed_fr,
567            circuit_hashed.get_value().expect("get_value failure")
568        );
569    }
570}