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] }
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 #[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 current[0] |= item;
170 } else {
171 self.fields
172 .push(BigInteger256::from_64x4(current).try_into().unwrap()); 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()); }
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)] pub 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 {CODA_SIGNATURE, "CodaSignature"},
310 {TESTNET_ZKAPP_BODY, "TestnetZkappBody"},
311 {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() }));
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 {CODA_SIGNATURE, "CodaSignature"},
563 {TESTNET_ZKAPP_BODY, "TestnetZkappBody"},
564 {MINA_SIGNATURE_MAINNET, "MinaSignatureMainnet"},
566 {MAINNET_ZKAPP_BODY, "MainnetZkappBody"}
567 );
568 }
569}