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)]
1289#[serde(into = "v2::MinaBaseAccountBinableArgStableV2")]
1290#[serde(try_from = "v2::MinaBaseAccountBinableArgStableV2")]
1291pub struct Account {
1292 pub public_key: CompressedPubKey,
1295
1296 pub token_id: TokenId,
1300
1301 pub token_symbol: TokenSymbol,
1306
1307 pub balance: Balance,
1310
1311 pub nonce: Nonce,
1317
1318 pub receipt_chain_hash: ReceiptChainHash,
1325
1326 pub delegate: Option<CompressedPubKey>,
1331
1332 pub voting_for: VotingFor,
1336
1337 pub timing: Timing,
1344
1345 pub permissions: Permissions<AuthRequired>,
1350
1351 pub zkapp: Option<Box<ZkAppAccount>>,
1359}
1360
1361impl Account {
1362 #[cfg(test)]
1363 pub fn create() -> Self {
1364 let pubkey = CompressedPubKey::from_address(
1365 "B62qnzbXmRNo9q32n4SNu2mpB8e7FYYLH8NmaX6oFCBYjjQ8SbD7uzV",
1366 )
1367 .unwrap();
1368
1369 Self {
1370 public_key: pubkey.clone(),
1371 token_id: TokenId::default(),
1372 token_symbol: TokenSymbol::default(),
1373 balance: Balance::from_u64(10101),
1374 nonce: Nonce::zero(),
1375 receipt_chain_hash: ReceiptChainHash::empty(),
1376 delegate: Some(pubkey),
1377 voting_for: VotingFor::dummy(),
1378 timing: Timing::Untimed,
1379 permissions: Permissions::user_default(),
1380 zkapp: None,
1381 }
1382 }
1383
1384 pub fn create_with(account_id: AccountId, balance: Balance) -> Self {
1385 let delegate = if account_id.token_id.is_default() {
1386 Some(account_id.public_key.clone())
1388 } else {
1389 None
1390 };
1391
1392 Self {
1393 public_key: account_id.public_key,
1394 token_id: account_id.token_id,
1395 token_symbol: TokenSymbol::default(),
1396 balance,
1397 nonce: Nonce::zero(),
1398 receipt_chain_hash: ReceiptChainHash::empty(),
1399 delegate,
1400 voting_for: VotingFor::dummy(),
1401 timing: Timing::Untimed,
1402 permissions: Permissions::user_default(),
1403 zkapp: None,
1404 }
1405 }
1406
1407 pub fn delegate_or_empty(&self) -> MyCow<'_, CompressedPubKey> {
1408 MyCow::borrow_or_else(&self.delegate, CompressedPubKey::empty)
1409 }
1410
1411 pub fn zkapp_or_empty(&self) -> MyCow<'_, Box<ZkAppAccount>> {
1412 MyCow::borrow_or_else(&self.zkapp, Box::<ZkAppAccount>::default)
1413 }
1414
1415 pub fn initialize(account_id: &AccountId) -> Self {
1416 Self::create_with(account_id.clone(), Balance::zero())
1417 }
1418
1419 pub fn deserialize(bytes: &[u8]) -> Self {
1420 let mut cursor = Cursor::new(bytes);
1421 Account::binprot_read(&mut cursor).unwrap()
1422 }
1423
1424 pub fn serialize(&self) -> Vec<u8> {
1425 let mut bytes = Vec::with_capacity(10000);
1426 self.binprot_write(&mut bytes).unwrap();
1427 bytes
1428 }
1429
1430 pub fn empty() -> Self {
1431 Self {
1432 public_key: CompressedPubKey {
1433 x: Fp::zero(),
1434 is_odd: false,
1435 },
1436 token_id: TokenId::default(),
1437 token_symbol: TokenSymbol::default(),
1438 balance: Balance::zero(),
1439 nonce: Nonce::zero(),
1440 receipt_chain_hash: ReceiptChainHash::empty(),
1441 delegate: None,
1442 voting_for: VotingFor::dummy(),
1443 timing: Timing::Untimed,
1444 permissions: Permissions::user_default(),
1445 zkapp: None,
1446 }
1447 }
1448
1449 pub fn id(&self) -> AccountId {
1450 AccountId {
1451 public_key: self.public_key.clone(),
1452 token_id: self.token_id.clone(),
1453 }
1454 }
1455
1456 pub fn has_locked_tokens(&self, global_slot: Slot) -> bool {
1457 match self.timing {
1458 Timing::Untimed => false,
1459 Timing::Timed { .. } => {
1460 let curr_min_balance = self.min_balance_at_slot(global_slot);
1461
1462 !curr_min_balance.is_zero()
1463 }
1464 }
1465 }
1466
1467 pub fn has_locked_tokens_checked(
1468 &self,
1469 global_slot: &CheckedSlot<Fp>,
1470 w: &mut Witness<Fp>,
1471 ) -> Boolean {
1472 let TimingAsRecordChecked {
1473 is_timed: _,
1474 initial_minimum_balance,
1475 cliff_time,
1476 cliff_amount,
1477 vesting_period,
1478 vesting_increment,
1479 } = self.timing.to_record_checked::<Fp>();
1480
1481 let cur_min_balance = checked_min_balance_at_slot(
1482 global_slot,
1483 &cliff_time,
1484 &cliff_amount,
1485 &vesting_period,
1486 &vesting_increment,
1487 &initial_minimum_balance,
1488 w,
1489 );
1490
1491 let zero_min_balance = CheckedBalance::zero().equal(&cur_min_balance, w);
1492 zero_min_balance.neg()
1493 }
1494
1495 pub fn liquid_balance_at_slot(&self, global_slot: Slot) -> Balance {
1496 match self.timing {
1497 Timing::Untimed => self.balance,
1498 Timing::Timed { .. } => self
1499 .balance
1500 .sub_amount(self.min_balance_at_slot(global_slot).to_amount())
1501 .unwrap(),
1502 }
1503 }
1504
1505 pub fn min_balance_at_slot(&self, global_slot: Slot) -> Balance {
1506 match self.timing {
1507 Timing::Untimed => Balance::zero(),
1508 Timing::Timed {
1509 initial_minimum_balance,
1510 cliff_time,
1511 cliff_amount,
1512 vesting_period,
1513 vesting_increment,
1514 } => {
1515 if global_slot < cliff_time {
1516 initial_minimum_balance
1517 } else if vesting_period.is_zero() {
1518 Balance::zero()
1520 } else {
1521 match initial_minimum_balance.sub_amount(cliff_amount) {
1522 None => Balance::zero(),
1523 Some(min_balance_past_cliff) => {
1524 let num_periods = (global_slot.as_u32() - cliff_time.as_u32())
1527 / vesting_period.as_u32();
1528 let num_periods: u64 = num_periods.into();
1529
1530 let vesting_decrement = {
1531 let vesting_increment = vesting_increment.as_u64();
1532
1533 if u64::MAX
1534 .checked_div(num_periods)
1535 .map(|res| {
1536 matches!(
1537 res.cmp(&vesting_increment),
1538 std::cmp::Ordering::Less
1539 )
1540 })
1541 .unwrap_or(false)
1542 {
1543 Amount::from_u64(u64::MAX)
1545 } else {
1546 Amount::from_u64(
1547 num_periods.checked_mul(vesting_increment).unwrap(),
1548 )
1549 }
1550 };
1551
1552 match min_balance_past_cliff.sub_amount(vesting_decrement) {
1553 None => Balance::zero(),
1554 Some(amount) => amount,
1555 }
1556 }
1557 }
1558 }
1559 }
1560 }
1561 }
1562
1563 pub fn has_permission_to(&self, control: ControlTag, to: PermissionTo) -> bool {
1565 match to {
1566 PermissionTo::Access => check_permission(self.permissions.access, control),
1567 PermissionTo::Send => check_permission(self.permissions.send, control),
1568 PermissionTo::Receive => check_permission(self.permissions.receive, control),
1569 PermissionTo::SetDelegate => check_permission(self.permissions.set_delegate, control),
1570 PermissionTo::IncrementNonce => {
1571 check_permission(self.permissions.increment_nonce, control)
1572 }
1573 }
1574 }
1575
1576 pub fn checked_has_permission_to<F: FieldWitness>(
1577 &self,
1578 consts: PermsConst,
1579 signature_verifies: Option<Boolean>,
1580 to: PermissionTo,
1581 w: &mut Witness<F>,
1582 ) -> Boolean {
1583 let signature_verifies = match signature_verifies {
1584 Some(signature_verifies) => signature_verifies,
1585 None => match to {
1586 PermissionTo::Send => Boolean::True,
1587 PermissionTo::Receive => Boolean::False,
1588 PermissionTo::SetDelegate => Boolean::True,
1589 PermissionTo::IncrementNonce => Boolean::True,
1590 PermissionTo::Access => {
1591 panic!("signature_verifies argument must be given for access permission")
1592 }
1593 },
1594 };
1595
1596 let auth = match to {
1597 PermissionTo::Send => self.permissions.send,
1598 PermissionTo::Receive => self.permissions.receive,
1599 PermissionTo::SetDelegate => self.permissions.set_delegate,
1600 PermissionTo::IncrementNonce => self.permissions.increment_nonce,
1601 PermissionTo::Access => self.permissions.access,
1602 };
1603
1604 eval_no_proof(
1605 auth,
1606 signature_verifies,
1607 consts.and_const,
1608 consts.or_const,
1609 w,
1610 )
1611 }
1612
1613 pub fn has_permission_to_send(&self) -> bool {
1615 self.has_permission_to(ControlTag::Signature, PermissionTo::Access)
1616 && self.has_permission_to(ControlTag::Signature, PermissionTo::Send)
1617 }
1618
1619 pub fn has_permission_to_receive(&self) -> bool {
1621 self.has_permission_to(ControlTag::NoneGiven, PermissionTo::Access)
1622 && self.has_permission_to(ControlTag::NoneGiven, PermissionTo::Receive)
1623 }
1624
1625 pub fn has_permission_to_set_delegate(&self) -> bool {
1627 self.has_permission_to(ControlTag::Signature, PermissionTo::Access)
1628 && self.has_permission_to(ControlTag::Signature, PermissionTo::SetDelegate)
1629 }
1630
1631 pub fn has_permission_to_increment_nonce(&self) -> bool {
1633 self.has_permission_to(ControlTag::Signature, PermissionTo::Access)
1634 && self.has_permission_to(ControlTag::Signature, PermissionTo::IncrementNonce)
1635 }
1636
1637 pub fn hash(&self) -> Fp {
1638 self.hash_with_param(&MINA_ACCOUNT)
1639 }
1640
1641 pub fn checked_hash(&self, w: &mut Witness<Fp>) -> Fp {
1642 use crate::proofs::transaction::transaction_snark::checked_hash;
1643
1644 let inputs = self.to_inputs_owned();
1645
1646 checked_hash(&MINA_ACCOUNT, &inputs.to_fields(), w)
1647 }
1648
1649 pub fn rand() -> Self {
1650 let mut rng = rand::thread_rng();
1651 let rng = &mut rng;
1652
1653 let symbol: u64 = rng.gen();
1654 let mut symbol = symbol.to_string();
1655 symbol.truncate(6);
1656
1657 let zkapp_uri: u64 = rng.gen();
1658 let mut zkapp_uri = zkapp_uri.to_string();
1659 zkapp_uri.truncate(6);
1660
1661 let gen_perm = |rng: &mut ThreadRng| {
1662 let n: u64 = rng.gen();
1663 if n % 5 == 0 {
1664 AuthRequired::Either
1665 } else if n % 4 == 0 {
1666 AuthRequired::Impossible
1667 } else if n % 3 == 0 {
1668 AuthRequired::None
1669 } else if n % 2 == 0 {
1670 AuthRequired::Proof
1671 } else {
1672 AuthRequired::Signature
1673 }
1674 };
1675
1676 Self {
1677 public_key: gen_compressed(),
1678 token_id: TokenId(Fp::rand(rng)),
1679 token_symbol: TokenSymbol(symbol.into_bytes()),
1680 balance: rng.gen(),
1681 nonce: rng.gen(),
1682 receipt_chain_hash: ReceiptChainHash(Fp::rand(rng)),
1683 delegate: if rng.gen() {
1684 Some(gen_compressed())
1685 } else {
1686 None
1687 },
1688 voting_for: VotingFor(Fp::rand(rng)),
1689 timing: if rng.gen() {
1690 Timing::Untimed
1691 } else {
1692 Timing::Timed {
1693 initial_minimum_balance: rng.gen(),
1694 cliff_time: rng.gen(),
1695 cliff_amount: rng.gen(),
1696 vesting_period: rng.gen(),
1697 vesting_increment: rng.gen(),
1698 }
1699 },
1700 permissions: Permissions {
1701 edit_state: gen_perm(rng),
1702 send: gen_perm(rng),
1703 receive: gen_perm(rng),
1704 set_delegate: gen_perm(rng),
1705 set_permissions: gen_perm(rng),
1706 set_verification_key: SetVerificationKey {
1707 auth: gen_perm(rng),
1708 txn_version: TXN_VERSION_CURRENT, },
1710 set_zkapp_uri: gen_perm(rng),
1711 edit_action_state: gen_perm(rng),
1712 set_token_symbol: gen_perm(rng),
1713 increment_nonce: gen_perm(rng),
1714 set_voting_for: gen_perm(rng),
1715 access: gen_perm(rng),
1716 set_timing: gen_perm(rng),
1717 },
1718 zkapp: if rng.gen() {
1719 Some(
1720 ZkAppAccount {
1721 app_state: [
1722 Fp::rand(rng),
1723 Fp::rand(rng),
1724 Fp::rand(rng),
1725 Fp::rand(rng),
1726 Fp::rand(rng),
1727 Fp::rand(rng),
1728 Fp::rand(rng),
1729 Fp::rand(rng),
1730 ],
1731 verification_key: if rng.gen() {
1732 Some(VerificationKeyWire::gen())
1733 } else {
1734 None
1735 },
1736 zkapp_version: rng.gen(),
1737 action_state: [
1738 Fp::rand(rng),
1739 Fp::rand(rng),
1740 Fp::rand(rng),
1741 Fp::rand(rng),
1742 Fp::rand(rng),
1743 ],
1744 last_action_slot: rng.gen(),
1745 proved_state: rng.gen(),
1746 zkapp_uri: ZkAppUri(zkapp_uri.into_bytes()),
1747 }
1748 .into(),
1749 )
1750 } else {
1751 None
1752 },
1753 }
1754 }
1755}
1756
1757pub fn default_zkapp_hash() -> Fp {
1758 static HASH: Lazy<Fp> = Lazy::new(|| {
1759 let default = ZkAppAccount::default();
1760 default.hash()
1761 });
1762 *HASH
1763}
1764
1765impl ToInputs for Account {
1766 fn to_inputs(&self, inputs: &mut Inputs) {
1767 let Self {
1768 public_key,
1769 token_id,
1770 token_symbol,
1771 balance,
1772 nonce,
1773 receipt_chain_hash,
1774 delegate,
1775 voting_for,
1776 timing,
1777 permissions,
1778 zkapp,
1779 } = self;
1780
1781 let field_zkapp = match zkapp.as_ref() {
1783 Some(zkapp) => zkapp.hash(),
1784 None => default_zkapp_hash(),
1785 };
1786 inputs.append(&field_zkapp);
1787 inputs.append(permissions);
1788
1789 let TimingAsRecord {
1791 is_timed,
1792 initial_minimum_balance,
1793 cliff_time,
1794 cliff_amount,
1795 vesting_period,
1796 vesting_increment,
1797 } = timing.to_record();
1798 inputs.append_bool(is_timed);
1799 inputs.append_u64(initial_minimum_balance.as_u64());
1800 inputs.append_u32(cliff_time.as_u32());
1801 inputs.append_u64(cliff_amount.as_u64());
1802 inputs.append_u32(vesting_period.as_u32());
1803 inputs.append_u64(vesting_increment.as_u64());
1804
1805 inputs.append_field(voting_for.0);
1807 let delegate = MyCow::borrow_or_else(delegate, CompressedPubKey::empty);
1809 inputs.append(delegate.as_ref());
1810 inputs.append_field(receipt_chain_hash.0);
1812 inputs.append_u32(nonce.as_u32());
1814 inputs.append_u64(balance.as_u64());
1816 assert!(token_symbol.len() <= 6);
1819 inputs.append(token_symbol);
1820 inputs.append_field(token_id.0);
1822 inputs.append(public_key);
1824 }
1825}
1826
1827fn verify_merkle_path(account: &Account, merkle_path: &[MerklePath]) -> Fp {
1828 let account_hash = account.hash();
1829
1830 merkle_path
1831 .iter()
1832 .enumerate()
1833 .fold(account_hash, |accum, (height, path)| {
1834 let hashes = match path {
1835 MerklePath::Left(right) => [accum, *right],
1836 MerklePath::Right(left) => [*left, accum],
1837 };
1838 let param = get_merkle_param_for_height(height);
1839 hash_with_kimchi(param, &hashes)
1840 })
1841}
1842
1843pub fn checked_verify_merkle_path(
1845 account: &Account,
1846 merkle_path: &[MerklePath],
1847 w: &mut Witness<Fp>,
1848) -> Fp {
1849 use crate::proofs::transaction::transaction_snark::checked_hash;
1850
1851 let account_hash = account.checked_hash(w);
1852
1853 merkle_path
1854 .iter()
1855 .enumerate()
1856 .fold(account_hash, |accum, (height, path)| {
1857 let hashes = match path {
1858 MerklePath::Left(right) => [accum, *right],
1859 MerklePath::Right(left) => [*left, accum],
1860 };
1861 w.exists(hashes);
1862
1863 let param = get_merkle_param_for_height(height);
1864 checked_hash(param, &hashes, w)
1865 })
1866}
1867
1868#[cfg(test)]
1869mod tests {
1870 use o1_utils::FieldHelpers;
1871
1872 #[cfg(target_family = "wasm")]
1873 use wasm_bindgen_test::wasm_bindgen_test as test;
1874
1875 use super::*;
1876
1877 #[test]
1878 fn test_size_account() {
1879 const SIZE_WITH_9LIMBS: usize = 296;
1880
1881 #[cfg(not(target_family = "wasm"))]
1882 const SIZE: usize = 280;
1883
1884 #[cfg(target_family = "wasm")]
1886 const SIZE: usize = 280;
1887
1888 if std::mem::size_of::<BigInteger256>() == 9 * 4 {
1889 assert_eq!(std::mem::size_of::<Account>(), SIZE_WITH_9LIMBS);
1890 } else {
1891 assert_eq!(std::mem::size_of::<Account>(), SIZE);
1892 }
1893 }
1894
1895 #[test]
1896 fn test_hash_account() {
1897 let acc = Account::create();
1898 let hash = acc.hash();
1899
1900 elog!("account_hash={}", hash);
1901 elog!("account_hash={}", hash.to_hex());
1902
1903 assert_eq!(
1904 hash.to_hex(),
1905 "7018596b7a10344908c7582482b1401a3cbdd40212beb428baf629b84ceb7f0b"
1906 );
1907
1908 let acc = Account {
1909 public_key: CompressedPubKey::from_address(
1910 "B62qnzbXmRNo9q32n4SNu2mpB8e7FYYLH8NmaX6oFCBYjjQ8SbD7uzV",
1911 )
1912 .unwrap(),
1913 token_id: TokenId::default(),
1914 token_symbol: TokenSymbol::from("seb".to_string().into_bytes()),
1915 balance: Balance::from_u64(10101),
1916 nonce: Nonce::from_u32(62772),
1917 receipt_chain_hash: ReceiptChainHash::empty(),
1918 delegate: None,
1919 voting_for: VotingFor::dummy(),
1920 timing: Timing::Untimed,
1921 permissions: Permissions::user_default(),
1922 zkapp: None,
1923 };
1924
1925 assert_eq!(
1926 acc.hash().to_hex(),
1927 "8cb53d374b844227d4c63b2dcf198312f8fcb4b60392fee5b165243508d16e32"
1928 );
1929 }
1930
1931 #[test]
1932 fn test_hash_genesis_winner_account() {
1933 let acc = Account {
1934 public_key: CompressedPubKey::from_address(
1935 "B62qiy32p8kAKnny8ZFwoMhYpBppM1DWVCqAPBYNcXnsAHhnfAAuXgg",
1936 )
1937 .unwrap(),
1938 token_id: TokenId::default(),
1939 token_symbol: TokenSymbol::default(),
1940 balance: Balance::from_u64(20000001000),
1941 nonce: Nonce::from_u32(0),
1942 receipt_chain_hash: ReceiptChainHash::parse_str(
1943 "2mzbV7WevxLuchs2dAMY4vQBS6XttnCUF8Hvks4XNBQ5qiSGGBQe",
1944 )
1945 .unwrap(),
1946 delegate: Some(
1947 CompressedPubKey::from_address(
1948 "B62qiy32p8kAKnny8ZFwoMhYpBppM1DWVCqAPBYNcXnsAHhnfAAuXgg",
1949 )
1950 .unwrap(),
1951 ),
1952 voting_for: VotingFor::parse_str(
1953 "3NK2tkzqqK5spR2sZ7tujjqPksL45M3UUrcA4WhCkeiPtnugyE2x",
1954 )
1955 .unwrap(),
1956 timing: Timing::Untimed,
1957 permissions: Permissions {
1958 edit_state: AuthRequired::Signature,
1959 access: AuthRequired::None,
1960 send: AuthRequired::Signature,
1961 receive: AuthRequired::None,
1962 set_delegate: AuthRequired::Signature,
1963 set_permissions: AuthRequired::Signature,
1964 set_verification_key: SetVerificationKey {
1965 auth: AuthRequired::Signature,
1966 txn_version: TxnVersion::from_u32(2),
1967 },
1968 set_zkapp_uri: AuthRequired::Signature,
1969 edit_action_state: AuthRequired::Signature,
1970 set_token_symbol: AuthRequired::Signature,
1971 increment_nonce: AuthRequired::Signature,
1972 set_voting_for: AuthRequired::Signature,
1973 set_timing: AuthRequired::Signature,
1974 },
1975 zkapp: None,
1976 };
1977
1978 println!("{:?}", acc);
1979
1980 assert_eq!(
1981 mina_p2p_messages::v2::LedgerHash::from_fp(acc.hash()).to_string(),
1982 "jwnEz6CjzSYowUsvw5gKpuTkRjBY5dEtc6YmQj1U5d2k5KZzTmc"
1983 );
1984 }
1985
1986 #[test]
1987 fn test_dummy_sideloaded_verification_key() {
1988 assert_eq!(
1989 VerificationKey::dummy().hash().to_hex(),
1990 "d6da18e4091fbcd86843604fb8ff2d9613e76fa16c49b0263a1566a8e7188007"
1991 );
1992 }
1993
1994 #[test]
1995 fn test_rand() {
1996 for _ in 0..1000 {
1997 let rand = Account::rand();
1998 let hash = rand.hash();
1999
2000 let bytes = Account::serialize(&rand);
2001 let rand2: Account = Account::deserialize(&bytes);
2002
2003 assert_eq!(hash, rand2.hash());
2004 }
2005 }
2006
2007 #[cfg(not(target_family = "wasm"))] #[test]
2009 fn test_rand_tree() {
2010 }
2061}