poseidon/
hash.rs

1use ark_ff::{BigInteger256, Field, FromBytes as _};
2use mina_curves::pasta::Fp;
3
4use crate::{PlonkSpongeConstantsKimchi, Sponge, SpongeParamsForField};
5
6enum Item {
7    Bool(bool),
8    U2(u8),
9    U8(u8),
10    U32(u32),
11    U48([u8; 6]),
12    U64(u64),
13}
14
15impl std::fmt::Debug for Item {
16    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17        match self {
18            Self::Bool(arg0) => f.write_fmt(format_args!("{}_bool", i32::from(*arg0))),
19            Self::U2(arg0) => f.write_fmt(format_args!("{}_u2", arg0)),
20            Self::U8(arg0) => f.write_fmt(format_args!("{}_u8", arg0)),
21            Self::U32(arg0) => f.write_fmt(format_args!("{}_u32", arg0)),
22            Self::U48(arg0) => f.write_fmt(format_args!("{:?}_u48", arg0)),
23            Self::U64(arg0) => f.write_fmt(format_args!("{}_u64", arg0)),
24        }
25    }
26}
27
28impl Item {
29    fn nbits(&self) -> u32 {
30        match self {
31            Item::Bool(_) => 1,
32            Item::U2(_) => 2,
33            Item::U8(_) => 8,
34            Item::U32(_) => 32,
35            Item::U48(_) => 48,
36            Item::U64(_) => 64,
37        }
38    }
39
40    fn as_bigint(&self) -> u64 {
41        match self {
42            Item::Bool(v) => *v as u64,
43            Item::U2(v) => *v as u64,
44            Item::U8(v) => *v as u64,
45            Item::U32(v) => *v as u64,
46            Item::U48(v) => {
47                let mut bytes = <[u8; 32]>::default();
48                bytes[..6].copy_from_slice(&v[..]);
49                BigInteger256::read(&bytes[..]).unwrap().to_64x4()[0] // Never fail with only 6 bytes
50            }
51            Item::U64(v) => *v,
52        }
53    }
54}
55
56pub struct Inputs {
57    fields: Vec<Fp>,
58    packeds: Vec<Item>,
59}
60
61impl Default for Inputs {
62    fn default() -> Self {
63        Self::new()
64    }
65}
66
67impl std::fmt::Debug for Inputs {
68    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69        f.debug_struct("Inputs")
70            .field(&format!("fields[{:?}]", self.fields.len()), &self.fields)
71            .field(&format!("packeds[{:?}]", self.packeds.len()), &self.packeds)
72            .finish()
73    }
74}
75
76#[allow(clippy::needless_range_loop)]
77fn shl(bigint: &mut [u64; 4], mut n: u32) {
78    if n >= 64 * 4 {
79        *bigint = [0, 0, 0, 0];
80        return;
81    }
82    while n >= 64 {
83        let mut t = 0;
84        for i in 0..4 {
85            core::mem::swap(&mut t, &mut bigint[i]);
86        }
87        n -= 64;
88    }
89    if n > 0 {
90        let mut t = 0;
91        for i in 0..4 {
92            let a = &mut bigint[i];
93            let t2 = *a >> (64 - n);
94            *a <<= n;
95            *a |= t;
96            t = t2;
97        }
98    }
99}
100
101impl Inputs {
102    pub fn new() -> Self {
103        Self {
104            fields: Vec::with_capacity(256),
105            packeds: Vec::with_capacity(256),
106        }
107    }
108
109    pub fn append_bool(&mut self, value: bool) {
110        self.packeds.push(Item::Bool(value));
111    }
112
113    pub fn append_u2(&mut self, value: u8) {
114        self.packeds.push(Item::U2(value));
115    }
116
117    pub fn append_u8(&mut self, value: u8) {
118        self.packeds.push(Item::U8(value));
119    }
120
121    pub fn append_u32(&mut self, value: u32) {
122        self.packeds.push(Item::U32(value));
123    }
124
125    pub fn append_u64(&mut self, value: u64) {
126        self.packeds.push(Item::U64(value));
127    }
128
129    pub fn append_u48(&mut self, value: [u8; 6]) {
130        self.packeds.push(Item::U48(value));
131    }
132
133    pub fn append_field(&mut self, value: Fp) {
134        self.fields.push(value);
135    }
136
137    pub fn append_bytes(&mut self, value: &[u8]) {
138        const BITS: [u8; 8] = [1, 2, 4, 8, 16, 32, 64, 128];
139
140        self.packeds.reserve(value.len() * 8);
141
142        for byte in value {
143            for bit in BITS {
144                self.append_bool(byte & bit != 0);
145            }
146        }
147    }
148
149    // pub fn append<T>(&mut self, value: &T)
150    // where
151    //     T: ToInputs,
152    // {
153    //     value.to_inputs(self);
154    // }
155
156    #[allow(clippy::wrong_self_convention)]
157    pub fn to_fields(mut self) -> Vec<Fp> {
158        let mut nbits = 0;
159        let mut current: [u64; 4] = [0, 0, 0, 0];
160
161        for (item, item_nbits) in self.packeds.iter().map(|i| (i.as_bigint(), i.nbits())) {
162            nbits += item_nbits;
163
164            if nbits < 255 {
165                shl(&mut current, item_nbits);
166
167                // Addition, but we use 'bitwise or' because we know bits of
168                // `current` are zero (we just shift-left them)
169                current[0] |= item;
170            } else {
171                self.fields
172                    .push(BigInteger256::from_64x4(current).try_into().unwrap()); // Never fail
173                current = [item, 0, 0, 0];
174                nbits = item_nbits;
175            }
176        }
177
178        if nbits > 0 {
179            self.fields
180                .push(BigInteger256::from_64x4(current).try_into().unwrap()); // Never fail
181        }
182
183        self.fields
184    }
185}
186
187fn param_to_field_impl(param: &str, default: &[u8; 32]) -> Fp {
188    let param_bytes = param.as_bytes();
189    let len = param_bytes.len();
190
191    let mut fp = *default;
192    fp[..len].copy_from_slice(param_bytes);
193
194    Fp::read(&fp[..]).expect("fp read failed")
195}
196
197pub fn param_to_field(param: &str) -> Fp {
198    const DEFAULT: &[u8; 32] = b"********************\0\0\0\0\0\0\0\0\0\0\0\0";
199
200    if param.len() > 20 {
201        panic!("must be 20 byte maximum");
202    }
203
204    param_to_field_impl(param, DEFAULT)
205}
206
207fn param_to_field_noinputs(param: &str) -> Fp {
208    const DEFAULT: &[u8; 32] = &[0; 32];
209
210    if param.len() > 32 {
211        panic!("must be 32 byte maximum");
212    }
213
214    param_to_field_impl(param, DEFAULT)
215}
216
217pub fn hash_with_kimchi(param: &LazyParam, fields: &[Fp]) -> Fp {
218    let LazyParam {
219        sponge_state,
220        state,
221        ..
222    } = param;
223
224    let mut sponge = Sponge {
225        sponge_state: sponge_state.clone(),
226        state: *state,
227        ..Sponge::<Fp, PlonkSpongeConstantsKimchi>::default()
228    };
229
230    sponge.absorb(fields);
231    sponge.squeeze()
232}
233
234pub fn hash_fields<F: Field + SpongeParamsForField<F>>(fields: &[F]) -> F {
235    let mut sponge = Sponge::<F>::default();
236
237    sponge.absorb(fields);
238    sponge.squeeze()
239}
240
241pub fn hash_noinputs(param: &LazyParam) -> Fp {
242    let LazyParam { last_squeezed, .. } = param;
243
244    *last_squeezed
245}
246
247#[derive(Debug)]
248#[allow(dead_code)] // `string` is never read
249pub struct LazyParam {
250    sponge_state: crate::SpongeState,
251    state: [Fp; 3],
252    last_squeezed: Fp,
253    string: &'static str,
254}
255
256impl LazyParam {
257    pub fn state(&self) -> [Fp; 3] {
258        self.state
259    }
260}
261
262pub mod params {
263    use once_cell::sync::Lazy;
264
265    use super::*;
266
267    macro_rules! impl_params {
268        ($({$name:tt, $string:tt}),*) => ($(
269            pub static $name: Lazy<Box<LazyParam>> = Lazy::new(|| {
270                let mut sponge = Sponge::<Fp>::default();
271                sponge.absorb(&[param_to_field($string)]);
272                let last_squeezed = sponge.squeeze();
273                Box::new(LazyParam {
274                    sponge_state: sponge.sponge_state,
275                    state: sponge.state,
276                    last_squeezed,
277                    string: $string,
278                })
279            });
280        )*)
281    }
282
283    impl_params!(
284        {MINA_ACCOUNT, "MinaAccount"},
285        {MINA_PROTO_STATE, "MinaProtoState"},
286        {MINA_PROTO_STATE_BODY, "MinaProtoStateBody"},
287        {MINA_DERIVE_TOKEN_ID, "MinaDeriveTokenId"},
288        {MINA_EPOCH_SEED, "MinaEpochSeed"},
289        {MINA_SIDELOADED_VK, "MinaSideLoadedVk"},
290        {MINA_VRF_MESSAGE, "MinaVrfMessage"},
291        {MINA_VRF_OUTPUT, "MinaVrfOutput"},
292
293        {CODA_RECEIPT_UC, "CodaReceiptUC"},
294        {COINBASE_STACK, "CoinbaseStack"},
295
296        {MINA_ACCOUNT_UPDATE_CONS, "MinaAcctUpdateCons"},
297        {MINA_ACCOUNT_UPDATE_NODE, "MinaAcctUpdateNode"},
298        {MINA_ACCOUNT_UPDATE_STACK_FRAME, "MinaAcctUpdStckFrm"},
299        {MINA_ACCOUNT_UPDATE_STACK_FRAME_CONS, "MinaActUpStckFrmCons"},
300
301        {MINA_ZKAPP_ACCOUNT, "MinaZkappAccount"},
302        {MINA_ZKAPP_MEMO, "MinaZkappMemo"},
303        {MINA_ZKAPP_URI, "MinaZkappUri"},
304        {MINA_ZKAPP_EVENT, "MinaZkappEvent"},
305        {MINA_ZKAPP_EVENTS, "MinaZkappEvents"},
306        {MINA_ZKAPP_SEQ_EVENTS, "MinaZkappSeqEvents"},
307
308        // devnet
309        {CODA_SIGNATURE, "CodaSignature"},
310        {TESTNET_ZKAPP_BODY, "TestnetZkappBody"},
311        // mainnet
312        {MINA_SIGNATURE_MAINNET, "MinaSignatureMainnet"},
313        {MAINNET_ZKAPP_BODY, "MainnetZkappBody"},
314
315        {MINA_MERKLE_TREE_0, "MinaMklTree000"},
316        {MINA_MERKLE_TREE_1, "MinaMklTree001"},
317        {MINA_MERKLE_TREE_2, "MinaMklTree002"},
318        {MINA_MERKLE_TREE_3, "MinaMklTree003"},
319        {MINA_MERKLE_TREE_4, "MinaMklTree004"},
320        {MINA_MERKLE_TREE_5, "MinaMklTree005"},
321        {MINA_MERKLE_TREE_6, "MinaMklTree006"},
322        {MINA_MERKLE_TREE_7, "MinaMklTree007"},
323        {MINA_MERKLE_TREE_8, "MinaMklTree008"},
324        {MINA_MERKLE_TREE_9, "MinaMklTree009"},
325        {MINA_MERKLE_TREE_10, "MinaMklTree010"},
326        {MINA_MERKLE_TREE_11, "MinaMklTree011"},
327        {MINA_MERKLE_TREE_12, "MinaMklTree012"},
328        {MINA_MERKLE_TREE_13, "MinaMklTree013"},
329        {MINA_MERKLE_TREE_14, "MinaMklTree014"},
330        {MINA_MERKLE_TREE_15, "MinaMklTree015"},
331        {MINA_MERKLE_TREE_16, "MinaMklTree016"},
332        {MINA_MERKLE_TREE_17, "MinaMklTree017"},
333        {MINA_MERKLE_TREE_18, "MinaMklTree018"},
334        {MINA_MERKLE_TREE_19, "MinaMklTree019"},
335        {MINA_MERKLE_TREE_20, "MinaMklTree020"},
336        {MINA_MERKLE_TREE_21, "MinaMklTree021"},
337        {MINA_MERKLE_TREE_22, "MinaMklTree022"},
338        {MINA_MERKLE_TREE_23, "MinaMklTree023"},
339        {MINA_MERKLE_TREE_24, "MinaMklTree024"},
340        {MINA_MERKLE_TREE_25, "MinaMklTree025"},
341        {MINA_MERKLE_TREE_26, "MinaMklTree026"},
342        {MINA_MERKLE_TREE_27, "MinaMklTree027"},
343        {MINA_MERKLE_TREE_28, "MinaMklTree028"},
344        {MINA_MERKLE_TREE_29, "MinaMklTree029"},
345        {MINA_MERKLE_TREE_30, "MinaMklTree030"},
346        {MINA_MERKLE_TREE_31, "MinaMklTree031"},
347        {MINA_MERKLE_TREE_32, "MinaMklTree032"},
348        {MINA_MERKLE_TREE_33, "MinaMklTree033"},
349        {MINA_MERKLE_TREE_34, "MinaMklTree034"},
350        {MINA_MERKLE_TREE_35, "MinaMklTree035"},
351
352        {MINA_CB_MERKLE_TREE_0, "MinaCbMklTree000"},
353        {MINA_CB_MERKLE_TREE_1, "MinaCbMklTree001"},
354        {MINA_CB_MERKLE_TREE_2, "MinaCbMklTree002"},
355        {MINA_CB_MERKLE_TREE_3, "MinaCbMklTree003"},
356        {MINA_CB_MERKLE_TREE_4, "MinaCbMklTree004"},
357        {MINA_CB_MERKLE_TREE_5, "MinaCbMklTree005"}
358    );
359
360    pub fn get_coinbase_param_for_height(height: usize) -> &'static LazyParam {
361        static ARRAY: [&Lazy<Box<LazyParam>>; 6] = [
362            &MINA_CB_MERKLE_TREE_0,
363            &MINA_CB_MERKLE_TREE_1,
364            &MINA_CB_MERKLE_TREE_2,
365            &MINA_CB_MERKLE_TREE_3,
366            &MINA_CB_MERKLE_TREE_4,
367            &MINA_CB_MERKLE_TREE_5,
368        ];
369
370        ARRAY[height]
371    }
372
373    pub fn get_merkle_param_for_height(height: usize) -> &'static LazyParam {
374        static ARRAY: [&Lazy<Box<LazyParam>>; 36] = [
375            &MINA_MERKLE_TREE_0,
376            &MINA_MERKLE_TREE_1,
377            &MINA_MERKLE_TREE_2,
378            &MINA_MERKLE_TREE_3,
379            &MINA_MERKLE_TREE_4,
380            &MINA_MERKLE_TREE_5,
381            &MINA_MERKLE_TREE_6,
382            &MINA_MERKLE_TREE_7,
383            &MINA_MERKLE_TREE_8,
384            &MINA_MERKLE_TREE_9,
385            &MINA_MERKLE_TREE_10,
386            &MINA_MERKLE_TREE_11,
387            &MINA_MERKLE_TREE_12,
388            &MINA_MERKLE_TREE_13,
389            &MINA_MERKLE_TREE_14,
390            &MINA_MERKLE_TREE_15,
391            &MINA_MERKLE_TREE_16,
392            &MINA_MERKLE_TREE_17,
393            &MINA_MERKLE_TREE_18,
394            &MINA_MERKLE_TREE_19,
395            &MINA_MERKLE_TREE_20,
396            &MINA_MERKLE_TREE_21,
397            &MINA_MERKLE_TREE_22,
398            &MINA_MERKLE_TREE_23,
399            &MINA_MERKLE_TREE_24,
400            &MINA_MERKLE_TREE_25,
401            &MINA_MERKLE_TREE_26,
402            &MINA_MERKLE_TREE_27,
403            &MINA_MERKLE_TREE_28,
404            &MINA_MERKLE_TREE_29,
405            &MINA_MERKLE_TREE_30,
406            &MINA_MERKLE_TREE_31,
407            &MINA_MERKLE_TREE_32,
408            &MINA_MERKLE_TREE_33,
409            &MINA_MERKLE_TREE_34,
410            &MINA_MERKLE_TREE_35,
411        ];
412
413        ARRAY[height]
414    }
415
416    macro_rules! impl_params_noinput {
417        ($({$name:tt, $string:tt}),*) => ($(
418            pub static $name: Lazy<Box<LazyParam>> = Lazy::new(|| {
419                let mut sponge = Sponge::<Fp>::default();
420                sponge.absorb(&[param_to_field_noinputs($string)]);
421                let last_squeezed = sponge.squeeze();
422                Box::new(LazyParam {
423                    sponge_state: sponge.sponge_state,
424                    state: sponge.state,
425                    last_squeezed,
426                    string: $string,
427                })
428            });
429        )*)
430    }
431
432    impl_params_noinput!(
433        {NO_INPUT_ZKAPP_ACTION_STATE_EMPTY_ELT, "MinaZkappActionStateEmptyElt"},
434        {NO_INPUT_COINBASE_STACK, "CoinbaseStack"},
435        {NO_INPUT_MINA_ZKAPP_EVENTS_EMPTY, "MinaZkappEventsEmpty"},
436        {NO_INPUT_MINA_ZKAPP_ACTIONS_EMPTY, "MinaZkappActionsEmpty"}
437    );
438}
439
440pub mod legacy {
441    use ark_ff::fields::arithmetic::InvalidBigInt;
442
443    use super::*;
444
445    #[derive(Clone, Debug)]
446    pub struct Inputs<F: Field> {
447        fields: Vec<F>,
448        bits: Vec<bool>,
449    }
450
451    impl<F: Field> Default for Inputs<F> {
452        fn default() -> Self {
453            Self::new()
454        }
455    }
456
457    impl<F: Field> Inputs<F> {
458        pub fn new() -> Self {
459            Self {
460                fields: Vec::with_capacity(256),
461                bits: Vec::with_capacity(512),
462            }
463        }
464
465        pub fn append_bit(&mut self, bit: bool) {
466            self.bits.push(bit);
467        }
468
469        pub fn append_bool(&mut self, value: bool) {
470            self.append_bit(value);
471        }
472
473        pub fn append_bits(&mut self, bits: &[bool]) {
474            self.bits.extend(bits);
475        }
476
477        pub fn append_bytes(&mut self, bytes: &[u8]) {
478            const BITS: [u8; 8] = [1, 2, 4, 8, 16, 32, 64, 128];
479
480            self.bits.reserve(bytes.len() * 8);
481
482            for byte in bytes {
483                for bit in BITS {
484                    self.append_bit(byte & bit != 0);
485                }
486            }
487        }
488
489        pub fn append_u64(&mut self, value: u64) {
490            self.append_bytes(&value.to_le_bytes());
491        }
492
493        pub fn append_u32(&mut self, value: u32) {
494            self.append_bytes(&value.to_le_bytes());
495        }
496
497        pub fn append_field(&mut self, field: F) {
498            self.fields.push(field);
499        }
500    }
501
502    impl<F: Field + TryFrom<BigInteger256, Error = InvalidBigInt>> Inputs<F> {
503        pub fn to_fields(mut self) -> Vec<F> {
504            const NBITS: usize = 255 - 1;
505
506            self.fields.reserve(self.bits.len() / NBITS);
507            self.fields.extend(self.bits.chunks(NBITS).map(|bits| {
508                let mut field = [0u64; 4];
509                for (index, bit) in bits.iter().enumerate() {
510                    let limb_index = index / 64;
511                    let bit_index = index % 64;
512                    field[limb_index] |= (*bit as u64) << bit_index;
513                }
514                F::try_from(BigInteger256::from_64x4(field)).unwrap() // Never fail
515            }));
516            self.fields
517        }
518    }
519
520    pub fn hash_with_kimchi(param: &LazyParam, fields: &[Fp]) -> Fp {
521        let LazyParam {
522            sponge_state,
523            state,
524            ..
525        } = param;
526
527        let mut sponge = Sponge {
528            sponge_state: sponge_state.clone(),
529            state: *state,
530            ..Sponge::new_legacy()
531        };
532
533        sponge.absorb(fields);
534        sponge.squeeze()
535    }
536
537    pub mod params {
538        use once_cell::sync::Lazy;
539
540        use super::*;
541
542        macro_rules! impl_params {
543            ($({$name:tt, $string:tt}),*) => ($(
544                pub static $name: Lazy<Box<LazyParam>> = Lazy::new(|| {
545                    let mut sponge = Sponge::new_legacy();
546                    sponge.absorb(&[param_to_field($string)]);
547                    let last_squeezed = sponge.squeeze();
548                    Box::new(LazyParam {
549                        sponge_state: sponge.sponge_state,
550                        state: sponge.state,
551                        last_squeezed,
552                        string: $string,
553                    })
554                });
555            )*)
556        }
557
558        impl_params!(
559            {CODA_RECEIPT_UC, "CodaReceiptUC"},
560
561            // devnet
562            {CODA_SIGNATURE, "CodaSignature"},
563            {TESTNET_ZKAPP_BODY, "TestnetZkappBody"},
564            // mainnet
565            {MINA_SIGNATURE_MAINNET, "MinaSignatureMainnet"},
566            {MAINNET_ZKAPP_BODY, "MainnetZkappBody"}
567        );
568    }
569}