1use std::{io::Cursor, str::FromStr, sync::Arc};
2
3use ark_ff::{BigInteger256, One, UniformRand, Zero};
4use mina_curves::pasta::Fp;
5use mina_p2p_messages::{
6 binprot::{BinProtRead, BinProtWrite},
7 v2,
8};
9use mina_signer::CompressedPubKey;
10use once_cell::sync::{Lazy, OnceCell};
11use openmina_core::constants::PROTOCOL_VERSION;
12use rand::{prelude::ThreadRng, seq::SliceRandom, Rng};
13use serde::{Deserialize, Serialize};
14
15use crate::{
16 gen_compressed,
17 proofs::{
18 field::{Boolean, FieldWitness, ToBoolean},
19 numbers::{
20 currency::{CheckedBalance, CheckedCurrency},
21 nat::CheckedSlot,
22 },
23 to_field_elements::ToFieldElements,
24 transaction::{
25 make_group, transaction_snark::checked_min_balance_at_slot, Check, InnerCurve,
26 PlonkVerificationKeyEvals,
27 },
28 witness::Witness,
29 },
30 scan_state::currency::{Amount, Balance, Magnitude, Nonce, Slot, TxnVersion},
31 zkapps::snark::FlaggedOption,
32 AppendToInputs as _, MerklePath, MyCow, ToInputs,
33};
34use poseidon::hash::{
35 hash_noinputs, hash_with_kimchi,
36 params::{
37 get_merkle_param_for_height, MINA_ACCOUNT, MINA_DERIVE_TOKEN_ID, MINA_SIDELOADED_VK,
38 MINA_ZKAPP_ACCOUNT, MINA_ZKAPP_URI, NO_INPUT_ZKAPP_ACTION_STATE_EMPTY_ELT,
39 },
40 Inputs,
41};
42
43use super::common::*;
44
45pub const TXN_VERSION_CURRENT: TxnVersion =
47 TxnVersion::from_u32(PROTOCOL_VERSION.transaction.as_u64() as u32);
48
49#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
50#[serde(into = "v2::MinaBaseTokenIdStableV2")]
51#[serde(try_from = "v2::MinaBaseTokenIdStableV2")]
52pub struct TokenId(pub Fp);
53
54impl std::fmt::Debug for TokenId {
55 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56 use crate::FpExt;
57 f.write_fmt(format_args!("TokenId({})", self.0.to_decimal()))
58 }
59}
60
61impl Default for TokenId {
62 fn default() -> Self {
63 Self(Fp::one())
64 }
65}
66
67impl From<u64> for TokenId {
68 fn from(num: u64) -> Self {
69 TokenId(Fp::from(num))
70 }
71}
72
73impl TokenId {
74 pub fn is_default(&self) -> bool {
75 self == &Self::default()
76 }
77}
78
79#[derive(Clone, PartialEq, Eq, derive_more::From)]
81pub struct TokenSymbol(pub Vec<u8>);
82
83impl std::fmt::Debug for TokenSymbol {
84 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85 let s = String::from_utf8_lossy(self.as_bytes());
86 let s: &str = &s;
87 f.debug_tuple("TokenSymbol").field(&s).finish()
88 }
89}
90
91impl TokenSymbol {
92 pub fn gen() -> Self {
93 let mut rng = rand::thread_rng();
94
95 let sym: u32 = rng.gen();
96 let mut sym = sym.to_string();
97 sym.truncate(6);
98
99 Self(sym.into_bytes())
100 }
101
102 pub fn len(&self) -> usize {
103 self.0.len()
104 }
105
106 pub fn is_empty(&self) -> bool {
107 self.0.is_empty()
108 }
109
110 pub fn as_bytes(&self) -> &[u8] {
111 &self.0
112 }
113
114 pub fn to_bytes(&self, bytes: &mut [u8]) {
115 if self.0.is_empty() {
116 return;
117 }
118 let len = self.0.len();
119 let s: &[u8] = self.as_bytes();
120 bytes[..len].copy_from_slice(&s[..len.min(6)]);
121 }
122
123 pub fn to_field<F: FieldWitness>(&self) -> F {
124 use ark_ff::FromBytes;
125
126 let mut s = <[u8; 32]>::default();
127 self.to_bytes(&mut s);
128
129 let bigint = BigInteger256::read(&s[..]).unwrap();
130 F::try_from(bigint).unwrap() }
132}
133
134#[allow(clippy::derivable_impls)]
135impl Default for TokenSymbol {
136 fn default() -> Self {
137 Self(Vec::new())
140 }
141}
142
143impl From<&mina_p2p_messages::string::TokenSymbol> for TokenSymbol {
144 fn from(value: &mina_p2p_messages::string::TokenSymbol) -> Self {
145 Self(value.as_ref().to_vec())
146 }
147}
148
149impl From<&TokenSymbol> for mina_p2p_messages::string::TokenSymbol {
150 fn from(value: &TokenSymbol) -> Self {
151 value.0.as_slice().into()
152 }
153}
154
155impl ToInputs for TokenSymbol {
156 fn to_inputs(&self, inputs: &mut Inputs) {
157 let mut s = <[u8; 6]>::default();
161 self.to_bytes(&mut s);
162 inputs.append_u48(s);
163 }
164}
165
166#[derive(Clone, Debug, PartialEq, Eq)]
167pub struct SetVerificationKey<Controller> {
168 pub auth: Controller,
169 pub txn_version: TxnVersion,
170}
171
172#[derive(Clone, Debug, PartialEq, Eq)]
174pub struct Permissions<Controller> {
175 pub edit_state: Controller,
176 pub access: Controller,
177 pub send: Controller,
178 pub receive: Controller,
179 pub set_delegate: Controller,
180 pub set_permissions: Controller,
181 pub set_verification_key: SetVerificationKey<Controller>,
182 pub set_zkapp_uri: Controller,
183 pub edit_action_state: Controller,
184 pub set_token_symbol: Controller,
185 pub increment_nonce: Controller,
186 pub set_voting_for: Controller,
187 pub set_timing: Controller,
188}
189
190pub enum AuthOrVersion<'a, T> {
191 Auth(&'a T),
192 Version(TxnVersion),
193}
194
195impl Permissions<AuthRequired> {
196 pub fn iter_as_bits<F>(&self, mut fun: F)
197 where
198 F: FnMut(AuthOrVersion<'_, bool>),
199 {
200 let Self {
201 edit_state,
202 access,
203 send,
204 receive,
205 set_delegate,
206 set_permissions,
207 set_verification_key:
208 SetVerificationKey {
209 auth: set_verification_key_auth,
210 txn_version,
211 },
212 set_zkapp_uri,
213 edit_action_state,
214 set_token_symbol,
215 increment_nonce,
216 set_voting_for,
217 set_timing,
218 } = self;
219
220 for auth in [
221 AuthOrVersion::Auth(edit_state),
222 AuthOrVersion::Auth(access),
223 AuthOrVersion::Auth(send),
224 AuthOrVersion::Auth(receive),
225 AuthOrVersion::Auth(set_delegate),
226 AuthOrVersion::Auth(set_permissions),
227 AuthOrVersion::Auth(set_verification_key_auth),
228 AuthOrVersion::Version(*txn_version),
229 AuthOrVersion::Auth(set_zkapp_uri),
230 AuthOrVersion::Auth(edit_action_state),
231 AuthOrVersion::Auth(set_token_symbol),
232 AuthOrVersion::Auth(increment_nonce),
233 AuthOrVersion::Auth(set_voting_for),
234 AuthOrVersion::Auth(set_timing),
235 ] {
236 match auth {
237 AuthOrVersion::Auth(auth) => {
238 for bit in auth.encode().to_bits() {
239 fun(AuthOrVersion::Auth(&bit));
240 }
241 }
242 AuthOrVersion::Version(version) => {
243 fun(AuthOrVersion::Version(version));
244 }
245 }
246 }
247 }
248}
249
250impl ToInputs for Permissions<AuthRequired> {
251 fn to_inputs(&self, inputs: &mut Inputs) {
252 self.iter_as_bits(|bit| match bit {
253 AuthOrVersion::Auth(bit) => inputs.append_bool(*bit),
254 AuthOrVersion::Version(version) => inputs.append(&version),
255 });
256 }
257}
258
259impl<F: FieldWitness> Check<F> for Permissions<AuthRequired> {
260 fn check(&self, w: &mut Witness<F>) {
261 let Self {
262 edit_state: _,
263 access: _,
264 send: _,
265 receive: _,
266 set_delegate: _,
267 set_permissions: _,
268 set_verification_key:
269 SetVerificationKey {
270 auth: _,
271 txn_version,
272 },
273 set_zkapp_uri: _,
274 edit_action_state: _,
275 set_token_symbol: _,
276 increment_nonce: _,
277 set_voting_for: _,
278 set_timing: _,
279 } = self;
280
281 txn_version.check(w);
282 }
283}
284
285impl Default for Permissions<AuthRequired> {
286 fn default() -> Self {
287 Self::user_default()
288 }
289}
290
291impl Permissions<AuthRequired> {
292 pub fn user_default() -> Self {
293 use AuthRequired::*;
294 Self {
295 edit_state: Signature,
296 send: Signature,
297 receive: None,
298 set_delegate: Signature,
299 set_permissions: Signature,
300 set_verification_key: SetVerificationKey {
301 auth: Signature,
302 txn_version: TXN_VERSION_CURRENT,
303 },
304 set_zkapp_uri: Signature,
305 edit_action_state: Signature,
306 set_token_symbol: Signature,
307 increment_nonce: Signature,
308 set_voting_for: Signature,
309 set_timing: Signature,
310 access: None,
311 }
312 }
313
314 pub fn empty() -> Self {
315 use AuthRequired::*;
316 Self {
317 edit_state: None,
318 send: None,
319 receive: None,
320 access: None,
321 set_delegate: None,
322 set_permissions: None,
323 set_verification_key: SetVerificationKey {
324 auth: None,
325 txn_version: TXN_VERSION_CURRENT,
326 },
327 set_zkapp_uri: None,
328 edit_action_state: None,
329 set_token_symbol: None,
330 increment_nonce: None,
331 set_voting_for: None,
332 set_timing: None,
333 }
334 }
335
336 pub fn gen(auth_tag: ControlTag) -> Self {
338 let mut rng = rand::thread_rng();
339
340 let auth_required_gen = match auth_tag {
341 ControlTag::Proof => AuthRequired::gen_for_proof_authorization,
342 ControlTag::Signature => AuthRequired::gen_for_signature_authorization,
343 ControlTag::NoneGiven => AuthRequired::gen_for_none_given_authorization,
344 };
345
346 Self {
347 edit_state: auth_required_gen(&mut rng),
348 send: auth_required_gen(&mut rng),
349 receive: auth_required_gen(&mut rng),
350 set_delegate: auth_required_gen(&mut rng),
351 set_permissions: auth_required_gen(&mut rng),
352 set_verification_key: SetVerificationKey {
353 auth: auth_required_gen(&mut rng),
354 txn_version: TXN_VERSION_CURRENT,
355 },
356 set_zkapp_uri: auth_required_gen(&mut rng),
357 edit_action_state: auth_required_gen(&mut rng),
358 set_token_symbol: auth_required_gen(&mut rng),
359 increment_nonce: auth_required_gen(&mut rng),
360 set_voting_for: auth_required_gen(&mut rng),
361 set_timing: auth_required_gen(&mut rng),
362 access: {
363 AuthRequired::gen_for_none_given_authorization(&mut rng)
366 },
367 }
368 }
369}
370
371#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
372pub enum ProofVerified {
373 N0,
374 N1,
375 N2,
376}
377
378impl ProofVerified {
379 pub fn to_int(&self) -> usize {
381 match self {
382 ProofVerified::N0 => 0,
383 ProofVerified::N1 => 1,
384 ProofVerified::N2 => 2,
385 }
386 }
387}
388
389impl ToInputs for ProofVerified {
390 fn to_inputs(&self, inputs: &mut Inputs) {
392 let bits = match self {
393 ProofVerified::N0 => [true, false, false],
394 ProofVerified::N1 => [false, true, false],
395 ProofVerified::N2 => [false, false, true],
396 };
397
398 for bit in bits {
399 inputs.append_bool(bit);
400 }
401 }
402}
403
404impl ToFieldElements<Fp> for ProofVerified {
406 fn to_field_elements(&self, fields: &mut Vec<Fp>) {
407 use Boolean::{False, True};
408
409 let bits = match self {
410 ProofVerified::N0 => [True, False, False],
411 ProofVerified::N1 => [False, True, False],
412 ProofVerified::N2 => [False, False, True],
413 };
414
415 bits.to_field_elements(fields);
416 }
417}
418
419#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
420pub struct VerificationKey {
421 pub max_proofs_verified: ProofVerified,
422 pub actual_wrap_domain_size: ProofVerified,
423 pub wrap_index: Box<PlonkVerificationKeyEvals<Fp>>,
424 pub wrap_vk: Option<()>,
426}
427
428impl Check<Fp> for VerificationKey {
435 fn check(&self, w: &mut Witness<Fp>) {
436 let Self {
437 max_proofs_verified: _,
438 actual_wrap_domain_size: _,
439 wrap_index,
440 wrap_vk: _,
441 } = self;
442
443 wrap_index.check(w);
444 }
445}
446
447impl ToFieldElements<Fp> for VerificationKey {
448 fn to_field_elements(&self, fields: &mut Vec<Fp>) {
449 let Self {
450 max_proofs_verified,
451 actual_wrap_domain_size,
452 wrap_index,
453 wrap_vk: _,
454 } = self;
455
456 let PlonkVerificationKeyEvals {
457 sigma,
458 coefficients,
459 generic,
460 psm,
461 complete_add,
462 mul,
463 emul,
464 endomul_scalar,
465 } = wrap_index.as_ref();
466
467 max_proofs_verified.to_field_elements(fields);
468 actual_wrap_domain_size.to_field_elements(fields);
469
470 sigma.to_field_elements(fields);
471 coefficients.to_field_elements(fields);
472 generic.to_field_elements(fields);
473 psm.to_field_elements(fields);
474 complete_add.to_field_elements(fields);
475 mul.to_field_elements(fields);
476 emul.to_field_elements(fields);
477 endomul_scalar.to_field_elements(fields);
478 }
479}
480
481impl ToInputs for VerificationKey {
482 fn to_inputs(&self, inputs: &mut Inputs) {
483 let Self {
484 max_proofs_verified,
485 actual_wrap_domain_size,
486 wrap_index,
487 wrap_vk: _,
488 } = self;
489
490 let PlonkVerificationKeyEvals {
491 sigma,
492 coefficients,
493 generic,
494 psm,
495 complete_add,
496 mul,
497 emul,
498 endomul_scalar,
499 } = wrap_index.as_ref();
500
501 inputs.append(max_proofs_verified);
502 inputs.append(actual_wrap_domain_size);
503
504 for sigma in sigma {
505 inputs.append(sigma);
506 }
507 for coefficients in coefficients {
508 inputs.append(coefficients);
509 }
510 inputs.append(generic);
511 inputs.append(psm);
512 inputs.append(complete_add);
513 inputs.append(mul);
514 inputs.append(emul);
515 inputs.append(endomul_scalar);
516 }
517}
518
519impl VerificationKey {
520 pub fn dummy() -> Arc<Self> {
522 static VK: OnceCell<Arc<VerificationKey>> = OnceCell::new();
523
524 VK.get_or_init(|| {
525 let g = InnerCurve::of_affine(make_group(
526 Fp::one(),
527 Fp::from_str(
528 "12418654782883325593414442427049395787963493412651469444558597405572177144507",
529 )
530 .unwrap(),
531 ));
532 Arc::new(Self {
533 max_proofs_verified: ProofVerified::N2,
534 actual_wrap_domain_size: ProofVerified::N2,
535 wrap_index: PlonkVerificationKeyEvals {
536 sigma: std::array::from_fn(|_| g.clone()),
537 coefficients: std::array::from_fn(|_| g.clone()),
538 generic: g.clone(),
539 psm: g.clone(),
540 complete_add: g.clone(),
541 mul: g.clone(),
542 emul: g.clone(),
543 endomul_scalar: g,
544 }
545 .into(),
546 wrap_vk: None,
547 })
548 })
549 .clone()
550 }
551
552 pub fn digest(&self) -> Fp {
553 self.hash()
554 }
555
556 pub fn hash(&self) -> Fp {
557 self.hash_with_param(&MINA_SIDELOADED_VK)
558 }
559
560 pub fn gen() -> Self {
561 let mut rng = rand::thread_rng();
562
563 VerificationKey {
564 max_proofs_verified: {
565 let n: u64 = rng.gen();
566
567 if n % 3 == 0 {
568 ProofVerified::N2
569 } else if n % 2 == 0 {
570 ProofVerified::N1
571 } else {
572 ProofVerified::N0
573 }
574 },
575 wrap_index: PlonkVerificationKeyEvals::rand().into(),
576 wrap_vk: None,
577 actual_wrap_domain_size: {
578 let n: u64 = rng.gen();
579
580 if n % 3 == 0 {
581 ProofVerified::N2
582 } else if n % 2 == 0 {
583 ProofVerified::N1
584 } else {
585 ProofVerified::N0
586 }
587 },
588 }
589 }
590}
591
592#[derive(Clone, PartialEq, Eq, derive_more::From)]
593pub struct ZkAppUri(Vec<u8>);
594
595impl std::fmt::Debug for ZkAppUri {
596 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
597 let s = String::from_utf8_lossy(&self.0);
598 let s: &str = &s;
599 f.debug_tuple("ZkAppUri").field(&s).finish()
600 }
601}
602
603fn default_zkapp_uri_hash() -> Fp {
604 static HASH: Lazy<Fp> = Lazy::new(|| {
605 let mut inputs = Inputs::new();
606 inputs.append(&Fp::zero());
607 inputs.append(&Fp::zero());
608 hash_with_kimchi(&MINA_ZKAPP_URI, &inputs.to_fields())
609 });
610 *HASH
611}
612
613impl ZkAppUri {
614 #[allow(clippy::new_without_default)]
615 pub fn new() -> Self {
616 Self(Vec::new())
617 }
618
619 pub fn gen() -> Self {
620 let mut rng = rand::thread_rng();
621
622 let zkapp_uri: u64 = rng.gen();
623 let zkapp_uri = zkapp_uri.to_string();
624
625 Self(zkapp_uri.into_bytes())
626 }
627
628 fn opt_to_field(opt: Option<&ZkAppUri>) -> Fp {
629 let Some(zkapp_uri) = opt else {
630 return default_zkapp_uri_hash();
631 };
632 let mut inputs = Inputs::new();
633 for c in zkapp_uri.0.as_slice() {
634 for j in 0..8 {
635 inputs.append_bool((c & (1 << j)) != 0);
636 }
637 }
638 inputs.append_bool(true);
639 hash_with_kimchi(&MINA_ZKAPP_URI, &inputs.to_fields())
640 }
641}
642
643impl ToFieldElements<Fp> for Option<&ZkAppUri> {
644 fn to_field_elements(&self, fields: &mut Vec<Fp>) {
645 let field_zkapp_uri = ZkAppUri::opt_to_field(*self);
646 field_zkapp_uri.to_field_elements(fields);
647 }
648}
649
650impl ToInputs for Option<&ZkAppUri> {
651 fn to_inputs(&self, inputs: &mut Inputs) {
653 let field_zkapp_uri = ZkAppUri::opt_to_field(*self);
654 inputs.append(&field_zkapp_uri);
655 }
656}
657
658impl std::ops::Deref for ZkAppUri {
659 type Target = [u8];
660
661 fn deref(&self) -> &Self::Target {
662 &self.0
663 }
664}
665
666impl From<&mina_p2p_messages::string::ZkAppUri> for ZkAppUri {
667 fn from(value: &mina_p2p_messages::string::ZkAppUri) -> Self {
668 Self(value.as_ref().to_vec())
669 }
670}
671
672impl From<&ZkAppUri> for mina_p2p_messages::string::ZkAppUri {
673 fn from(value: &ZkAppUri) -> Self {
674 Self::from(value.0.clone())
675 }
676}
677
678#[derive(Clone, Debug)]
681pub struct MutableFp {
682 fp: Arc<std::sync::Mutex<Option<Fp>>>,
683}
684
685#[cfg(feature = "fuzzing")]
688pub static GLOBAL_SKIP_PARTIAL_EQ: Lazy<std::sync::RwLock<bool>> =
689 Lazy::new(|| std::sync::RwLock::new(false));
690
691impl Eq for MutableFp {}
692
693impl PartialEq for MutableFp {
694 fn eq(&self, other: &Self) -> bool {
695 #[cfg(feature = "fuzzing")]
696 if *GLOBAL_SKIP_PARTIAL_EQ.read().unwrap() {
697 return true;
698 }
699
700 self.get().unwrap() == other.get().unwrap()
701 }
702}
703
704impl MutableFp {
705 pub fn empty() -> Self {
706 Self {
707 fp: Arc::new(std::sync::Mutex::new(None)),
708 }
709 }
710 pub fn new(fp: Fp) -> Self {
711 Self {
712 fp: Arc::new(std::sync::Mutex::new(Some(fp))),
713 }
714 }
715 pub fn get(&self) -> Option<Fp> {
716 *self.fp.lock().unwrap()
717 }
718 pub fn set(&self, fp: Fp) {
719 *self.fp.lock().unwrap() = Some(fp)
720 }
721}
722
723#[derive(Clone, Debug)]
724pub struct VerificationKeyWire {
725 vk: VerificationKey,
726 hash: MutableFp,
727}
728
729impl Eq for VerificationKeyWire {}
730
731impl PartialEq for VerificationKeyWire {
732 fn eq(&self, other: &Self) -> bool {
733 match (self.hash.get(), other.hash.get()) {
734 (Some(this), Some(other)) => this == other,
735 _ => self.vk == other.vk,
736 }
737 }
738}
739
740impl VerificationKeyWire {
741 pub fn new(vk: VerificationKey) -> Self {
742 Self {
743 vk,
744 hash: MutableFp::empty(),
745 }
746 }
747
748 pub fn with_hash(vk: VerificationKey, hash: Fp) -> Self {
749 Self {
750 vk,
751 hash: MutableFp::new(hash),
752 }
753 }
754
755 pub fn hash(&self) -> Fp {
756 let Self { vk, hash } = self;
757 if let Some(hash) = hash.get() {
758 return hash;
759 }
760 let vk_hash = vk.hash();
761 hash.set(vk_hash);
762 vk_hash
763 }
764
765 pub fn vk(&self) -> &VerificationKey {
766 let Self { vk, hash: _ } = self;
767 vk
768 }
769
770 pub fn dummy_hash() -> Fp {
771 static DUMMY: OnceCell<Arc<Fp>> = OnceCell::new();
772 **DUMMY.get_or_init(|| {
773 let vk = VerificationKey::dummy();
774 Arc::new(vk.hash())
775 })
776 }
777
778 pub fn dummy() -> Self {
779 Self {
780 vk: (*VerificationKey::dummy()).clone(),
781 hash: MutableFp::new(Self::dummy_hash()),
782 }
783 }
784
785 pub fn gen() -> Self {
786 Self::new(VerificationKey::gen())
787 }
788}
789
790#[derive(Clone, Debug, PartialEq, Eq)]
792pub struct ZkAppAccount {
793 pub app_state: [Fp; 8],
794 pub verification_key: Option<VerificationKeyWire>,
795 pub zkapp_version: u32,
797 pub action_state: [Fp; 5],
798 pub last_action_slot: Slot,
799 pub proved_state: bool,
800 pub zkapp_uri: ZkAppUri,
801}
802
803impl ToInputs for ZkAppAccount {
804 fn to_inputs(&self, inputs: &mut Inputs) {
805 let Self {
806 app_state,
807 verification_key,
808 zkapp_version,
809 action_state,
810 last_action_slot,
811 proved_state,
812 zkapp_uri,
813 } = self;
814
815 inputs.append(&Some(zkapp_uri));
817
818 inputs.append_bool(*proved_state);
819 inputs.append_u32(last_action_slot.as_u32());
820 for fp in action_state {
821 inputs.append_field(*fp);
822 }
823 inputs.append_u32(*zkapp_version);
824 let vk_hash = verification_key
825 .as_ref()
826 .map(VerificationKeyWire::hash)
827 .unwrap_or_else(VerificationKeyWire::dummy_hash);
828 inputs.append_field(vk_hash);
829 for fp in app_state {
830 inputs.append_field(*fp);
831 }
832 }
833}
834
835impl ToFieldElements<Fp> for ZkAppAccount {
836 fn to_field_elements(&self, fields: &mut Vec<Fp>) {
837 let Self {
838 app_state,
839 verification_key,
840 zkapp_version,
841 action_state,
842 last_action_slot,
843 proved_state,
844 zkapp_uri,
845 } = self;
846
847 app_state.to_field_elements(fields);
848 (
849 FlaggedOption::from(
850 verification_key
851 .as_ref()
852 .map(VerificationKeyWire::hash)
853 .as_ref(),
854 ),
855 VerificationKeyWire::dummy_hash,
856 )
857 .to_field_elements(fields);
858 Fp::from(*zkapp_version).to_field_elements(fields);
859 action_state.to_field_elements(fields);
860 last_action_slot.to_field_elements(fields);
861 proved_state.to_field_elements(fields);
862 Some(zkapp_uri).to_field_elements(fields);
863 }
864}
865
866impl Check<Fp> for ZkAppAccount {
867 fn check(&self, w: &mut Witness<Fp>) {
868 let Self {
869 app_state: _,
870 verification_key: _,
871 zkapp_version,
872 action_state: _,
873 last_action_slot,
874 proved_state: _,
875 zkapp_uri: _,
876 } = self;
877
878 zkapp_version.check(w);
879 last_action_slot.check(w);
880 }
881}
882
883impl Default for ZkAppAccount {
884 fn default() -> Self {
885 Self {
886 app_state: [Fp::zero(); 8],
887 verification_key: None,
888 zkapp_version: 0,
889 action_state: {
890 let empty = Self::empty_action_state();
891 [empty, empty, empty, empty, empty]
892 },
893 last_action_slot: Slot::zero(),
894 proved_state: false,
895 zkapp_uri: ZkAppUri::new(),
896 }
897 }
898}
899
900impl ZkAppAccount {
901 pub fn hash(&self) -> Fp {
902 self.hash_with_param(&MINA_ZKAPP_ACCOUNT)
903 }
904
905 pub fn empty_action_state() -> Fp {
907 hash_noinputs(&NO_INPUT_ZKAPP_ACTION_STATE_EMPTY_ELT)
908 }
909
910 pub fn is_default(&self) -> bool {
911 self == &Self::default()
912 }
913}
914
915#[derive(Clone, Eq)]
924pub struct AccountIdOrderable {
925 bigint_public_key_x: BigInteger256,
928 bigint_public_key_is_odd: bool,
929 bigint_token_id: BigInteger256,
930 public_key: CompressedPubKey,
932 token_id: TokenId,
933}
934
935impl PartialEq for AccountIdOrderable {
936 fn eq(&self, other: &Self) -> bool {
937 let Self {
938 bigint_public_key_x: self_x,
939 bigint_public_key_is_odd: self_is_odd,
940 bigint_token_id: self_token_id,
941 public_key: _,
942 token_id: _,
943 } = self;
944 let Self {
945 bigint_public_key_x: other_x,
946 bigint_public_key_is_odd: other_is_odd,
947 bigint_token_id: other_token_id,
948 public_key: _,
949 token_id: _,
950 } = other;
951
952 self_x == other_x && self_is_odd == other_is_odd && self_token_id == other_token_id
953 }
954}
955impl Ord for AccountIdOrderable {
956 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
959 let Self {
960 bigint_public_key_x: self_x,
961 bigint_public_key_is_odd: self_is_odd,
962 bigint_token_id: self_token_id,
963 public_key: _,
964 token_id: _,
965 } = self;
966
967 let Self {
968 bigint_public_key_x: other_x,
969 bigint_public_key_is_odd: other_is_odd,
970 bigint_token_id: other_token_id,
971 public_key: _,
972 token_id: _,
973 } = other;
974
975 match self_x.cmp(other_x) {
976 std::cmp::Ordering::Equal => {}
977 non_eq => return non_eq,
978 }
979 match self_is_odd.cmp(other_is_odd) {
980 std::cmp::Ordering::Equal => {}
981 non_eq => return non_eq,
982 }
983 self_token_id.cmp(other_token_id)
984 }
985}
986
987impl PartialOrd for AccountIdOrderable {
988 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
989 Some(self.cmp(other))
990 }
991}
992
993impl From<AccountId> for AccountIdOrderable {
994 fn from(value: AccountId) -> Self {
995 let AccountId {
996 public_key,
997 token_id,
998 } = value;
999 let CompressedPubKey { x, is_odd } = &public_key;
1000
1001 Self {
1002 bigint_public_key_x: (*x).into(),
1003 bigint_public_key_is_odd: *is_odd,
1004 bigint_token_id: token_id.0.into(),
1005 public_key,
1006 token_id,
1007 }
1008 }
1009}
1010
1011impl From<AccountIdOrderable> for AccountId {
1012 fn from(value: AccountIdOrderable) -> Self {
1013 let AccountIdOrderable {
1014 bigint_public_key_x: _,
1015 bigint_public_key_is_odd: _,
1016 bigint_token_id: _,
1017 public_key,
1018 token_id,
1019 } = value;
1020
1021 Self {
1022 public_key,
1023 token_id,
1024 }
1025 }
1026}
1027
1028#[derive(Clone, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
1029#[serde(into = "v2::MinaBaseAccountIdStableV2")]
1030#[serde(try_from = "v2::MinaBaseAccountIdStableV2")]
1031pub struct AccountId {
1032 pub public_key: CompressedPubKey,
1033 pub token_id: TokenId,
1034}
1035
1036impl ToInputs for AccountId {
1037 fn to_inputs(&self, inputs: &mut Inputs) {
1038 let Self {
1039 public_key,
1040 token_id,
1041 } = self;
1042 inputs.append(public_key);
1043 inputs.append(token_id);
1044 }
1045}
1046
1047impl AccountId {
1048 pub fn empty() -> Self {
1049 Self {
1050 public_key: CompressedPubKey::empty(),
1051 token_id: TokenId::default(),
1052 }
1053 }
1054
1055 pub fn is_empty(&self) -> bool {
1056 self == &Self::empty()
1057 }
1058
1059 pub fn derive_token_id(&self) -> TokenId {
1060 let is_odd_field = match self.public_key.is_odd {
1062 true => Fp::one(),
1063 false => Fp::zero(),
1064 };
1065
1066 TokenId(hash_with_kimchi(
1067 &MINA_DERIVE_TOKEN_ID,
1068 &[self.public_key.x, self.token_id.0, is_odd_field],
1069 ))
1070 }
1071
1072 pub fn new(public_key: CompressedPubKey, token_id: TokenId) -> Self {
1073 Self {
1074 public_key,
1075 token_id,
1076 }
1077 }
1078
1079 pub fn new_with_default_token(public_key: CompressedPubKey) -> Self {
1080 Self::new(public_key, TokenId::default())
1081 }
1082
1083 pub fn create(public_key: CompressedPubKey, token_id: TokenId) -> Self {
1084 Self::new(public_key, token_id)
1085 }
1086
1087 pub fn ocaml_hash(&self) -> u32 {
1088 crate::port_ocaml::account_id_ocaml_hash(self)
1089 }
1090
1091 pub fn rand() -> Self {
1092 let mut rng = rand::thread_rng();
1093
1094 Self {
1095 public_key: gen_compressed(),
1096 token_id: TokenId(Fp::rand(&mut rng)),
1097 }
1098 }
1099
1100 pub fn deserialize(bytes: &[u8]) -> Self {
1101 let mut cursor = Cursor::new(bytes);
1102 AccountId::binprot_read(&mut cursor).unwrap()
1103 }
1104
1105 pub fn serialize(&self) -> Vec<u8> {
1106 let mut bytes = Vec::with_capacity(10000);
1107 self.binprot_write(&mut bytes).unwrap();
1108 bytes
1109 }
1110
1111 pub fn checked_equal(&self, other: &Self, w: &mut Witness<Fp>) -> Boolean {
1112 use crate::proofs::field::field;
1113
1114 let pk_equal = checked_equal_compressed_key(&self.public_key, &other.public_key, w);
1116
1117 let tid_equal = field::equal(self.token_id.0, other.token_id.0, w);
1119
1120 pk_equal.and(&tid_equal, w)
1122 }
1123}
1124
1125pub fn checked_equal_compressed_key(
1126 a: &CompressedPubKey,
1127 b: &CompressedPubKey,
1128 w: &mut Witness<Fp>,
1129) -> Boolean {
1130 use crate::proofs::field::field;
1131
1132 let x_eq = field::equal(a.x, b.x, w);
1133 let odd_eq = Boolean::equal(&a.is_odd.to_boolean(), &b.is_odd.to_boolean(), w);
1134 x_eq.and(&odd_eq, w)
1135}
1136
1137pub fn checked_equal_compressed_key_const_and(
1139 a: &CompressedPubKey,
1140 b: &CompressedPubKey,
1141 w: &mut Witness<Fp>,
1142) -> Boolean {
1143 use crate::proofs::field::field;
1144
1145 if b == &CompressedPubKey::empty() {
1146 let x_eq = field::equal(a.x, b.x, w);
1147 let odd_eq = Boolean::const_equal(&a.is_odd.to_boolean(), &b.is_odd.to_boolean());
1148 x_eq.and(&odd_eq, w)
1149 } else {
1150 let x_eq = field::equal(a.x, b.x, w);
1151 let odd_eq = Boolean::equal(&a.is_odd.to_boolean(), &b.is_odd.to_boolean(), w);
1152 x_eq.const_and(&odd_eq)
1153 }
1154}
1155
1156impl std::fmt::Debug for AccountId {
1157 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1158 f.debug_struct("AccountId")
1159 .field("public_key", &self.public_key)
1160 .field("token_id", &self.token_id)
1161 .finish()
1162 }
1163}
1164
1165impl std::hash::Hash for AccountId {
1166 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1167 self.public_key.x.hash(state);
1168 self.public_key.is_odd.hash(state);
1169 self.token_id.hash(state);
1170 }
1171}
1172
1173impl PartialEq for AccountId {
1174 fn eq(&self, other: &Self) -> bool {
1175 self.public_key.x == other.public_key.x
1176 && self.public_key.is_odd == other.public_key.is_odd
1177 && self.token_id.0 == other.token_id.0
1178 }
1179}
1180
1181#[derive(Debug)]
1182pub enum PermissionTo {
1183 Access,
1184 Send,
1185 Receive,
1186 SetDelegate,
1187 IncrementNonce,
1188}
1189
1190#[derive(Copy, Clone, Debug)]
1191pub enum ControlTag {
1192 Proof,
1193 Signature,
1194 NoneGiven,
1195}
1196
1197impl ControlTag {
1198 pub fn gen(rng: &mut ThreadRng) -> Self {
1199 match Self::NoneGiven {
1201 ControlTag::Proof => {}
1202 ControlTag::Signature => {}
1203 ControlTag::NoneGiven => {}
1204 };
1205
1206 [Self::Proof, Self::Signature, Self::NoneGiven]
1207 .choose(rng)
1208 .copied()
1209 .unwrap()
1210 }
1211}
1212
1213pub fn check_permission(auth: AuthRequired, tag: ControlTag) -> bool {
1214 use AuthRequired::*;
1215 use ControlTag as Tag;
1216
1217 match (auth, tag) {
1218 (Impossible, _) => false,
1219 (None, _) => true,
1220 (Proof, Tag::Proof) => true,
1221 (Signature, Tag::Signature) => true,
1222 (Either, Tag::Proof | Tag::Signature) => true,
1224 (Signature, Tag::Proof) => false,
1225 (Proof, Tag::Signature) => false,
1226 (Proof | Signature | Either, Tag::NoneGiven) => false,
1227 (Both, _) => unimplemented!("check_permission with `Both` Not implemented in OCaml"),
1228 }
1229}
1230
1231pub fn eval_no_proof<F: FieldWitness>(
1233 auth: AuthRequired,
1234 signature_verifies: Boolean,
1235 is_and_const: bool,
1236 is_or_const: bool,
1237 w: &mut Witness<F>,
1238) -> Boolean {
1239 let AuthRequiredEncoded {
1242 constant,
1243 signature_necessary: _,
1244 signature_sufficient,
1245 } = auth.encode();
1246
1247 let constant = constant.to_boolean();
1248 let signature_sufficient = signature_sufficient.to_boolean();
1249
1250 let a = if is_and_const {
1251 constant.neg().const_and(&signature_verifies)
1252 } else {
1253 constant.neg().and(&signature_verifies, w)
1254 };
1255 let b = if is_or_const {
1256 constant.const_or(&a)
1257 } else {
1258 constant.or(&a, w)
1259 };
1260 signature_sufficient.and(&b, w)
1261}
1262
1263pub struct PermsConst {
1264 pub and_const: bool,
1265 pub or_const: bool,
1266}
1267
1268#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1270#[serde(into = "v2::MinaBaseAccountBinableArgStableV2")]
1271#[serde(try_from = "v2::MinaBaseAccountBinableArgStableV2")]
1272pub struct Account {
1273 pub public_key: CompressedPubKey, pub token_id: TokenId, pub token_symbol: TokenSymbol, pub balance: Balance, pub nonce: Nonce, pub receipt_chain_hash: ReceiptChainHash, pub delegate: Option<CompressedPubKey>, pub voting_for: VotingFor, pub timing: Timing, pub permissions: Permissions<AuthRequired>, pub zkapp: Option<Box<ZkAppAccount>>, }
1287
1288impl Account {
1289 #[cfg(test)]
1290 pub fn create() -> Self {
1291 let pubkey = CompressedPubKey::from_address(
1292 "B62qnzbXmRNo9q32n4SNu2mpB8e7FYYLH8NmaX6oFCBYjjQ8SbD7uzV",
1293 )
1294 .unwrap();
1295
1296 Self {
1297 public_key: pubkey.clone(),
1298 token_id: TokenId::default(),
1299 token_symbol: TokenSymbol::default(),
1300 balance: Balance::from_u64(10101),
1301 nonce: Nonce::zero(),
1302 receipt_chain_hash: ReceiptChainHash::empty(),
1303 delegate: Some(pubkey),
1304 voting_for: VotingFor::dummy(),
1305 timing: Timing::Untimed,
1306 permissions: Permissions::user_default(),
1307 zkapp: None,
1308 }
1309 }
1310
1311 pub fn create_with(account_id: AccountId, balance: Balance) -> Self {
1312 let delegate = if account_id.token_id.is_default() {
1313 Some(account_id.public_key.clone())
1315 } else {
1316 None
1317 };
1318
1319 Self {
1320 public_key: account_id.public_key,
1321 token_id: account_id.token_id,
1322 token_symbol: TokenSymbol::default(),
1323 balance,
1324 nonce: Nonce::zero(),
1325 receipt_chain_hash: ReceiptChainHash::empty(),
1326 delegate,
1327 voting_for: VotingFor::dummy(),
1328 timing: Timing::Untimed,
1329 permissions: Permissions::user_default(),
1330 zkapp: None,
1331 }
1332 }
1333
1334 pub fn delegate_or_empty(&self) -> MyCow<'_, CompressedPubKey> {
1335 MyCow::borrow_or_else(&self.delegate, CompressedPubKey::empty)
1336 }
1337
1338 pub fn zkapp_or_empty(&self) -> MyCow<'_, Box<ZkAppAccount>> {
1339 MyCow::borrow_or_else(&self.zkapp, Box::<ZkAppAccount>::default)
1340 }
1341
1342 pub fn initialize(account_id: &AccountId) -> Self {
1343 Self::create_with(account_id.clone(), Balance::zero())
1344 }
1345
1346 pub fn deserialize(bytes: &[u8]) -> Self {
1347 let mut cursor = Cursor::new(bytes);
1348 Account::binprot_read(&mut cursor).unwrap()
1349 }
1350
1351 pub fn serialize(&self) -> Vec<u8> {
1352 let mut bytes = Vec::with_capacity(10000);
1353 self.binprot_write(&mut bytes).unwrap();
1354 bytes
1355 }
1356
1357 pub fn empty() -> Self {
1358 Self {
1359 public_key: CompressedPubKey {
1360 x: Fp::zero(),
1361 is_odd: false,
1362 },
1363 token_id: TokenId::default(),
1364 token_symbol: TokenSymbol::default(),
1365 balance: Balance::zero(),
1366 nonce: Nonce::zero(),
1367 receipt_chain_hash: ReceiptChainHash::empty(),
1368 delegate: None,
1369 voting_for: VotingFor::dummy(),
1370 timing: Timing::Untimed,
1371 permissions: Permissions::user_default(),
1372 zkapp: None,
1373 }
1374 }
1375
1376 pub fn id(&self) -> AccountId {
1377 AccountId {
1378 public_key: self.public_key.clone(),
1379 token_id: self.token_id.clone(),
1380 }
1381 }
1382
1383 pub fn has_locked_tokens(&self, global_slot: Slot) -> bool {
1384 match self.timing {
1385 Timing::Untimed => false,
1386 Timing::Timed { .. } => {
1387 let curr_min_balance = self.min_balance_at_slot(global_slot);
1388
1389 !curr_min_balance.is_zero()
1390 }
1391 }
1392 }
1393
1394 pub fn has_locked_tokens_checked(
1395 &self,
1396 global_slot: &CheckedSlot<Fp>,
1397 w: &mut Witness<Fp>,
1398 ) -> Boolean {
1399 let TimingAsRecordChecked {
1400 is_timed: _,
1401 initial_minimum_balance,
1402 cliff_time,
1403 cliff_amount,
1404 vesting_period,
1405 vesting_increment,
1406 } = self.timing.to_record_checked::<Fp>();
1407
1408 let cur_min_balance = checked_min_balance_at_slot(
1409 global_slot,
1410 &cliff_time,
1411 &cliff_amount,
1412 &vesting_period,
1413 &vesting_increment,
1414 &initial_minimum_balance,
1415 w,
1416 );
1417
1418 let zero_min_balance = CheckedBalance::zero().equal(&cur_min_balance, w);
1419 zero_min_balance.neg()
1420 }
1421
1422 pub fn liquid_balance_at_slot(&self, global_slot: Slot) -> Balance {
1423 match self.timing {
1424 Timing::Untimed => self.balance,
1425 Timing::Timed { .. } => self
1426 .balance
1427 .sub_amount(self.min_balance_at_slot(global_slot).to_amount())
1428 .unwrap(),
1429 }
1430 }
1431
1432 pub fn min_balance_at_slot(&self, global_slot: Slot) -> Balance {
1433 match self.timing {
1434 Timing::Untimed => Balance::zero(),
1435 Timing::Timed {
1436 initial_minimum_balance,
1437 cliff_time,
1438 cliff_amount,
1439 vesting_period,
1440 vesting_increment,
1441 } => {
1442 if global_slot < cliff_time {
1443 initial_minimum_balance
1444 } else if vesting_period.is_zero() {
1445 Balance::zero()
1447 } else {
1448 match initial_minimum_balance.sub_amount(cliff_amount) {
1449 None => Balance::zero(),
1450 Some(min_balance_past_cliff) => {
1451 let num_periods = (global_slot.as_u32() - cliff_time.as_u32())
1454 / vesting_period.as_u32();
1455 let num_periods: u64 = num_periods.into();
1456
1457 let vesting_decrement = {
1458 let vesting_increment = vesting_increment.as_u64();
1459
1460 if u64::MAX
1461 .checked_div(num_periods)
1462 .map(|res| {
1463 matches!(
1464 res.cmp(&vesting_increment),
1465 std::cmp::Ordering::Less
1466 )
1467 })
1468 .unwrap_or(false)
1469 {
1470 Amount::from_u64(u64::MAX)
1472 } else {
1473 Amount::from_u64(
1474 num_periods.checked_mul(vesting_increment).unwrap(),
1475 )
1476 }
1477 };
1478
1479 match min_balance_past_cliff.sub_amount(vesting_decrement) {
1480 None => Balance::zero(),
1481 Some(amount) => amount,
1482 }
1483 }
1484 }
1485 }
1486 }
1487 }
1488 }
1489
1490 pub fn has_permission_to(&self, control: ControlTag, to: PermissionTo) -> bool {
1492 match to {
1493 PermissionTo::Access => check_permission(self.permissions.access, control),
1494 PermissionTo::Send => check_permission(self.permissions.send, control),
1495 PermissionTo::Receive => check_permission(self.permissions.receive, control),
1496 PermissionTo::SetDelegate => check_permission(self.permissions.set_delegate, control),
1497 PermissionTo::IncrementNonce => {
1498 check_permission(self.permissions.increment_nonce, control)
1499 }
1500 }
1501 }
1502
1503 pub fn checked_has_permission_to<F: FieldWitness>(
1504 &self,
1505 consts: PermsConst,
1506 signature_verifies: Option<Boolean>,
1507 to: PermissionTo,
1508 w: &mut Witness<F>,
1509 ) -> Boolean {
1510 let signature_verifies = match signature_verifies {
1511 Some(signature_verifies) => signature_verifies,
1512 None => match to {
1513 PermissionTo::Send => Boolean::True,
1514 PermissionTo::Receive => Boolean::False,
1515 PermissionTo::SetDelegate => Boolean::True,
1516 PermissionTo::IncrementNonce => Boolean::True,
1517 PermissionTo::Access => {
1518 panic!("signature_verifies argument must be given for access permission")
1519 }
1520 },
1521 };
1522
1523 let auth = match to {
1524 PermissionTo::Send => self.permissions.send,
1525 PermissionTo::Receive => self.permissions.receive,
1526 PermissionTo::SetDelegate => self.permissions.set_delegate,
1527 PermissionTo::IncrementNonce => self.permissions.increment_nonce,
1528 PermissionTo::Access => self.permissions.access,
1529 };
1530
1531 eval_no_proof(
1532 auth,
1533 signature_verifies,
1534 consts.and_const,
1535 consts.or_const,
1536 w,
1537 )
1538 }
1539
1540 pub fn has_permission_to_send(&self) -> bool {
1542 self.has_permission_to(ControlTag::Signature, PermissionTo::Access)
1543 && self.has_permission_to(ControlTag::Signature, PermissionTo::Send)
1544 }
1545
1546 pub fn has_permission_to_receive(&self) -> bool {
1548 self.has_permission_to(ControlTag::NoneGiven, PermissionTo::Access)
1549 && self.has_permission_to(ControlTag::NoneGiven, PermissionTo::Receive)
1550 }
1551
1552 pub fn has_permission_to_set_delegate(&self) -> bool {
1554 self.has_permission_to(ControlTag::Signature, PermissionTo::Access)
1555 && self.has_permission_to(ControlTag::Signature, PermissionTo::SetDelegate)
1556 }
1557
1558 pub fn has_permission_to_increment_nonce(&self) -> bool {
1560 self.has_permission_to(ControlTag::Signature, PermissionTo::Access)
1561 && self.has_permission_to(ControlTag::Signature, PermissionTo::IncrementNonce)
1562 }
1563
1564 pub fn hash(&self) -> Fp {
1565 self.hash_with_param(&MINA_ACCOUNT)
1566 }
1567
1568 pub fn checked_hash(&self, w: &mut Witness<Fp>) -> Fp {
1569 use crate::proofs::transaction::transaction_snark::checked_hash;
1570
1571 let inputs = self.to_inputs_owned();
1572
1573 checked_hash(&MINA_ACCOUNT, &inputs.to_fields(), w)
1574 }
1575
1576 pub fn rand() -> Self {
1577 let mut rng = rand::thread_rng();
1578 let rng = &mut rng;
1579
1580 let symbol: u64 = rng.gen();
1581 let mut symbol = symbol.to_string();
1582 symbol.truncate(6);
1583
1584 let zkapp_uri: u64 = rng.gen();
1585 let mut zkapp_uri = zkapp_uri.to_string();
1586 zkapp_uri.truncate(6);
1587
1588 let gen_perm = |rng: &mut ThreadRng| {
1589 let n: u64 = rng.gen();
1590 if n % 5 == 0 {
1591 AuthRequired::Either
1592 } else if n % 4 == 0 {
1593 AuthRequired::Impossible
1594 } else if n % 3 == 0 {
1595 AuthRequired::None
1596 } else if n % 2 == 0 {
1597 AuthRequired::Proof
1598 } else {
1599 AuthRequired::Signature
1600 }
1601 };
1602
1603 Self {
1604 public_key: gen_compressed(),
1605 token_id: TokenId(Fp::rand(rng)),
1606 token_symbol: TokenSymbol(symbol.into_bytes()),
1607 balance: rng.gen(),
1608 nonce: rng.gen(),
1609 receipt_chain_hash: ReceiptChainHash(Fp::rand(rng)),
1610 delegate: if rng.gen() {
1611 Some(gen_compressed())
1612 } else {
1613 None
1614 },
1615 voting_for: VotingFor(Fp::rand(rng)),
1616 timing: if rng.gen() {
1617 Timing::Untimed
1618 } else {
1619 Timing::Timed {
1620 initial_minimum_balance: rng.gen(),
1621 cliff_time: rng.gen(),
1622 cliff_amount: rng.gen(),
1623 vesting_period: rng.gen(),
1624 vesting_increment: rng.gen(),
1625 }
1626 },
1627 permissions: Permissions {
1628 edit_state: gen_perm(rng),
1629 send: gen_perm(rng),
1630 receive: gen_perm(rng),
1631 set_delegate: gen_perm(rng),
1632 set_permissions: gen_perm(rng),
1633 set_verification_key: SetVerificationKey {
1634 auth: gen_perm(rng),
1635 txn_version: TXN_VERSION_CURRENT, },
1637 set_zkapp_uri: gen_perm(rng),
1638 edit_action_state: gen_perm(rng),
1639 set_token_symbol: gen_perm(rng),
1640 increment_nonce: gen_perm(rng),
1641 set_voting_for: gen_perm(rng),
1642 access: gen_perm(rng),
1643 set_timing: gen_perm(rng),
1644 },
1645 zkapp: if rng.gen() {
1646 Some(
1647 ZkAppAccount {
1648 app_state: [
1649 Fp::rand(rng),
1650 Fp::rand(rng),
1651 Fp::rand(rng),
1652 Fp::rand(rng),
1653 Fp::rand(rng),
1654 Fp::rand(rng),
1655 Fp::rand(rng),
1656 Fp::rand(rng),
1657 ],
1658 verification_key: if rng.gen() {
1659 Some(VerificationKeyWire::gen())
1660 } else {
1661 None
1662 },
1663 zkapp_version: rng.gen(),
1664 action_state: [
1665 Fp::rand(rng),
1666 Fp::rand(rng),
1667 Fp::rand(rng),
1668 Fp::rand(rng),
1669 Fp::rand(rng),
1670 ],
1671 last_action_slot: rng.gen(),
1672 proved_state: rng.gen(),
1673 zkapp_uri: ZkAppUri(zkapp_uri.into_bytes()),
1674 }
1675 .into(),
1676 )
1677 } else {
1678 None
1679 },
1680 }
1681 }
1682}
1683
1684pub fn default_zkapp_hash() -> Fp {
1685 static HASH: Lazy<Fp> = Lazy::new(|| {
1686 let default = ZkAppAccount::default();
1687 default.hash()
1688 });
1689 *HASH
1690}
1691
1692impl ToInputs for Account {
1693 fn to_inputs(&self, inputs: &mut Inputs) {
1694 let Self {
1695 public_key,
1696 token_id,
1697 token_symbol,
1698 balance,
1699 nonce,
1700 receipt_chain_hash,
1701 delegate,
1702 voting_for,
1703 timing,
1704 permissions,
1705 zkapp,
1706 } = self;
1707
1708 let field_zkapp = match zkapp.as_ref() {
1710 Some(zkapp) => zkapp.hash(),
1711 None => default_zkapp_hash(),
1712 };
1713 inputs.append(&field_zkapp);
1714 inputs.append(permissions);
1715
1716 let TimingAsRecord {
1718 is_timed,
1719 initial_minimum_balance,
1720 cliff_time,
1721 cliff_amount,
1722 vesting_period,
1723 vesting_increment,
1724 } = timing.to_record();
1725 inputs.append_bool(is_timed);
1726 inputs.append_u64(initial_minimum_balance.as_u64());
1727 inputs.append_u32(cliff_time.as_u32());
1728 inputs.append_u64(cliff_amount.as_u64());
1729 inputs.append_u32(vesting_period.as_u32());
1730 inputs.append_u64(vesting_increment.as_u64());
1731
1732 inputs.append_field(voting_for.0);
1734 let delegate = MyCow::borrow_or_else(delegate, CompressedPubKey::empty);
1736 inputs.append(delegate.as_ref());
1737 inputs.append_field(receipt_chain_hash.0);
1739 inputs.append_u32(nonce.as_u32());
1741 inputs.append_u64(balance.as_u64());
1743 assert!(token_symbol.len() <= 6);
1746 inputs.append(token_symbol);
1747 inputs.append_field(token_id.0);
1749 inputs.append(public_key);
1751 }
1752}
1753
1754fn verify_merkle_path(account: &Account, merkle_path: &[MerklePath]) -> Fp {
1755 let account_hash = account.hash();
1756
1757 merkle_path
1758 .iter()
1759 .enumerate()
1760 .fold(account_hash, |accum, (height, path)| {
1761 let hashes = match path {
1762 MerklePath::Left(right) => [accum, *right],
1763 MerklePath::Right(left) => [*left, accum],
1764 };
1765 let param = get_merkle_param_for_height(height);
1766 hash_with_kimchi(param, &hashes)
1767 })
1768}
1769
1770pub fn checked_verify_merkle_path(
1772 account: &Account,
1773 merkle_path: &[MerklePath],
1774 w: &mut Witness<Fp>,
1775) -> Fp {
1776 use crate::proofs::transaction::transaction_snark::checked_hash;
1777
1778 let account_hash = account.checked_hash(w);
1779
1780 merkle_path
1781 .iter()
1782 .enumerate()
1783 .fold(account_hash, |accum, (height, path)| {
1784 let hashes = match path {
1785 MerklePath::Left(right) => [accum, *right],
1786 MerklePath::Right(left) => [*left, accum],
1787 };
1788 w.exists(hashes);
1789
1790 let param = get_merkle_param_for_height(height);
1791 checked_hash(param, &hashes, w)
1792 })
1793}
1794
1795#[cfg(test)]
1796mod tests {
1797 use o1_utils::FieldHelpers;
1798
1799 #[cfg(target_family = "wasm")]
1800 use wasm_bindgen_test::wasm_bindgen_test as test;
1801
1802 use super::*;
1803
1804 #[test]
1805 fn test_size_account() {
1806 const SIZE_WITH_9LIMBS: usize = 296;
1807
1808 #[cfg(not(target_family = "wasm"))]
1809 const SIZE: usize = 280;
1810
1811 #[cfg(target_family = "wasm")]
1813 const SIZE: usize = 280;
1814
1815 if std::mem::size_of::<BigInteger256>() == 9 * 4 {
1816 assert_eq!(std::mem::size_of::<Account>(), SIZE_WITH_9LIMBS);
1817 } else {
1818 assert_eq!(std::mem::size_of::<Account>(), SIZE);
1819 }
1820 }
1821
1822 #[test]
1823 fn test_hash_account() {
1824 let acc = Account::create();
1825 let hash = acc.hash();
1826
1827 elog!("account_hash={}", hash);
1828 elog!("account_hash={}", hash.to_hex());
1829
1830 assert_eq!(
1831 hash.to_hex(),
1832 "7018596b7a10344908c7582482b1401a3cbdd40212beb428baf629b84ceb7f0b"
1833 );
1834
1835 let acc = Account {
1836 public_key: CompressedPubKey::from_address(
1837 "B62qnzbXmRNo9q32n4SNu2mpB8e7FYYLH8NmaX6oFCBYjjQ8SbD7uzV",
1838 )
1839 .unwrap(),
1840 token_id: TokenId::default(),
1841 token_symbol: TokenSymbol::from("seb".to_string().into_bytes()),
1842 balance: Balance::from_u64(10101),
1843 nonce: Nonce::from_u32(62772),
1844 receipt_chain_hash: ReceiptChainHash::empty(),
1845 delegate: None,
1846 voting_for: VotingFor::dummy(),
1847 timing: Timing::Untimed,
1848 permissions: Permissions::user_default(),
1849 zkapp: None,
1850 };
1851
1852 assert_eq!(
1853 acc.hash().to_hex(),
1854 "8cb53d374b844227d4c63b2dcf198312f8fcb4b60392fee5b165243508d16e32"
1855 );
1856 }
1857
1858 #[test]
1859 fn test_hash_genesis_winner_account() {
1860 let acc = Account {
1861 public_key: CompressedPubKey::from_address(
1862 "B62qiy32p8kAKnny8ZFwoMhYpBppM1DWVCqAPBYNcXnsAHhnfAAuXgg",
1863 )
1864 .unwrap(),
1865 token_id: TokenId::default(),
1866 token_symbol: TokenSymbol::default(),
1867 balance: Balance::from_u64(20000001000),
1868 nonce: Nonce::from_u32(0),
1869 receipt_chain_hash: ReceiptChainHash::parse_str(
1870 "2mzbV7WevxLuchs2dAMY4vQBS6XttnCUF8Hvks4XNBQ5qiSGGBQe",
1871 )
1872 .unwrap(),
1873 delegate: Some(
1874 CompressedPubKey::from_address(
1875 "B62qiy32p8kAKnny8ZFwoMhYpBppM1DWVCqAPBYNcXnsAHhnfAAuXgg",
1876 )
1877 .unwrap(),
1878 ),
1879 voting_for: VotingFor::parse_str(
1880 "3NK2tkzqqK5spR2sZ7tujjqPksL45M3UUrcA4WhCkeiPtnugyE2x",
1881 )
1882 .unwrap(),
1883 timing: Timing::Untimed,
1884 permissions: Permissions {
1885 edit_state: AuthRequired::Signature,
1886 access: AuthRequired::None,
1887 send: AuthRequired::Signature,
1888 receive: AuthRequired::None,
1889 set_delegate: AuthRequired::Signature,
1890 set_permissions: AuthRequired::Signature,
1891 set_verification_key: SetVerificationKey {
1892 auth: AuthRequired::Signature,
1893 txn_version: TxnVersion::from_u32(2),
1894 },
1895 set_zkapp_uri: AuthRequired::Signature,
1896 edit_action_state: AuthRequired::Signature,
1897 set_token_symbol: AuthRequired::Signature,
1898 increment_nonce: AuthRequired::Signature,
1899 set_voting_for: AuthRequired::Signature,
1900 set_timing: AuthRequired::Signature,
1901 },
1902 zkapp: None,
1903 };
1904
1905 println!("{:?}", acc);
1906
1907 assert_eq!(
1908 mina_p2p_messages::v2::LedgerHash::from_fp(acc.hash()).to_string(),
1909 "jwnEz6CjzSYowUsvw5gKpuTkRjBY5dEtc6YmQj1U5d2k5KZzTmc"
1910 );
1911 }
1912
1913 #[test]
1914 fn test_dummy_sideloaded_verification_key() {
1915 assert_eq!(
1916 VerificationKey::dummy().hash().to_hex(),
1917 "d6da18e4091fbcd86843604fb8ff2d9613e76fa16c49b0263a1566a8e7188007"
1918 );
1919 }
1920
1921 #[test]
1922 fn test_rand() {
1923 for _ in 0..1000 {
1924 let rand = Account::rand();
1925 let hash = rand.hash();
1926
1927 let bytes = Account::serialize(&rand);
1928 let rand2: Account = Account::deserialize(&bytes);
1929
1930 assert_eq!(hash, rand2.hash());
1931 }
1932 }
1933
1934 #[cfg(not(target_family = "wasm"))] #[test]
1936 fn test_rand_tree() {
1937 }
1988}