mina_tree/generators/
zkapp_command.rs

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
46// use mina_p2p_messages::v2::MinaBaseAccountUpdateCallTypeStableV1 as CallType;
47
48use super::{Failure, NotPermitedOf, Role};
49
50// /// Value when we run `dune runtest src/lib/staged_ledger -f`
51// const ACCOUNT_CREATION_FEE: Fee = Fee::from_u64(1000000000);
52
53/// <https://github.com/MinaProtocol/mina/blob/2ff0292b637684ce0372e7b8e23ec85404dc5091/src/lib/mina_generators/zkapp_command_generators.ml#L443>
54fn 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                    // TODO: This should be `psv.min_window_density` here
112                    //       Should open PR on mina repo
113                    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
182/// <https://github.com/MinaProtocol/mina/blob/2ff0292b637684ce0372e7b8e23ec85404dc5091/src/lib/mina_generators/zkapp_command_generators.ml#L319>
183fn 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
225/// <https://github.com/MinaProtocol/mina/blob/2ff0292b637684ce0372e7b8e23ec85404dc5091/src/lib/mina_generators/zkapp_command_generators.ml#L367>
226fn 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    // choose constructor
316    let b = if is_nonce_precondition {
317        false
318    } else {
319        rng.gen()
320    };
321
322    if b {
323        // Full
324
325        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 len = Pickles_types.Nat.to_int Zkapp_state.Max_state_size.n
363
364                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                    // choose a value from account action state
382                    OrIgnore::Check(action_state.choose(&mut rng).copied().unwrap())
383                };
384
385                let proved_state = OrIgnore::Check(*proved_state);
386
387                // when we apply the generated Zkapp_command.t, the account
388                // is always in the ledger
389                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            // tamper with account using randomly chosen item
421            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        // Nonce
492        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    /// <https://github.com/MinaProtocol/mina/blob/2ff0292b637684ce0372e7b8e23ec85404dc5091/src/lib/mina_generators/zkapp_command_generators.ml#L576>
523    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    /// <https://github.com/MinaProtocol/mina/blob/2ff0292b637684ce0372e7b8e23ec85404dc5091/src/lib/mina_generators/zkapp_command_generators.ml#L592>
538    #[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
588/// The type `a` is associated with the `delta` field, which is an unsigned fee
589/// for the fee payer, and a signed amount for other zkapp_command.
590/// The type `b` is associated with the `use_full_commitment` field, which is
591/// `unit` for the fee payer, and `bool` for other zkapp_command.
592/// The type `c` is associated with the `token_id` field, which is `unit` for the
593/// fee payer, and `Token_id.t` for other zkapp_command.
594/// The type `d` is associated with the `account_precondition` field, which is
595/// a nonce for the fee payer, and `Account_precondition.t` for other zkapp_command
596fn 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        // caller,
611        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    // fee payers have to be in the ledger
636    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    // account_update_increment_nonce for fee payer is unit and increment_nonce is true
651    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 public key no longer available
679        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 only for balancing account_update.
724                                    // Newly created account should not be used in balancing account_update
725                                    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                // get the latest state of the account
738                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                // fee payer
777                true
778            }
779            Some(hash_set) => {
780                // other account_updates
781                !hash_set.contains(&account_id)
782            }
783        }
784    };
785
786    let account_precondition = f_account_precondition(first_use_of_account, &account);
787
788    // update the depth when generating `account_updates` in Zkapp_command.t
789    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    // Need to update below when variant changes
824    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    // update account state table with all the changes
851    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            // new entry in table
957            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); // just making sure we work on `updated_account`
972
973            // update entry in table
974            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
1008// struct BodyComponentsParams<'a, A, B, C, D> {
1009//     update: Option<Update>,
1010//     account_id: Option<AccountId>,
1011//     token_id: Option<TokenId>,
1012//     caller: Option<CallType>,
1013//     account_ids_seen: Option<HashSet<AccountId>>,
1014//     account_state_tbl: &'a mut HashMap<AccountId, (Account, Role)>,
1015//     vk: Option<WithHash<VerificationKey>>,
1016//     failure: Option<Failure>,
1017//     new_account: Option<bool>,
1018//     zkapp_account: Option<bool>,
1019//     is_fee_payer: Option<bool>,
1020//     available_public_keys: Option<HashSet<HashableCompressedPubKey>>,
1021//     permissions_auth: Option<ControlTag>,
1022//     required_balance_change: Option<A>,
1023//     protocol_state_view: Option<&'a ProtocolStateView>,
1024//     zkapp_account_ids: Vec<AccountId>,
1025//     increment_nonce: (B, bool),
1026//     authorization_tag: ControlTag,
1027//     _phantom: PhantomData<(C, D)>,
1028// }
1029
1030fn 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    // if negative, magnitude constrained to balance in account
1052    // the effective balance is what's in the account state table
1053
1054    let effective_balance = account.balance;
1055    let small_balance_change = {
1056        // make small transfers to allow generating large number of zkapp_command
1057        // without an overflow
1058        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    // check conditions to avoid replays
1082    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    // caller: Option<CallType>,
1104    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    // is_fee_payer: Option<bool>,
1114    // increment_nonce: (B, bool),
1115    // authorization_tag: ControlTag,
1116    // _phantom: PhantomData<(C, D)>,
1117}
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    // permissions_auth is used to generate updated permissions consistent with a
1141    // contemplated authorization;
1142    // allow incrementing the nonce only if we know the authorization will be Signature
1143    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        // gen_balance_change,
1180        |account| gen_balance_change(permissions_auth, account, failure, new_account),
1181        // gen_use_full_commitment,
1182        |account_precondition| {
1183            gen_use_full_commitment(increment_nonce, account_precondition, &authorization)
1184        },
1185        // f_balance_change,
1186        |balance| *balance,
1187        // f_token_id,
1188        |token_id| token_id.clone(),
1189        // f_account_precondition,
1190        |first_use_of_account, account| {
1191            gen_account_precondition_from_account(None, None, first_use_of_account, account)
1192        },
1193        // f_account_update_account_precondition
1194        |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
1207/// Value of `Mina_compile_config.minimum_user_command_fee` when we run `dune runtest src/lib/staged_ledger -f`
1208const 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
1222/// Fee payer balance change is Neg
1223fn fee_to_amt(fee: &Fee) -> Signed<Amount> {
1224    Signed::<Amount>::of_unsigned(Amount::from_u64(fee.as_u64())).negate()
1225}
1226
1227/// takes an account id, if we want to sign this data
1228///
1229/// <https://github.com/MinaProtocol/mina/blob/2ff0292b637684ce0372e7b8e23ec85404dc5091/src/lib/mina_generators/zkapp_command_generators.ml#L1020>
1230fn 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_balance_change
1265        gen_fee,
1266        // gen_use_full_commitment
1267        |_account_precondition| {},
1268        // f_balance_change
1269        fee_to_amt,
1270        // f_token_id
1271        |token_id| {
1272            // make sure the fee payer's token id is the default,
1273            // which is represented by the unit value in the body
1274            assert!(token_id.is_default());
1275            // return unit
1276        },
1277        // f_account_precondition,
1278        |_, account| account_precondition_gen(account),
1279        // f_account_update_account_precondition,
1280        |nonce| AccountPreconditions::with_nonce(*nonce),
1281    );
1282
1283    body_components.to_fee_payer()
1284}
1285
1286/// <https://github.com/MinaProtocol/mina/blob/2ff0292b637684ce0372e7b8e23ec85404dc5091/src/lib/mina_generators/zkapp_command_generators.ml#L1046>
1287fn 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    // real signature to be added when this data inserted into a Zkapp_command.t
1307    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
1328/// `gen_zkapp_command_from` generates a zkapp_command and record the change of accounts accordingly
1329/// in `account_state_tbl`. Note that `account_state_tbl` is optional. If it's not provided
1330/// then it would be computed from the ledger. If you plan to generate several zkapp_command,
1331/// then please manually pass `account_state_tbl` to `gen_zkapp_command_from` function.
1332/// If you are generating several zkapp_command, it's better to pre-compute the
1333/// `account_state_tbl` before you call this function. This way you can manually set the
1334/// role of fee payer accounts to be `Fee_payer` in `account_state_tbl` which would prevent
1335/// those accounts being used as ordinary participants in other zkapp_command.
1336///
1337/// Generated zkapp_command uses dummy signatures and dummy proofs.
1338pub 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    // table of public keys to accounts, updated when generating each account_update
1363    // a Map would be more principled, but threading that map through the code
1364    // adds complexity
1365
1366    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    // make sure all ledger keys are in the keymap
1373    for account in ledger_accounts.into_iter() {
1374        let id = account.id();
1375        let pk = id.public_key.clone();
1376
1377        // Initialize account states
1378        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    // table of public keys not in the ledger, to be used for new zkapp_command
1395    // we have the corresponding private keys, so we can create signatures for those
1396    // new zkapp_command
1397    let ledger_account_list: Vec<AccountId> = ledger
1398        .accounts()
1399        .iter()
1400        .chain(account_state_tbl.keys())
1401        .collect::<HashSet<&AccountId>>() // deduplicate
1402        .into_iter()
1403        .cloned()
1404        .collect(); // TODO: Not sure if it matches ocaml
1405
1406    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    // account ids seen, to generate receipt chain hash precondition only if
1423    // a account_update with a given account id has not been encountered before
1424    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()), // TODO: OCaml uses `()`
1458                    }
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()), // TODO: OCaml uses `()`
1471            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                // choose a random authorization
1482                // first Account_update.t updates the permissions, using the Signature authorization,
1483                //  according the random authorization
1484                // second Account_update.t uses the random authorization
1485
1486                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                // Signature authorization to start
1556                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                    // authorization according to chosen permissions auth
1581                    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    // at least 1 account_update
1666    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        // init
1681        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    // modify the balancing account_update with balance change to yield a zero sum
1696    // balancing account_update is created immediately after the fee payer
1697    // account_update is created. This is because the preconditions generation
1698    // is sensitive to the order of account_update generation.
1699
1700    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            // let mut account_updates = account_updates.into_add_callers_simple();
1807
1808            // TODO: accumulate_hashes_predicated ?
1809            account_updates.accumulate_hashes_predicated();
1810            account_updates
1811        },
1812        memo,
1813    };
1814
1815    // update receipt chain hashes in accounts table
1816    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}