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