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 current[0] |= item;
163 } else {
164 self.fields.push(BigInteger256::new(current).into()); current = [item, 0, 0, 0];
166 nbits = item_nbits;
167 }
168 }
169
170 if nbits > 0 {
171 self.fields.push(BigInteger256::new(current).into()); }
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)] pub 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 {CODA_SIGNATURE, "CodaSignature"},
301 {TESTNET_ZKAPP_BODY, "TestnetZkappBody"},
302 {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)) }));
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 {CODA_SIGNATURE, "CodaSignature"},
552 {TESTNET_ZKAPP_BODY, "TestnetZkappBody"},
553 {MINA_SIGNATURE_MAINNET, "MinaSignatureMainnet"},
555 {MAINNET_ZKAPP_BODY, "MainnetZkappBody"}
556 );
557 }
558}