1use std::{collections::HashMap, rc::Rc};
2
3use mina_signer::Keypair;
4use rand::Rng;
5
6use crate::{
7 gen_keypair,
8 scan_state::{
9 currency::{Balance, Magnitude},
10 transaction_logic::{
11 for_tests::HashableCompressedPubKey,
12 valid,
13 zkapp_command::{self, verifiable},
14 },
15 },
16 util, Account, AccountId, AuthRequired, BaseLedger, Mask, MyCowMut, Permissions, TokenId,
17 VerificationKey, VerificationKeyWire, ZkAppAccount, TXN_VERSION_CURRENT,
18};
19
20use super::{
21 zkapp_command::GenZkappCommandParams, Failure, Role, LEDGER_DEPTH, MAX_ACCOUNT_UPDATES,
22 MAX_TOKEN_UPDATES, MINIMUM_USER_COMMAND_FEE,
23};
24
25fn zkapp_command_with_ledger(
26 num_keypairs: Option<usize>,
27 max_account_updates: Option<usize>,
28 max_token_updates: Option<usize>,
29 account_state_tbl: Option<&mut HashMap<AccountId, (Account, Role)>>,
30 vk: Option<VerificationKeyWire>,
31 failure: Option<&Failure>,
32) -> (
33 valid::UserCommand,
34 Keypair,
35 HashMap<HashableCompressedPubKey, Keypair>,
36 Mask,
37) {
38 let mut rng = rand::thread_rng();
39
40 let max_account_updates = max_account_updates.unwrap_or(MAX_ACCOUNT_UPDATES);
47 let max_token_updates = max_token_updates.unwrap_or(MAX_TOKEN_UPDATES);
48 let num_keypairs =
49 num_keypairs.unwrap_or((max_account_updates * 2) + (max_token_updates * 3) + 2);
50
51 let keypairs: Vec<Keypair> = (0..num_keypairs).map(|_| gen_keypair()).collect();
52
53 let keymap: HashMap<HashableCompressedPubKey, Keypair> = keypairs
54 .iter()
55 .map(|kp| {
56 let compressed = kp.public.into_compressed();
57 (HashableCompressedPubKey(compressed), kp.clone())
58 })
59 .collect();
60
61 let num_keypairs_in_ledger = num_keypairs / 2;
62 let keypairs_in_ledger = util::take(&keypairs, num_keypairs_in_ledger);
63
64 let account_ids: Vec<AccountId> = keypairs_in_ledger
65 .iter()
66 .map(|Keypair { public, .. }| {
67 AccountId::create(public.into_compressed(), TokenId::default())
68 })
69 .collect();
70
71 let verification_key = vk.clone().unwrap_or_else(|| {
72 let dummy_vk = VerificationKey::dummy();
73 VerificationKeyWire::new((*dummy_vk).clone())
74 });
75
76 let balances: Vec<Balance> = {
77 let min_cmd_fee = MINIMUM_USER_COMMAND_FEE;
78
79 let min_balance = {
80 let balance = min_cmd_fee.as_u64() + 100_000_000_000_000_000;
81 Balance::from_u64(balance)
82 };
83
84 let max_balance = {
86 let max_bal = Balance::of_mina_string_exn("2000000000.0");
87
88 assert_eq!(max_bal.as_u64(), 2000000000000000000);
89
90 min_balance
91 .checked_add(&max_bal)
92 .expect("zkapp_command_with_ledger: overflow for max_balance")
93 };
94
95 (0..num_keypairs_in_ledger)
96 .map(move |_| {
97 let balance = rng.gen_range(min_balance.as_u64()..max_balance.as_u64());
98 Balance::from_u64(balance)
99 })
100 .collect()
101 };
102
103 let account_ids_and_balances: Vec<(AccountId, Balance)> =
104 account_ids.iter().cloned().zip(balances).collect();
105
106 let snappify_account = |mut account: Account| {
107 let permissions = Permissions {
108 edit_state: AuthRequired::Either,
109 send: AuthRequired::Either,
110 set_delegate: AuthRequired::Either,
111 set_permissions: AuthRequired::Either,
112 set_verification_key: crate::SetVerificationKey {
113 auth: AuthRequired::Either,
114 txn_version: TXN_VERSION_CURRENT,
115 },
116 set_zkapp_uri: AuthRequired::Either,
117 edit_action_state: AuthRequired::Either,
118 set_token_symbol: AuthRequired::Either,
119 increment_nonce: AuthRequired::Either,
120 set_voting_for: AuthRequired::Either,
121 set_timing: AuthRequired::Either,
122 ..Permissions::user_default()
124 };
125
126 let verification_key = Some(verification_key.clone());
127 let zkapp = Some(
128 ZkAppAccount {
129 verification_key,
130 ..ZkAppAccount::default()
131 }
132 .into(),
133 );
134
135 account.zkapp = zkapp;
136 account.permissions = permissions;
137
138 account
139 };
140
141 let accounts =
143 account_ids_and_balances
144 .iter()
145 .enumerate()
146 .map(|(ndx, (account_id, balance))| {
147 let account = Account::create_with(account_id.clone(), *balance);
148 if ndx % 2 == 0 {
149 account
150 } else {
151 snappify_account(account)
152 }
153 });
154
155 let fee_payer_keypair = keypairs.first().unwrap();
156
157 let mut ledger = Mask::create(LEDGER_DEPTH);
158
159 account_ids.iter().zip(accounts).for_each(|(id, account)| {
160 let res = ledger
161 .get_or_create_account(id.clone(), account)
162 .expect("zkapp_command: error adding account for account id");
163 assert!(
164 matches!(res, crate::GetOrCreated::Added(_)),
165 "zkapp_command: account for account id already exists"
166 );
167 });
168
169 let mut account_state_tbl = match account_state_tbl {
171 Some(account_state_tbl) => MyCowMut::Borrow(account_state_tbl),
172 None => MyCowMut::Own(HashMap::new()),
173 };
174 let account_state_tbl = Some(&mut *account_state_tbl);
175
176 let zkapp_command =
177 super::zkapp_command::gen_zkapp_command_from(super::zkapp_command::GenZkappCommandParams {
178 failure,
179 max_account_updates: Some(max_account_updates),
180 max_token_updates: Some(max_token_updates),
181 fee_payer_keypair,
182 keymap: &keymap,
183 account_state_tbl,
184 ledger: ledger.clone(),
185 protocol_state_view: None,
186 vk: vk.as_ref(),
187 global_slot: None,
188 });
189
190 use crate::scan_state::transaction_logic::TransactionStatus::Applied;
191
192 let zkapp_command =
193 zkapp_command::valid::to_valid(zkapp_command, &Applied, |hash, account_id| {
194 verifiable::find_vk_via_ledger(ledger.clone(), hash, account_id)
195 })
196 .unwrap();
197 let user_command = valid::UserCommand::ZkAppCommand(Box::new(zkapp_command));
198
199 (user_command, fee_payer_keypair.clone(), keymap, ledger)
201}
202
203pub fn sequence_zkapp_command_with_ledger(
205 max_account_updates: Option<usize>,
206 max_token_updates: Option<usize>,
207 length: Option<usize>,
208 vk: Option<VerificationKeyWire>,
209 failure: Option<&Failure>,
210) -> (
211 Vec<(
212 valid::UserCommand,
213 Rc<Keypair>,
214 Rc<HashMap<HashableCompressedPubKey, Keypair>>,
215 )>,
216 Mask,
217) {
218 let mut rng = rand::thread_rng();
219
220 let length = length.unwrap_or_else(|| rng.gen::<usize>() % 100);
221 let max_account_updates = max_account_updates.unwrap_or(MAX_ACCOUNT_UPDATES);
222 let max_token_updates = max_token_updates.unwrap_or(MAX_TOKEN_UPDATES);
223
224 let num_keypairs = length * max_account_updates * 2;
225
226 let mut account_state_tbl = HashMap::<AccountId, (Account, Role)>::with_capacity(64);
228
229 let num_keypairs = Some(num_keypairs);
230 let max_account_updates = Some(max_account_updates);
231 let max_token_updates = Some(max_token_updates);
232 let (zkapp_command, fee_payer_keypair, keymap, ledger) = zkapp_command_with_ledger(
235 num_keypairs,
236 max_account_updates,
237 max_token_updates,
238 Some(&mut account_state_tbl),
239 vk.clone(),
240 failure,
241 );
242
243 let fee_payer_keypair = Rc::new(fee_payer_keypair);
244 let keymap = Rc::new(keymap);
245
246 let mut commands = Vec::with_capacity(length);
247
248 commands.push((
249 zkapp_command,
250 Rc::clone(&fee_payer_keypair),
251 Rc::clone(&keymap),
252 ));
253
254 (0..length.saturating_sub(1)).for_each(|_| {
255 let zkapp_command = super::zkapp_command::gen_zkapp_command_from(GenZkappCommandParams {
256 failure,
257 max_account_updates,
258 max_token_updates,
259 fee_payer_keypair: &fee_payer_keypair,
260 keymap: &keymap,
261 account_state_tbl: Some(&mut account_state_tbl),
262 ledger: ledger.clone(),
263 protocol_state_view: None,
264 vk: vk.as_ref(),
265 global_slot: None,
266 });
267
268 use crate::scan_state::transaction_logic::TransactionStatus::Applied;
269 let zkapp_command =
270 zkapp_command::valid::to_valid(zkapp_command, &Applied, |hash, account_id| {
271 verifiable::find_vk_via_ledger(ledger.clone(), hash, account_id)
272 })
273 .unwrap();
274 let zkapp_command = valid::UserCommand::ZkAppCommand(Box::new(zkapp_command));
275
276 commands.push((
277 zkapp_command,
278 Rc::clone(&fee_payer_keypair),
279 Rc::clone(&keymap),
280 ));
281 });
282
283 (commands, ledger)
284}