1use std::{
2 collections::{
3 hash_map::Entry::{Occupied, Vacant},
4 HashMap, HashSet,
5 },
6 marker::PhantomData,
7};
8
9use ark_ff::{UniformRand, Zero};
10use mina_curves::pasta::Fp;
11use mina_signer::{CompressedPubKey, Keypair, Signature};
12use rand::{
13 rngs::ThreadRng,
14 seq::{IteratorRandom, SliceRandom},
15 Rng,
16};
17
18use crate::{
19 gen_compressed, gen_keypair,
20 generators::{
21 zkapp_command_builder, ACCOUNT_CREATION_FEE, MAX_ACCOUNT_UPDATES, MAX_TOKEN_UPDATES,
22 },
23 scan_state::{
24 currency::{Amount, Balance, Fee, Index, Length, Magnitude, Nonce, Sgn, Signed, Slot},
25 transaction_logic::{
26 cons_zkapp_command_commitment,
27 for_tests::HashableCompressedPubKey,
28 protocol_state::{self, ProtocolStateView},
29 zkapp_command::{
30 self, AccountPreconditions, AccountUpdateSimple, AuthorizationKind, CallForest,
31 ClosedInterval, Control, FeePayer, FeePayerBody, MayUseToken, Numeric, OrIgnore,
32 Preconditions, SetOrKeep, Update, WithStackHash, ZkAppCommand, ZkAppPreconditions,
33 },
34 Memo,
35 },
36 },
37 zkapps::{
38 non_snark::ZkappNonSnark,
39 zkapp_logic::{update_action_state, ZkAppCommandElt},
40 },
41 Account, AccountId, AuthRequired, BaseLedger, ControlTag, Mask, MutableFp, MyCowMut,
42 Permissions, ReceiptChainHash, SetVerificationKey, TokenId, VerificationKeyWire, VotingFor,
43 ZkAppAccount, TXN_VERSION_CURRENT,
44};
45
46use super::{Failure, NotPermitedOf, Role};
49
50fn gen_invalid_protocol_state_precondition(psv: &ProtocolStateView) -> ZkAppPreconditions {
55 enum Tamperable {
56 BlockchainLength,
57 MinWindowDensity,
58 TotalCurrency,
59 GlobalSlotSinceGenesis,
60 }
61
62 let mut rng = rand::thread_rng();
63
64 let mut protocol_state_precondition = ZkAppPreconditions::accept();
65 let lower = rng.gen::<bool>();
66
67 match [
68 Tamperable::BlockchainLength,
69 Tamperable::MinWindowDensity,
70 Tamperable::TotalCurrency,
71 Tamperable::GlobalSlotSinceGenesis,
72 ]
73 .choose(&mut rng)
74 .unwrap()
75 {
76 Tamperable::BlockchainLength => {
77 let blockchain_length = {
78 let epsilon = Length::from_u32(rng.gen_range(1..10));
79
80 if lower || psv.blockchain_length > epsilon {
81 ClosedInterval {
82 lower: Length::zero(),
83 upper: psv
84 .blockchain_length
85 .checked_sub(&epsilon)
86 .unwrap_or_else(Length::zero),
87 }
88 } else {
89 ClosedInterval {
90 lower: psv.blockchain_length.checked_add(&epsilon).unwrap(),
91 upper: Length::max(),
92 }
93 }
94 };
95
96 protocol_state_precondition.blockchain_length = OrIgnore::Check(blockchain_length);
97 }
98 Tamperable::MinWindowDensity => {
99 let min_window_density = {
100 let epsilon = Length::from_u32(rng.gen_range(1..10));
101
102 if lower || psv.min_window_density > epsilon {
103 ClosedInterval {
104 lower: Length::zero(),
105 upper: psv
106 .min_window_density
107 .checked_sub(&epsilon)
108 .unwrap_or_else(Length::zero),
109 }
110 } else {
111 ClosedInterval {
114 lower: psv.blockchain_length.checked_add(&epsilon).unwrap(),
115 upper: Length::max(),
116 }
117 }
118 };
119
120 protocol_state_precondition.min_window_density = OrIgnore::Check(min_window_density);
121 }
122 Tamperable::TotalCurrency => {
123 let total_currency = {
124 let epsilon = Amount::from_u64(rng.gen_range(
125 Amount::of_nanomina_int_exn(1_000).as_u64()
126 ..Amount::of_mina_int_exn(1).as_u64(),
127 ));
128
129 if lower || psv.total_currency > epsilon {
130 ClosedInterval {
131 lower: Amount::zero(),
132 upper: psv
133 .total_currency
134 .checked_sub(&epsilon)
135 .unwrap_or_else(Amount::zero),
136 }
137 } else {
138 ClosedInterval {
139 lower: psv.total_currency.checked_add(&epsilon).unwrap(),
140 upper: Amount::max(),
141 }
142 }
143 };
144
145 protocol_state_precondition.total_currency = OrIgnore::Check(total_currency);
146 }
147 Tamperable::GlobalSlotSinceGenesis => {
148 let global_slot_since_genesis = {
149 let epsilon = Slot::from_u32(rng.gen_range(1..10));
150
151 if lower || psv.global_slot_since_genesis > epsilon {
152 ClosedInterval {
153 lower: Slot::zero(),
154 upper: psv
155 .global_slot_since_genesis
156 .checked_sub(&epsilon)
157 .unwrap_or_else(Slot::zero),
158 }
159 } else {
160 ClosedInterval {
161 lower: psv.global_slot_since_genesis.checked_add(&epsilon).unwrap(),
162 upper: Slot::max(),
163 }
164 }
165 };
166
167 protocol_state_precondition.global_slot_since_genesis =
168 OrIgnore::Check(global_slot_since_genesis);
169 }
170 }
171
172 protocol_state_precondition
173}
174
175fn closed_interval_exact<T: Copy>(value: T) -> ClosedInterval<T> {
176 ClosedInterval {
177 lower: value,
178 upper: value,
179 }
180}
181
182fn gen_epoch_data_predicate(
184 epoch_data: &protocol_state::EpochData<Fp>,
185) -> zkapp_command::EpochData {
186 let mut rng = rand::thread_rng();
187
188 let ledger = {
189 let hash = OrIgnore::gen(|| epoch_data.ledger.hash);
190
191 let total_currency =
192 OrIgnore::gen(|| closed_interval_exact(epoch_data.ledger.total_currency));
193
194 zkapp_command::EpochLedger {
195 hash,
196 total_currency,
197 }
198 };
199
200 let seed = OrIgnore::gen(|| epoch_data.seed);
201 let start_checkpoint = OrIgnore::gen(|| epoch_data.start_checkpoint);
202 let lock_checkpoint = OrIgnore::gen(|| epoch_data.lock_checkpoint);
203
204 let epoch_length = OrIgnore::gen(|| {
205 let mut gen = || Length::from_u32(rng.gen_range(0..10));
206
207 ClosedInterval {
208 lower: epoch_data
209 .epoch_length
210 .checked_sub(&gen())
211 .unwrap_or_else(Length::zero),
212 upper: epoch_data.epoch_length.checked_add(&gen()).unwrap(),
213 }
214 });
215
216 zkapp_command::EpochData {
217 ledger,
218 seed,
219 start_checkpoint,
220 lock_checkpoint,
221 epoch_length,
222 }
223}
224
225fn gen_protocol_state_precondition(psv: &ProtocolStateView) -> ZkAppPreconditions {
227 let mut rng = rand::thread_rng();
228
229 let snarked_ledger_hash = OrIgnore::gen(|| psv.snarked_ledger_hash);
230
231 let blockchain_length = OrIgnore::gen(|| {
232 let mut gen = || Length::from_u32(rng.gen_range(0..10));
233
234 ClosedInterval {
235 lower: psv
236 .blockchain_length
237 .checked_sub(&gen())
238 .unwrap_or_else(Length::zero),
239 upper: psv.blockchain_length.checked_add(&gen()).unwrap(),
240 }
241 });
242
243 let min_window_density = OrIgnore::gen(|| {
244 let mut gen = || Length::from_u32(rng.gen_range(0..10));
245
246 ClosedInterval {
247 lower: psv
248 .min_window_density
249 .checked_sub(&gen())
250 .unwrap_or_else(Length::zero),
251 upper: psv.min_window_density.checked_add(&gen()).unwrap(),
252 }
253 });
254
255 let total_currency = OrIgnore::gen(|| {
256 let mut gen = || Amount::from_u64(rng.gen_range(0..Amount::of_mina_int_exn(1).as_u64()));
257
258 ClosedInterval {
259 lower: psv
260 .total_currency
261 .checked_sub(&gen())
262 .unwrap_or_else(Amount::zero),
263 upper: psv
264 .total_currency
265 .checked_add(&gen())
266 .unwrap_or(psv.total_currency),
267 }
268 });
269
270 let global_slot_since_genesis = OrIgnore::gen(|| {
271 let mut gen = || Slot::from_u32(rng.gen_range(0..10));
272
273 ClosedInterval {
274 lower: psv
275 .global_slot_since_genesis
276 .checked_sub(&gen())
277 .unwrap_or_else(Slot::zero),
278 upper: psv.global_slot_since_genesis.checked_add(&gen()).unwrap(),
279 }
280 });
281
282 let staking_epoch_data = gen_epoch_data_predicate(&psv.staking_epoch_data);
283 let next_epoch_data = gen_epoch_data_predicate(&psv.next_epoch_data);
284
285 ZkAppPreconditions {
286 snarked_ledger_hash,
287 blockchain_length,
288 min_window_density,
289 total_currency,
290 global_slot_since_genesis,
291 staking_epoch_data,
292 next_epoch_data,
293 }
294}
295
296fn gen_account_precondition_from_account(
297 failure: Option<Failure>,
298 is_nonce_precondition: Option<bool>,
299 first_use_of_account: bool,
300 account: &Account,
301) -> AccountPreconditions {
302 let is_nonce_precondition = is_nonce_precondition.unwrap_or(false);
303
304 let mut rng = rand::thread_rng();
305
306 let Account {
307 balance,
308 nonce,
309 receipt_chain_hash,
310 delegate,
311 zkapp,
312 ..
313 } = account;
314
315 let b = if is_nonce_precondition {
317 false
318 } else {
319 rng.gen()
320 };
321
322 if b {
323 let balance = OrIgnore::gen(|| {
326 let balance_change_int = rng.gen_range(1..10_000_000);
327 let balance_change = Balance::of_nanomina_int_exn(balance_change_int);
328
329 let lower = balance
330 .checked_sub(&balance_change)
331 .unwrap_or_else(Balance::zero);
332 let upper = balance
333 .checked_add(&balance_change)
334 .unwrap_or_else(Balance::max);
335
336 ClosedInterval { lower, upper }
337 });
338
339 let nonce = OrIgnore::gen(|| {
340 let nonce_change_int = rng.gen_range(1..10);
341 let nonce_change = Nonce::from_u32(nonce_change_int);
342
343 let lower = nonce.checked_sub(&nonce_change).unwrap_or_else(Nonce::zero);
344 let upper = nonce.checked_add(&nonce_change).unwrap_or_else(Nonce::max);
345
346 ClosedInterval { lower, upper }
347 });
348
349 let receipt_chain_hash = if first_use_of_account {
350 OrIgnore::Check(receipt_chain_hash.clone())
351 } else {
352 OrIgnore::Ignore
353 };
354
355 let delegate = match delegate {
356 Some(delegate) => OrIgnore::gen(|| delegate.clone()),
357 None => OrIgnore::Ignore,
358 };
359
360 let (state, action_state, proved_state, is_new) = match zkapp {
361 None => {
362 let state = std::array::from_fn(|_| OrIgnore::Ignore);
365 let action_state = OrIgnore::Ignore;
366 let proved_state = OrIgnore::Ignore;
367 let is_new = OrIgnore::Ignore;
368
369 (state, action_state, proved_state, is_new)
370 }
371 Some(zkapp_account) => {
372 let ZkAppAccount {
373 app_state,
374 action_state,
375 proved_state,
376 ..
377 } = zkapp_account.as_ref();
378 let state = std::array::from_fn(|i| OrIgnore::gen(|| app_state[i]));
379
380 let action_state = {
381 OrIgnore::Check(action_state.choose(&mut rng).copied().unwrap())
383 };
384
385 let proved_state = OrIgnore::Check(*proved_state);
386
387 let is_new = OrIgnore::Check(false);
390
391 (state, action_state, proved_state, is_new)
392 }
393 };
394
395 let mut predicate_account = zkapp_command::Account {
396 balance,
397 nonce,
398 receipt_chain_hash: receipt_chain_hash.map(|a| a.0),
399 delegate,
400 state,
401 action_state,
402 proved_state,
403 is_new,
404 };
405
406 let Account { balance, nonce, .. } = account;
407
408 if let Some(Failure::InvalidAccountPrecondition) = failure {
409 #[derive(Clone, Copy)]
410 enum Tamperable {
411 Balance,
412 Nonce,
413 ReceiptChainHash,
414 Delegate,
415 State,
416 SequenceState,
417 ProvedState,
418 }
419
420 match [
422 Tamperable::Balance,
423 Tamperable::Nonce,
424 Tamperable::ReceiptChainHash,
425 Tamperable::Delegate,
426 Tamperable::State,
427 Tamperable::SequenceState,
428 Tamperable::ProvedState,
429 ]
430 .choose(&mut rng)
431 .copied()
432 .unwrap()
433 {
434 Tamperable::Balance => {
435 let new_balance = if balance.is_zero() {
436 Balance::max()
437 } else {
438 Balance::zero()
439 };
440
441 let balance = OrIgnore::Check(ClosedInterval {
442 lower: new_balance,
443 upper: new_balance,
444 });
445
446 predicate_account.balance = balance;
447 }
448 Tamperable::Nonce => {
449 let new_nonce = if nonce.is_zero() {
450 Nonce::max()
451 } else {
452 Nonce::zero()
453 };
454
455 let nonce = Numeric::gen(|| ClosedInterval::gen(|| new_nonce));
456
457 predicate_account.nonce = nonce;
458 }
459 Tamperable::ReceiptChainHash => {
460 let receipt_chain_hash = OrIgnore::gen(ReceiptChainHash::gen);
461
462 predicate_account.receipt_chain_hash = receipt_chain_hash.map(|v| v.0);
463 }
464 Tamperable::Delegate => {
465 let delegate = OrIgnore::gen(|| gen_keypair().public.into_compressed());
466
467 predicate_account.delegate = delegate;
468 }
469 Tamperable::State => {
470 let field = predicate_account.state.choose_mut(&mut rng).unwrap();
471 *field = OrIgnore::Check(Fp::rand(&mut rng));
472 }
473 Tamperable::SequenceState => {
474 predicate_account.action_state = OrIgnore::Check(Fp::rand(&mut rng));
475 }
476 Tamperable::ProvedState => {
477 let proved_state = match predicate_account.proved_state {
478 OrIgnore::Check(b) => OrIgnore::Check(!b),
479 OrIgnore::Ignore => OrIgnore::Check(true),
480 };
481
482 predicate_account.proved_state = proved_state;
483 }
484 };
485
486 AccountPreconditions(predicate_account)
487 } else {
488 AccountPreconditions(predicate_account)
489 }
490 } else {
491 let Account { nonce, .. } = account;
493
494 match failure {
495 Some(Failure::InvalidAccountPrecondition) => {
496 AccountPreconditions::with_nonce(nonce.succ())
497 }
498 _ => AccountPreconditions::with_nonce(*nonce),
499 }
500 }
501}
502
503struct AccountUpdateBodyComponents<A, B, C, D> {
504 public_key: CompressedPubKey,
505 update: Update,
506 token_id: C,
507 balance_change: A,
508 increment_nonce: B,
509 events: zkapp_command::Events,
510 actions: zkapp_command::Actions,
511 call_data: Fp,
512 call_depth: usize,
513 protocol_state_precondition: ZkAppPreconditions,
514 account_precondition: D,
515 use_full_commitment: B,
516 valid_while_precondition: OrIgnore<ClosedInterval<Slot>>,
517 may_use_token: MayUseToken,
518 authorization_kind: AuthorizationKind,
519}
520
521impl<B, C> AccountUpdateBodyComponents<Fee, B, C, Nonce> {
522 fn to_fee_payer(&self) -> FeePayerBody {
524 FeePayerBody {
525 public_key: self.public_key.clone(),
526 fee: self.balance_change,
527 valid_until: match self.protocol_state_precondition.global_slot_since_genesis {
528 OrIgnore::Ignore => None,
529 OrIgnore::Check(ClosedInterval { lower: _, upper }) => Some(upper),
530 },
531 nonce: self.account_precondition,
532 }
533 }
534}
535
536impl AccountUpdateBodyComponents<Signed<Amount>, bool, TokenId, AccountPreconditions> {
537 #[allow(clippy::wrong_self_convention)]
539 fn to_typical_account_update(self) -> zkapp_command::BodySimple {
540 zkapp_command::BodySimple {
541 public_key: self.public_key,
542 token_id: self.token_id,
543 update: self.update,
544 balance_change: self.balance_change,
545 increment_nonce: self.increment_nonce,
546 events: self.events,
547 actions: self.actions,
548 call_data: self.call_data,
549 call_depth: self.call_depth,
550 preconditions: {
551 Preconditions {
552 network: self.protocol_state_precondition,
553 account: self.account_precondition,
554 valid_while: self.valid_while_precondition,
555 }
556 },
557 use_full_commitment: self.use_full_commitment,
558 implicit_account_creation_fee: false,
559 may_use_token: self.may_use_token,
560 authorization_kind: self.authorization_kind,
561 }
562 }
563}
564
565struct BodyComponentsParams<'a, A, B, C, D> {
566 global_slot: Option<Slot>,
567 update: Option<Update>,
568 account_id: Option<AccountId>,
569 token_id: Option<TokenId>,
570 may_use_token: Option<MayUseToken>,
571 account_ids_seen: Option<&'a mut HashSet<AccountId>>,
572 account_state_tbl: &'a mut HashMap<AccountId, (Account, Role)>,
573 vk: Option<&'a VerificationKeyWire>,
574 failure: Option<&'a Failure>,
575 new_account: Option<bool>,
576 zkapp_account: Option<bool>,
577 is_fee_payer: Option<bool>,
578 available_public_keys: Option<&'a mut HashSet<HashableCompressedPubKey>>,
579 permissions_auth: Option<ControlTag>,
580 required_balance_change: Option<A>,
581 protocol_state_view: Option<&'a ProtocolStateView>,
582 zkapp_account_ids: &'a [AccountId],
583 increment_nonce: (B, bool),
584 authorization_tag: ControlTag,
585 _phantom: PhantomData<(C, D)>,
586}
587
588fn gen_account_update_body_components<A, B, C, D>(
597 params: BodyComponentsParams<A, B, C, D>,
598 gen_balance_change: impl Fn(&Account) -> A,
599 gen_use_full_commitment: impl Fn(&AccountPreconditions) -> B,
600 f_balance_change: impl Fn(&A) -> Signed<Amount>,
601 f_token_id: impl Fn(&TokenId) -> C,
602 f_account_precondition: impl Fn(bool, &Account) -> D,
603 f_account_update_account_precondition: impl Fn(&D) -> AccountPreconditions,
604) -> AccountUpdateBodyComponents<A, B, C, D> {
605 let BodyComponentsParams {
606 global_slot,
607 update,
608 account_id,
609 token_id,
610 may_use_token,
612 account_ids_seen,
613 account_state_tbl,
614 vk,
615 failure,
616 new_account,
617 zkapp_account,
618 is_fee_payer,
619 available_public_keys,
620 permissions_auth,
621 required_balance_change,
622 protocol_state_view,
623 zkapp_account_ids,
624 increment_nonce,
625 authorization_tag,
626 _phantom,
627 } = params;
628
629 let mut rng = rand::thread_rng();
630
631 let new_account = new_account.unwrap_or(false);
632 let zkapp_account = zkapp_account.unwrap_or(false);
633 let is_fee_payer = is_fee_payer.unwrap_or(false);
634
635 assert!(!(is_fee_payer && new_account));
637
638 let token_account = token_id.is_some();
639
640 let mut update = match update {
641 None => Update::gen(
642 Some(token_account),
643 Some(zkapp_account),
644 vk,
645 permissions_auth,
646 ),
647 Some(update) => update,
648 };
649
650 let (account_update_increment_nonce, increment_nonce) = increment_nonce;
652
653 let verification_key = match vk {
654 Some(vk) => vk.clone(),
655 None => VerificationKeyWire::dummy(),
656 };
657
658 let mut account = if new_account {
659 assert!(
660 account_id.is_none(),
661 "gen_account_update_body: new account_update is true, but an account \
662 id, presumably from an existing account, was supplied"
663 );
664 let available_pks = match available_public_keys {
665 None => panic!(
666 "gen_account_update_body: new_account is true, but \
667 available_public_keys not provided"
668 ),
669 Some(available_pks) => available_pks,
670 };
671
672 let available_pk = available_pks
673 .iter()
674 .choose(&mut rng)
675 .cloned()
676 .expect("gen_account_update_body: no available public keys");
677
678 available_pks.remove(&available_pk);
680
681 let account_id = match token_id {
682 Some(custom_token_id) => AccountId::create(available_pk.0, custom_token_id),
683 None => AccountId::create(available_pk.0, TokenId::default()),
684 };
685
686 let mut account_with_pk = Account::create_with(account_id, Balance::zero());
687
688 if zkapp_account {
689 account_with_pk.zkapp = Some(
690 ZkAppAccount {
691 verification_key: Some(verification_key.clone()),
692 ..ZkAppAccount::default()
693 }
694 .into(),
695 );
696 }
697
698 account_with_pk
699 } else {
700 match account_id {
701 None => {
702 if zkapp_account {
703 let zkapp_account_id = zkapp_account_ids.choose(&mut rng).cloned().unwrap();
704 match account_state_tbl.get(&zkapp_account_id) {
705 None => panic!("gen_account_update_body: fail to find zkapp account"),
706 Some((_, Role::FeePayer | Role::NewAccount | Role::NewTokenAccount)) => {
707 panic!(
708 "gen_account_update_body: all zkapp accounts were new \
709 accounts or used as fee_payer accounts"
710 )
711 }
712 Some((account, Role::OrdinaryParticipant)) => account.clone(),
713 }
714 } else {
715 account_state_tbl
716 .values()
717 .filter(|(_, role)| {
718 match (&authorization_tag, role) {
719 (_, Role::FeePayer) => false,
720 (ControlTag::Proof, Role::NewAccount) => false,
721 (_, Role::NewTokenAccount) => false,
722 (_, Role::NewAccount) => {
723 required_balance_change.is_none()
726 }
727 (_, Role::OrdinaryParticipant) => true,
728 }
729 })
730 .choose(&mut rng)
731 .cloned()
732 .unwrap()
733 .0
734 }
735 }
736 Some(account_id) => {
737 let (account, _) = account_state_tbl.get(&account_id).unwrap();
739
740 if zkapp_account && account.zkapp.is_none() {
741 panic!("gen_account_update_body: provided account has no zkapp field");
742 }
743
744 account.clone()
745 }
746 }
747 };
748
749 let public_key = account.public_key.clone();
750 let token_id = account.token_id.clone();
751 let balance_change = match required_balance_change {
752 Some(bal_change) => bal_change,
753 None => gen_balance_change(&account),
754 };
755
756 let mut field_array_list_gen = |max_array_len: usize, max_list_len: usize| {
757 let array_gen = |rng: &mut ThreadRng| -> zkapp_command::Event {
758 let array_len = rng.gen_range(0..max_array_len);
759 zkapp_command::Event((0..array_len).map(|_| Fp::rand(rng)).collect())
760 };
761 let list_len = rng.gen_range(0..max_list_len);
762 (0..list_len)
763 .map(|_| array_gen(&mut rng))
764 .collect::<Vec<_>>()
765 };
766
767 let events = zkapp_command::Events(field_array_list_gen(2, 1));
768 let actions = zkapp_command::Actions(field_array_list_gen(2, 1));
769
770 let call_data = Fp::rand(&mut rng);
771
772 let first_use_of_account = {
773 let account_id = AccountId::create(public_key.clone(), token_id.clone());
774 match account_ids_seen {
775 None => {
776 true
778 }
779 Some(hash_set) => {
780 !hash_set.contains(&account_id)
782 }
783 }
784 };
785
786 let account_precondition = f_account_precondition(first_use_of_account, &account);
787
788 let call_depth: usize = 0;
790
791 let use_full_commitment = {
792 let full_account_precondition =
793 f_account_update_account_precondition(&account_precondition);
794 gen_use_full_commitment(&full_account_precondition)
795 };
796
797 let protocol_state_precondition = match protocol_state_view {
798 Some(psv) => match failure {
799 Some(Failure::InvalidProtocolStatePrecondition) => {
800 gen_invalid_protocol_state_precondition(psv)
801 }
802 _ => gen_protocol_state_precondition(psv),
803 },
804 None => ZkAppPreconditions::accept(),
805 };
806
807 let valid_while_precondition = match global_slot {
808 None => OrIgnore::Ignore,
809 Some(global_slot) => OrIgnore::gen(|| {
810 let mut epsilon = || Slot::from_u32(rng.gen_range(0..10));
811
812 let lower = global_slot
813 .checked_sub(&epsilon())
814 .unwrap_or_else(Slot::zero);
815 let upper = global_slot
816 .checked_add(&epsilon())
817 .unwrap_or_else(Slot::max);
818
819 ClosedInterval { lower, upper }
820 }),
821 };
822
823 match MayUseToken::No {
825 MayUseToken::No => (),
826 MayUseToken::ParentsOwnToken => (),
827 MayUseToken::InheritFromParent => (),
828 };
829
830 let may_use_token = match may_use_token {
831 None => [
832 MayUseToken::InheritFromParent,
833 MayUseToken::No,
834 MayUseToken::ParentsOwnToken,
835 ]
836 .choose(&mut rng)
837 .cloned()
838 .unwrap(),
839 Some(may_use_token) => may_use_token,
840 };
841
842 let token_id = f_token_id(&token_id);
843
844 let authorization_kind = match authorization_tag {
845 ControlTag::NoneGiven => AuthorizationKind::NoneGiven,
846 ControlTag::Signature => AuthorizationKind::Signature,
847 ControlTag::Proof => AuthorizationKind::Proof(verification_key.hash()),
848 };
849
850 let add_balance_and_balance_change =
852 |balance: Balance, balance_change: Signed<Amount>| match balance_change.sgn {
853 Sgn::Pos => balance
854 .add_amount(balance_change.magnitude)
855 .expect("add_balance_and_balance_change: overflow for sum"),
856 Sgn::Neg => balance
857 .sub_amount(balance_change.magnitude)
858 .expect("add_balance_and_balance_change: underflow for difference"),
859 };
860
861 let balance_change_original = balance_change;
862 let balance_change = f_balance_change(&balance_change_original);
863 let nonce_incr = |n: Nonce| if increment_nonce { n.succ() } else { n };
864
865 fn value_to_be_updated<T: Clone>(c: &SetOrKeep<T>, default: &T) -> T {
866 match c {
867 SetOrKeep::Set(x) => x.clone(),
868 SetOrKeep::Keep => default.clone(),
869 }
870 }
871
872 let delegate = |account: &Account| {
873 if is_fee_payer {
874 account.delegate.clone()
875 } else {
876 account
877 .delegate
878 .as_ref()
879 .map(|delegate| value_to_be_updated(&update.delegate, delegate))
880 }
881 };
882
883 let zkapp = |account: &Account| {
884 if is_fee_payer {
885 return account.zkapp.clone();
886 }
887
888 let zk = match account.zkapp.as_ref() {
889 None => return None,
890 Some(zkapp) => zkapp,
891 };
892
893 let app_state: [Fp; 8] = {
894 let account_app_state = &zk.app_state;
895
896 update
897 .app_state
898 .iter()
899 .zip(account_app_state)
900 .map(|(to_be_updated, current)| value_to_be_updated(to_be_updated, current))
901 .collect::<Vec<_>>()
902 .try_into()
903 .unwrap()
904 };
905
906 let action_state = {
907 let last_action_slot = zk.last_action_slot;
908 let txn_global_slot = match protocol_state_view {
909 None => last_action_slot,
910 Some(ps) => ps.global_slot_since_genesis,
911 };
912
913 let (action_state, _last_action_slot) = update_action_state::<ZkappNonSnark<Mask>>(
914 &zk.action_state,
915 &actions,
916 txn_global_slot,
917 last_action_slot,
918 &mut (),
919 );
920
921 action_state
922 };
923
924 let proved_state = {
925 let keeping_app_state = update.app_state.iter().all(|v| v.is_keep());
926 let changing_entire_app_state = update.app_state.iter().all(|v| v.is_set());
927
928 let proof_verifies = matches!(authorization_tag, ControlTag::Proof);
929
930 if keeping_app_state {
931 zk.proved_state
932 } else if proof_verifies {
933 if changing_entire_app_state {
934 true
935 } else {
936 zk.proved_state
937 }
938 } else {
939 false
940 }
941 };
942
943 Some(
944 ZkAppAccount {
945 app_state,
946 action_state,
947 proved_state,
948 ..*zk.clone()
949 }
950 .into(),
951 )
952 };
953
954 match account_state_tbl.entry(account.id()) {
955 Vacant(entry) => {
956 account.balance = add_balance_and_balance_change(account.balance, balance_change);
958 account.nonce = nonce_incr(account.nonce);
959 account.delegate = delegate(&account);
960 account.zkapp = zkapp(&account);
961
962 let role = if token_account {
963 Role::NewTokenAccount
964 } else {
965 Role::NewAccount
966 };
967
968 entry.insert((account, role));
969 }
970 Occupied(mut entry) => {
971 std::mem::drop(account); let (updated_account, _role) = entry.get_mut();
975
976 updated_account.balance =
977 add_balance_and_balance_change(updated_account.balance, balance_change);
978 updated_account.nonce = nonce_incr(updated_account.nonce);
979 updated_account.delegate = delegate(updated_account);
980 updated_account.zkapp = zkapp(updated_account);
981 }
982 }
983
984 AccountUpdateBodyComponents {
985 public_key,
986 update: if new_account {
987 update.verification_key = SetOrKeep::Set(verification_key);
988 update
989 } else {
990 update
991 },
992 token_id,
993 balance_change: balance_change_original,
994 increment_nonce: account_update_increment_nonce,
995 events,
996 actions,
997 call_data,
998 call_depth,
999 protocol_state_precondition,
1000 account_precondition,
1001 valid_while_precondition,
1002 use_full_commitment,
1003 may_use_token,
1004 authorization_kind,
1005 }
1006}
1007
1008fn gen_balance_change(
1031 permissions_auth: Option<ControlTag>,
1032 account: &Account,
1033 failure: Option<&Failure>,
1034 new_account: bool,
1035) -> Signed<Amount> {
1036 let mut rng = rand::thread_rng();
1037
1038 let sgn = if new_account {
1039 Sgn::Pos
1040 } else {
1041 match (failure, permissions_auth) {
1042 (Some(Failure::UpdateNotPermitted(NotPermitedOf::Send)), _) => Sgn::Neg,
1043 (Some(Failure::UpdateNotPermitted(NotPermitedOf::Receive)), _) => Sgn::Pos,
1044 (_, Some(auth)) => match auth {
1045 ControlTag::NoneGiven => Sgn::Pos,
1046 _ => [Sgn::Pos, Sgn::Neg].choose(&mut rng).copied().unwrap(),
1047 },
1048 (_, None) => [Sgn::Pos, Sgn::Neg].choose(&mut rng).copied().unwrap(),
1049 }
1050 };
1051 let effective_balance = account.balance;
1055 let small_balance_change = {
1056 if effective_balance < Balance::of_mina_string_exn("1.0") && !new_account {
1059 panic!("account has low balance");
1060 }
1061
1062 Balance::of_mina_string_exn("0.000001")
1063 };
1064
1065 let magnitude = if new_account {
1066 let min = Amount::of_mina_string_exn("50.0");
1067 let max = Amount::of_mina_string_exn("100.0");
1068 Amount::from_u64(rng.gen_range(min.as_u64()..max.as_u64()))
1069 } else {
1070 Amount::from_u64(rng.gen_range(0..small_balance_change.as_u64()))
1071 };
1072
1073 Signed::<Amount> { magnitude, sgn }
1074}
1075
1076fn gen_use_full_commitment(
1077 increment_nonce: bool,
1078 account_precondition: &AccountPreconditions,
1079 authorization: &zkapp_command::Control,
1080) -> bool {
1081 let incr_nonce_and_constrains_nonce =
1083 increment_nonce && account_precondition.to_full().nonce.is_constant();
1084
1085 let does_not_use_a_signature = !matches!(authorization.tag(), ControlTag::Signature);
1086
1087 if incr_nonce_and_constrains_nonce || does_not_use_a_signature {
1088 rand::thread_rng().gen()
1089 } else {
1090 true
1091 }
1092}
1093
1094struct AccountUpdateParams<'a> {
1095 global_slot: Option<Slot>,
1096 update: Option<Update>,
1097 failure: Option<&'a Failure>,
1098 new_account: Option<bool>,
1099 zkapp_account: Option<bool>,
1100 account_id: Option<AccountId>,
1101 token_id: Option<TokenId>,
1102 may_use_token: Option<MayUseToken>,
1103 permissions_auth: Option<ControlTag>,
1105 required_balance_change: Option<Signed<Amount>>,
1106 zkapp_account_ids: &'a [AccountId],
1107 authorization: zkapp_command::Control,
1108 account_ids_seen: &'a mut HashSet<AccountId>,
1109 available_public_keys: &'a mut HashSet<HashableCompressedPubKey>,
1110 account_state_tbl: &'a mut HashMap<AccountId, (Account, Role)>,
1111 protocol_state_view: Option<&'a ProtocolStateView>,
1112 vk: Option<&'a VerificationKeyWire>,
1113 }
1118
1119fn gen_account_update_from(params: AccountUpdateParams) -> AccountUpdateSimple {
1120 let AccountUpdateParams {
1121 global_slot,
1122 update,
1123 failure,
1124 new_account,
1125 zkapp_account,
1126 account_id,
1127 token_id,
1128 may_use_token,
1129 permissions_auth,
1130 required_balance_change,
1131 zkapp_account_ids,
1132 authorization,
1133 account_ids_seen,
1134 available_public_keys,
1135 account_state_tbl,
1136 protocol_state_view,
1137 vk,
1138 } = params;
1139
1140 let increment_nonce = match params.permissions_auth {
1144 Some(tag) => match tag {
1145 ControlTag::Signature => true,
1146 ControlTag::Proof | ControlTag::NoneGiven => false,
1147 },
1148 None => false,
1149 };
1150
1151 let new_account = new_account.unwrap_or(false);
1152 let zkapp_account = zkapp_account.unwrap_or(false);
1153
1154 let params = BodyComponentsParams {
1155 global_slot,
1156 update,
1157 account_id,
1158 token_id,
1159 may_use_token,
1160 account_ids_seen: Some(account_ids_seen),
1161 account_state_tbl,
1162 vk,
1163 failure,
1164 new_account: Some(new_account),
1165 zkapp_account: Some(zkapp_account),
1166 is_fee_payer: None,
1167 available_public_keys: Some(available_public_keys),
1168 permissions_auth,
1169 required_balance_change,
1170 protocol_state_view,
1171 zkapp_account_ids,
1172 increment_nonce: (increment_nonce, increment_nonce),
1173 authorization_tag: authorization.tag(),
1174 _phantom: PhantomData,
1175 };
1176
1177 let body_components = gen_account_update_body_components(
1178 params,
1179 |account| gen_balance_change(permissions_auth, account, failure, new_account),
1181 |account_precondition| {
1183 gen_use_full_commitment(increment_nonce, account_precondition, &authorization)
1184 },
1185 |balance| *balance,
1187 |token_id| token_id.clone(),
1189 |first_use_of_account, account| {
1191 gen_account_precondition_from_account(None, None, first_use_of_account, account)
1192 },
1193 |a| a.clone(),
1195 );
1196
1197 let body = body_components.to_typical_account_update();
1198 let account_id = AccountId::create(body.public_key.clone(), body.token_id.clone());
1199 account_ids_seen.insert(account_id);
1200
1201 AccountUpdateSimple {
1202 body,
1203 authorization,
1204 }
1205}
1206
1207const MINIMUM_USER_COMMAND_FEE: Fee = Fee::from_u64(1000000);
1209
1210fn gen_fee(account: &Account) -> Fee {
1211 let mut rng = rand::thread_rng();
1212
1213 let balance = account.balance;
1214 let lo_fee = MINIMUM_USER_COMMAND_FEE;
1215 let hi_fee = MINIMUM_USER_COMMAND_FEE.scale(2).unwrap();
1216
1217 assert!(hi_fee <= (Fee::from_u64(balance.as_u64())));
1218
1219 Fee::from_u64(rng.gen_range(lo_fee.as_u64()..hi_fee.as_u64()))
1220}
1221
1222fn fee_to_amt(fee: &Fee) -> Signed<Amount> {
1224 Signed::<Amount>::of_unsigned(Amount::from_u64(fee.as_u64())).negate()
1225}
1226
1227fn gen_account_update_body_fee_payer(
1231 global_slot: Option<Slot>,
1232 failure: Option<&Failure>,
1233 permissions_auth: Option<ControlTag>,
1234 account_id: AccountId,
1235 vk: Option<&VerificationKeyWire>,
1236 protocol_state_view: Option<&ProtocolStateView>,
1237 account_state_tbl: &mut HashMap<AccountId, (Account, Role)>,
1238) -> FeePayerBody {
1239 let account_precondition_gen = |account: &Account| account.nonce;
1240
1241 let body_components = gen_account_update_body_components(
1242 BodyComponentsParams {
1243 global_slot,
1244 update: None,
1245 account_id: Some(account_id),
1246 token_id: None,
1247 may_use_token: None,
1248 account_ids_seen: None,
1249 account_state_tbl,
1250 vk,
1251 failure,
1252 new_account: None,
1253 zkapp_account: None,
1254 is_fee_payer: Some(true),
1255 available_public_keys: None,
1256 permissions_auth,
1257 required_balance_change: None,
1258 protocol_state_view,
1259 zkapp_account_ids: &[],
1260 increment_nonce: ((), true),
1261 authorization_tag: ControlTag::Signature,
1262 _phantom: PhantomData,
1263 },
1264 gen_fee,
1266 |_account_precondition| {},
1268 fee_to_amt,
1270 |token_id| {
1272 assert!(token_id.is_default());
1275 },
1277 |_, account| account_precondition_gen(account),
1279 |nonce| AccountPreconditions::with_nonce(*nonce),
1281 );
1282
1283 body_components.to_fee_payer()
1284}
1285
1286fn gen_fee_payer(
1288 global_slot: Option<Slot>,
1289 failure: Option<&Failure>,
1290 permissions_auth: Option<ControlTag>,
1291 account_id: AccountId,
1292 protocol_state_view: Option<&ProtocolStateView>,
1293 vk: Option<&VerificationKeyWire>,
1294 account_state_tbl: &mut HashMap<AccountId, (Account, Role)>,
1295) -> FeePayer {
1296 let body = gen_account_update_body_fee_payer(
1297 global_slot,
1298 failure,
1299 permissions_auth,
1300 account_id,
1301 vk,
1302 protocol_state_view,
1303 account_state_tbl,
1304 );
1305
1306 let authorization = Signature::dummy();
1308
1309 FeePayer {
1310 body,
1311 authorization,
1312 }
1313}
1314
1315pub struct GenZkappCommandParams<'a> {
1316 pub global_slot: Option<Slot>,
1317 pub failure: Option<&'a Failure>,
1318 pub max_account_updates: Option<usize>,
1319 pub max_token_updates: Option<usize>,
1320 pub fee_payer_keypair: &'a Keypair,
1321 pub keymap: &'a HashMap<HashableCompressedPubKey, Keypair>,
1322 pub account_state_tbl: Option<&'a mut HashMap<AccountId, (Account, Role)>>,
1323 pub ledger: Mask,
1324 pub protocol_state_view: Option<&'a ProtocolStateView>,
1325 pub vk: Option<&'a VerificationKeyWire>,
1326}
1327
1328pub fn gen_zkapp_command_from(params: GenZkappCommandParams) -> ZkAppCommand {
1339 let GenZkappCommandParams {
1340 global_slot,
1341 failure,
1342 max_account_updates,
1343 max_token_updates,
1344 fee_payer_keypair,
1345 keymap,
1346 account_state_tbl,
1347 ledger,
1348 protocol_state_view,
1349 vk,
1350 } = params;
1351
1352 let mut rng = rand::thread_rng();
1353
1354 let max_account_updates = max_account_updates.unwrap_or(MAX_ACCOUNT_UPDATES);
1355 let max_token_updates = max_token_updates.unwrap_or(MAX_TOKEN_UPDATES);
1356
1357 let fee_payer_pk = fee_payer_keypair.public.into_compressed();
1358 let fee_payer_account_id = AccountId::create(fee_payer_pk, TokenId::default());
1359
1360 let ledger_accounts = ledger.to_list();
1361
1362 let mut account_state_tbl = match account_state_tbl {
1367 Some(account_state_tbl) => MyCowMut::Borrow(account_state_tbl),
1368 None => MyCowMut::Own(HashMap::new()),
1369 };
1370 let account_state_tbl = &mut account_state_tbl;
1371
1372 for account in ledger_accounts.into_iter() {
1374 let id = account.id();
1375 let pk = id.public_key.clone();
1376
1377 if let Vacant(entry) = account_state_tbl.entry(id.clone()) {
1379 entry.insert(if id == fee_payer_account_id {
1380 (account, Role::FeePayer)
1381 } else {
1382 (account, Role::OrdinaryParticipant)
1383 });
1384 };
1385
1386 if keymap.get(&HashableCompressedPubKey(pk.clone())).is_none() {
1387 panic!(
1388 "gen_zkapp_command_from: public key {:?} is in ledger, but not keymap",
1389 pk
1390 );
1391 }
1392 }
1393
1394 let ledger_account_list: Vec<AccountId> = ledger
1398 .accounts()
1399 .iter()
1400 .chain(account_state_tbl.keys())
1401 .collect::<HashSet<&AccountId>>() .into_iter()
1403 .cloned()
1404 .collect(); let ledger_pk_list: Vec<CompressedPubKey> = ledger_account_list
1407 .iter()
1408 .map(|id| id.public_key.clone())
1409 .collect();
1410 let ledger_pk_set: HashSet<HashableCompressedPubKey> = ledger_pk_list
1411 .iter()
1412 .map(|pk| HashableCompressedPubKey(pk.clone()))
1413 .collect();
1414
1415 let mut available_public_keys: HashSet<HashableCompressedPubKey> = keymap
1416 .keys()
1417 .filter(|pk| !ledger_pk_set.contains(pk))
1418 .cloned()
1419 .collect();
1420 let available_public_keys = &mut available_public_keys;
1421
1422 let mut account_ids_seen = HashSet::<AccountId>::new();
1425 let account_ids_seen = &mut account_ids_seen;
1426
1427 let fee_payer = gen_fee_payer(
1428 global_slot,
1429 failure,
1430 Some(ControlTag::Signature),
1431 fee_payer_account_id.clone(),
1432 protocol_state_view,
1433 vk,
1434 account_state_tbl,
1435 );
1436
1437 let zkapp_account_ids: Vec<AccountId> = account_state_tbl
1438 .iter()
1439 .filter(|(_, (a, role))| match role {
1440 Role::FeePayer | Role::NewAccount | Role::NewTokenAccount => false,
1441 Role::OrdinaryParticipant => a.zkapp.is_some(),
1442 })
1443 .map(|(id, _)| id.clone())
1444 .collect();
1445 let zkapp_account_ids = zkapp_account_ids.as_slice();
1446
1447 account_ids_seen.insert(fee_payer_account_id.clone());
1448
1449 fn mk_forest(
1450 ps: Vec<zkapp_command::Tree<AccountUpdateSimple>>,
1451 ) -> CallForest<AccountUpdateSimple> {
1452 CallForest(
1453 ps.into_iter()
1454 .map(|v| {
1455 WithStackHash {
1456 elt: v,
1457 stack_hash: MutableFp::new(Fp::zero()), }
1459 })
1460 .collect(),
1461 )
1462 }
1463
1464 fn mk_node(
1465 p: AccountUpdateSimple,
1466 calls: Vec<zkapp_command::Tree<AccountUpdateSimple>>,
1467 ) -> zkapp_command::Tree<AccountUpdateSimple> {
1468 zkapp_command::Tree {
1469 account_update: p,
1470 account_update_digest: MutableFp::new(Fp::zero()), calls: mk_forest(calls),
1472 }
1473 }
1474
1475 let mut gen_zkapp_command_with_dynamic_balance =
1476 |new_account: bool, num_zkapp_command: usize| {
1477 let mut rng = rand::thread_rng();
1478 let mut commands = Vec::with_capacity(num_zkapp_command);
1479
1480 for _ in 0..num_zkapp_command {
1481 let (permissions_auth, update) = match failure {
1487 Some(Failure::UpdateNotPermitted(ref update_type)) => {
1488 let is_proof = rng.gen::<bool>();
1489
1490 let auth_tag = if is_proof {
1491 ControlTag::Proof
1492 } else {
1493 ControlTag::Signature
1494 };
1495
1496 let mut perm = Permissions::gen(auth_tag);
1497
1498 match &update_type {
1499 NotPermitedOf::Delegate => {
1500 perm.set_delegate = AuthRequired::from(auth_tag);
1501 }
1502 NotPermitedOf::AppState => {
1503 perm.edit_state = AuthRequired::from(auth_tag);
1504 }
1505 NotPermitedOf::VerificationKey => {
1506 perm.set_verification_key = SetVerificationKey {
1507 auth: AuthRequired::from(auth_tag),
1508 txn_version: TXN_VERSION_CURRENT,
1509 }
1510 }
1511 NotPermitedOf::ZkappUri => {
1512 perm.set_zkapp_uri = AuthRequired::from(auth_tag);
1513 }
1514 NotPermitedOf::TokenSymbol => {
1515 perm.set_token_symbol = AuthRequired::from(auth_tag);
1516 }
1517 NotPermitedOf::VotingFor => {
1518 perm.set_voting_for = AuthRequired::from(auth_tag);
1519 }
1520 NotPermitedOf::Send => {
1521 perm.send = AuthRequired::from(auth_tag);
1522 }
1523 NotPermitedOf::Receive => {
1524 perm.receive = AuthRequired::from(auth_tag);
1525 }
1526 };
1527
1528 (
1529 auth_tag,
1530 Some(Update {
1531 permissions: SetOrKeep::Set(perm),
1532 ..Update::dummy()
1533 }),
1534 )
1535 }
1536 _ => {
1537 let tag = if new_account {
1538 [ControlTag::Signature, ControlTag::NoneGiven]
1539 .choose(&mut rng)
1540 .cloned()
1541 .unwrap()
1542 } else {
1543 ControlTag::gen(&mut rng)
1544 };
1545
1546 (tag, None)
1547 }
1548 };
1549
1550 let zkapp_account = match (failure, permissions_auth) {
1551 (Some(Failure::UpdateNotPermitted(_)), _) | (_, ControlTag::Proof) => true,
1552 (_, ControlTag::Signature) | (_, ControlTag::NoneGiven) => false,
1553 };
1554
1555 let account_update0 = {
1557 let authorization = zkapp_command::Control::Signature(Signature::dummy());
1558 gen_account_update_from(AccountUpdateParams {
1559 global_slot,
1560 update,
1561 failure,
1562 new_account: Some(new_account),
1563 zkapp_account: Some(zkapp_account),
1564 account_id: None,
1565 token_id: None,
1566 may_use_token: None,
1567 permissions_auth: Some(permissions_auth),
1568 required_balance_change: None,
1569 zkapp_account_ids,
1570 authorization,
1571 account_ids_seen,
1572 available_public_keys,
1573 account_state_tbl,
1574 protocol_state_view,
1575 vk,
1576 })
1577 };
1578
1579 let account_update = {
1580 let (authorization, update) = match failure {
1582 Some(Failure::UpdateNotPermitted(update_type)) => {
1583 let auth = match permissions_auth {
1584 ControlTag::Proof => Control::dummy_of_tag(ControlTag::Signature),
1585 ControlTag::Signature => Control::dummy_of_tag(ControlTag::Proof),
1586 _ => Control::dummy_of_tag(ControlTag::NoneGiven),
1587 };
1588
1589 let mut update = Update::dummy();
1590
1591 match update_type {
1592 NotPermitedOf::Delegate => {
1593 update.delegate = SetOrKeep::Set(gen_compressed());
1594 }
1595 NotPermitedOf::AppState => {
1596 update.app_state =
1597 std::array::from_fn(|_| SetOrKeep::Set(Fp::rand(&mut rng)));
1598 }
1599 NotPermitedOf::VerificationKey => {
1600 let vk = VerificationKeyWire::dummy();
1601 update.verification_key = SetOrKeep::Set(vk);
1602 }
1603 NotPermitedOf::ZkappUri => {
1604 update.zkapp_uri = SetOrKeep::Set(
1605 "https://o1labs.org".to_string().into_bytes().into(),
1606 );
1607 }
1608 NotPermitedOf::TokenSymbol => {
1609 update.token_symbol =
1610 SetOrKeep::Set("CODA".to_string().into_bytes().into());
1611 }
1612 NotPermitedOf::VotingFor => {
1613 update.voting_for =
1614 SetOrKeep::Set(VotingFor(Fp::rand(&mut rng)));
1615 }
1616 NotPermitedOf::Send | NotPermitedOf::Receive => {}
1617 };
1618
1619 let new_perm = Permissions::gen(ControlTag::Signature);
1620 update.permissions = SetOrKeep::Set(new_perm);
1621
1622 (auth, Some(update))
1623 }
1624 _ => {
1625 let auth = Control::dummy_of_tag(permissions_auth);
1626 (auth, None)
1627 }
1628 };
1629
1630 let account_id = AccountId::create(
1631 account_update0.body.public_key.clone(),
1632 account_update0.body.token_id.clone(),
1633 );
1634
1635 let permissions_auth = ControlTag::Signature;
1636
1637 gen_account_update_from(AccountUpdateParams {
1638 global_slot,
1639 update,
1640 failure,
1641 new_account: None,
1642 zkapp_account: None,
1643 account_id: Some(account_id),
1644 token_id: None,
1645 may_use_token: None,
1646 permissions_auth: Some(permissions_auth),
1647 required_balance_change: None,
1648 zkapp_account_ids,
1649 authorization,
1650 account_ids_seen,
1651 available_public_keys,
1652 account_state_tbl,
1653 protocol_state_view,
1654 vk,
1655 })
1656 };
1657
1658 commands.push(mk_node(account_update0, vec![]));
1659 commands.push(mk_node(account_update, vec![]));
1660 }
1661
1662 commands
1663 };
1664
1665 let num_zkapp_command = rng.gen_range(1..max_account_updates);
1667 let num_new_accounts = rng.gen_range(0..num_zkapp_command);
1668 let num_old_zkapp_command = num_zkapp_command - num_new_accounts;
1669
1670 let mut old_zkapp_command =
1671 gen_zkapp_command_with_dynamic_balance(false, num_old_zkapp_command);
1672 let mut new_zkapp_command = gen_zkapp_command_with_dynamic_balance(true, num_new_accounts);
1673
1674 let account_updates0: Vec<_> = {
1675 old_zkapp_command.append(&mut new_zkapp_command);
1676 old_zkapp_command
1677 };
1678
1679 let balance_change_sum = account_updates0.iter().fold(
1680 if num_new_accounts == 0 {
1682 Signed::<Amount>::zero()
1683 } else {
1684 let amount = Amount::from_u64(ACCOUNT_CREATION_FEE.as_u64());
1685 let amount = amount.scale(num_new_accounts as u64).unwrap();
1686 Signed::of_unsigned(amount)
1687 },
1688 |accum, node| {
1689 accum
1690 .add(&node.account_update.body.balance_change)
1691 .expect("Overflow adding other zkapp_command balances")
1692 },
1693 );
1694
1695 let balance_change = balance_change_sum.negate();
1701
1702 let balancing_account_update = {
1703 let authorization = Control::Signature(Signature::dummy());
1704 gen_account_update_from(AccountUpdateParams {
1705 global_slot,
1706 update: None,
1707 failure,
1708 new_account: Some(false),
1709 zkapp_account: None,
1710 account_id: None,
1711 token_id: None,
1712 may_use_token: None,
1713 permissions_auth: Some(ControlTag::Signature),
1714 required_balance_change: Some(balance_change),
1715 zkapp_account_ids,
1716 authorization,
1717 account_ids_seen,
1718 available_public_keys,
1719 account_state_tbl,
1720 protocol_state_view,
1721 vk,
1722 })
1723 };
1724
1725 let mut gen_zkapp_command_with_token_accounts = |num_zkapp_command: usize| {
1726 let authorization = Control::Signature(Signature::dummy());
1727 let permissions_auth = ControlTag::Signature;
1728
1729 (0..num_zkapp_command)
1730 .map(|_| {
1731 let parent = {
1732 let required_balance_change = {
1733 let amount = Amount::from_u64(ACCOUNT_CREATION_FEE.as_u64());
1734 Some(Signed::of_unsigned(amount).negate())
1735 };
1736
1737 gen_account_update_from(AccountUpdateParams {
1738 global_slot,
1739 update: None,
1740 failure,
1741 new_account: None,
1742 zkapp_account: None,
1743 account_id: None,
1744 token_id: None,
1745 may_use_token: Some(MayUseToken::No),
1746 permissions_auth: Some(permissions_auth),
1747 required_balance_change,
1748 zkapp_account_ids,
1749 authorization: authorization.clone(),
1750 account_ids_seen,
1751 available_public_keys,
1752 account_state_tbl,
1753 protocol_state_view,
1754 vk,
1755 })
1756 };
1757
1758 let token_id = Some(
1759 AccountId::create(parent.body.public_key.clone(), parent.body.token_id.clone())
1760 .derive_token_id(),
1761 );
1762
1763 let child = gen_account_update_from(AccountUpdateParams {
1764 global_slot,
1765 update: None,
1766 failure,
1767 new_account: Some(true),
1768 zkapp_account: None,
1769 account_id: None,
1770 token_id,
1771 may_use_token: Some(MayUseToken::ParentsOwnToken),
1772 permissions_auth: Some(permissions_auth),
1773 required_balance_change: None,
1774 zkapp_account_ids,
1775 authorization: authorization.clone(),
1776 account_ids_seen,
1777 available_public_keys,
1778 account_state_tbl,
1779 protocol_state_view,
1780 vk,
1781 });
1782
1783 mk_node(parent, vec![mk_node(child, vec![])])
1784 })
1785 .collect::<Vec<_>>()
1786 };
1787
1788 let num_new_token_zkapp_command = rng.gen_range(0..max_token_updates);
1789 let new_token_zkapp_command =
1790 gen_zkapp_command_with_token_accounts(num_new_token_zkapp_command);
1791
1792 let account_updates = mk_forest(
1793 account_updates0
1794 .into_iter()
1795 .chain([mk_node(balancing_account_update, vec![])])
1796 .chain(new_token_zkapp_command)
1797 .collect(),
1798 );
1799
1800 let memo = Memo::gen();
1801 let zkapp_command_dummy_authorizations = ZkAppCommand {
1802 fee_payer,
1803 account_updates: {
1804 let mut account_updates =
1805 account_updates.map_to(zkapp_command::AccountUpdate::of_simple);
1806 account_updates.accumulate_hashes_predicated();
1810 account_updates
1811 },
1812 memo,
1813 };
1814
1815 let receipt_elt = {
1817 let (_txn_commitment, full_txn_commitment) =
1818 zkapp_command_builder::get_transaction_commitments(&zkapp_command_dummy_authorizations);
1819
1820 ZkAppCommandElt::ZkAppCommandCommitment(ReceiptChainHash(full_txn_commitment.0))
1821 };
1822
1823 {
1824 let (account, role) = account_state_tbl
1825 .get_mut(&fee_payer_account_id)
1826 .expect("Expected fee payer account id to be in table");
1827
1828 let receipt_chain_hash = cons_zkapp_command_commitment(
1829 Index::zero(),
1830 receipt_elt.clone(),
1831 &account.receipt_chain_hash,
1832 );
1833
1834 account.receipt_chain_hash = receipt_chain_hash;
1835 *role = Role::FeePayer;
1836 }
1837
1838 let account_updates = zkapp_command_dummy_authorizations
1839 .account_updates
1840 .to_account_updates();
1841
1842 for (index, account_update) in account_updates.iter().enumerate() {
1843 match account_update.authorization {
1844 Control::Proof(_) | Control::Signature(_) => {}
1845 Control::NoneGiven => continue,
1846 }
1847
1848 let acct_id = account_update.account_id();
1849 let (account, _role) = account_state_tbl
1850 .get_mut(&acct_id)
1851 .expect("Expected other account_update account id to be in table");
1852
1853 let account_update_index = Index::from_u32((index + 1).try_into().unwrap());
1854
1855 let receipt_chain_hash = cons_zkapp_command_commitment(
1856 account_update_index,
1857 receipt_elt.clone(),
1858 &account.receipt_chain_hash,
1859 );
1860
1861 account.receipt_chain_hash = receipt_chain_hash;
1862 }
1863
1864 zkapp_command_dummy_authorizations
1865}