poseidon/
hash.rs

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