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