mina_tree/scan_state/transaction_logic/
for_tests.rs1use super::{
2 zkapp_command, Account, AccountId, Amount, Balance, Fee, Memo, Nonce, TokenId,
3 VerificationKeyWire,
4};
5use crate::{
6 gen_keypair,
7 scan_state::{currency::Magnitude, parallel_scan::ceil_log2},
8 sparse_ledger::LedgerIntf,
9 AuthRequired, BaseLedger, Mask, Permissions, VerificationKey, ZkAppAccount,
10 TXN_VERSION_CURRENT,
11};
12use mina_curves::pasta::Fp;
13use mina_signer::{CompressedPubKey, Keypair};
14use rand::Rng;
15use std::collections::{HashMap, HashSet};
16
17const MIN_INIT_BALANCE: u64 = 8000000000;
18const MAX_INIT_BALANCE: u64 = 8000000000000;
19const NUM_ACCOUNTS: u64 = 10;
20const NUM_TRANSACTIONS: u64 = 10;
21const DEPTH: u64 = ceil_log2(NUM_ACCOUNTS + NUM_TRANSACTIONS);
22
23#[derive(Debug, PartialEq, Eq)]
26pub struct HashableKeypair(pub Keypair);
27
28impl std::hash::Hash for HashableKeypair {
29 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
30 let compressed = self.0.public.into_compressed();
31 HashableCompressedPubKey(compressed).hash(state);
32 }
33}
34
35#[derive(Clone, Debug, Eq, derive_more::From)]
38pub struct HashableCompressedPubKey(pub CompressedPubKey);
39
40impl PartialEq for HashableCompressedPubKey {
41 fn eq(&self, other: &Self) -> bool {
42 self.0 == other.0
43 }
44}
45
46impl std::hash::Hash for HashableCompressedPubKey {
47 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
48 self.0.x.hash(state);
49 self.0.is_odd.hash(state);
50 }
51}
52
53impl PartialOrd for HashableCompressedPubKey {
54 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
55 match self.0.x.partial_cmp(&other.0.x) {
56 Some(core::cmp::Ordering::Equal) => {}
57 ord => return ord,
58 };
59 self.0.is_odd.partial_cmp(&other.0.is_odd)
60 }
61}
62
63#[derive(Debug)]
67pub struct InitLedger(pub Vec<(Keypair, u64)>);
68
69#[derive(Debug)]
73pub struct TransactionSpec {
74 pub fee: Fee,
75 pub sender: (Keypair, Nonce),
76 pub receiver: CompressedPubKey,
77 pub amount: Amount,
78}
79
80#[derive(Debug)]
84pub struct TestSpec {
85 pub init_ledger: InitLedger,
86 pub specs: Vec<TransactionSpec>,
87}
88
89impl InitLedger {
90 pub fn init(&self, zkapp: Option<bool>, ledger: &mut impl LedgerIntf) {
91 let zkapp = zkapp.unwrap_or(true);
92
93 self.0.iter().for_each(|(kp, amount)| {
94 let (_tag, mut account, loc) = ledger
95 .get_or_create(&AccountId::new(
96 kp.public.into_compressed(),
97 TokenId::default(),
98 ))
99 .unwrap();
100
101 use AuthRequired::Either;
102 let permissions = Permissions {
103 edit_state: Either,
104 access: AuthRequired::None,
105 send: Either,
106 receive: AuthRequired::None,
107 set_delegate: Either,
108 set_permissions: Either,
109 set_verification_key: crate::SetVerificationKey {
110 auth: Either,
111 txn_version: TXN_VERSION_CURRENT,
112 },
113 set_zkapp_uri: Either,
114 edit_action_state: Either,
115 set_token_symbol: Either,
116 increment_nonce: Either,
117 set_voting_for: Either,
118 set_timing: Either,
119 };
120
121 let zkapp = if zkapp {
122 let zkapp = ZkAppAccount {
123 verification_key: Some(VerificationKeyWire::new(
124 crate::dummy::trivial_verification_key(),
125 )),
126 ..Default::default()
127 };
128
129 Some(zkapp.into())
130 } else {
131 None
132 };
133
134 account.balance = Balance::from_u64(*amount);
135 account.permissions = permissions;
136 account.zkapp = zkapp;
137
138 ledger.set(&loc, account);
139 });
140 }
141
142 pub fn gen() -> Self {
143 let mut rng = rand::thread_rng();
144
145 let mut tbl = HashSet::with_capacity(256);
146
147 let init = (0..NUM_ACCOUNTS)
148 .map(|_| {
149 let kp = loop {
150 let keypair = gen_keypair();
151 let compressed = keypair.public.into_compressed();
152 if !tbl.contains(&HashableCompressedPubKey(compressed)) {
153 break keypair;
154 }
155 };
156
157 let amount = rng.gen_range(MIN_INIT_BALANCE..MAX_INIT_BALANCE);
158 tbl.insert(HashableCompressedPubKey(kp.public.into_compressed()));
159 (kp, amount)
160 })
161 .collect();
162
163 Self(init)
164 }
165}
166
167impl TransactionSpec {
168 pub fn gen(init_ledger: &InitLedger, nonces: &mut HashMap<HashableKeypair, Nonce>) -> Self {
169 let mut rng = rand::thread_rng();
170
171 let pk = |(kp, _): (Keypair, u64)| kp.public.into_compressed();
172
173 let receiver_is_new: bool = rng.gen();
174
175 let mut gen_index = || rng.gen_range(0..init_ledger.0.len().checked_sub(1).unwrap());
176
177 let receiver_index = if receiver_is_new {
178 None
179 } else {
180 Some(gen_index())
181 };
182
183 let receiver = match receiver_index {
184 None => gen_keypair().public.into_compressed(),
185 Some(i) => pk(init_ledger.0[i].clone()),
186 };
187
188 let sender = {
189 let i = match receiver_index {
190 None => gen_index(),
191 Some(j) => loop {
192 let i = gen_index();
193 if i != j {
194 break i;
195 }
196 },
197 };
198 init_ledger.0[i].0.clone()
199 };
200
201 let nonce = nonces
202 .get(&HashableKeypair(sender.clone()))
203 .cloned()
204 .unwrap();
205
206 let amount = Amount::from_u64(rng.gen_range(1_000_000..100_000_000));
207 let fee = Fee::from_u64(rng.gen_range(1_000_000..100_000_000));
208
209 let old = nonces.get_mut(&HashableKeypair(sender.clone())).unwrap();
210 *old = old.incr();
211
212 Self {
213 fee,
214 sender: (sender, nonce),
215 receiver,
216 amount,
217 }
218 }
219}
220
221impl TestSpec {
222 fn mk_gen(num_transactions: Option<u64>) -> TestSpec {
223 let num_transactions = num_transactions.unwrap_or(NUM_TRANSACTIONS);
224
225 let init_ledger = InitLedger::gen();
226
227 let mut map = init_ledger
228 .0
229 .iter()
230 .map(|(kp, _)| (HashableKeypair(kp.clone()), Nonce::zero()))
231 .collect();
232
233 let specs = (0..num_transactions)
234 .map(|_| TransactionSpec::gen(&init_ledger, &mut map))
235 .collect();
236
237 Self { init_ledger, specs }
238 }
239
240 pub fn gen() -> Self {
241 Self::mk_gen(Some(NUM_TRANSACTIONS))
242 }
243}
244
245#[derive(Debug)]
246pub struct UpdateStatesSpec {
247 pub fee: Fee,
248 pub sender: (Keypair, Nonce),
249 pub fee_payer: Option<(Keypair, Nonce)>,
250 pub receivers: Vec<(CompressedPubKey, Amount)>,
251 pub amount: Amount,
252 pub zkapp_account_keypairs: Vec<Keypair>,
253 pub memo: Memo,
254 pub new_zkapp_account: bool,
255 pub snapp_update: zkapp_command::Update,
256 pub current_auth: AuthRequired,
258 pub actions: Vec<Vec<Fp>>,
259 pub events: Vec<Vec<Fp>>,
260 pub call_data: Fp,
261 pub preconditions: Option<zkapp_command::Preconditions>,
262}
263
264pub fn trivial_zkapp_account(
265 permissions: Option<Permissions<AuthRequired>>,
266 vk: VerificationKey,
267 pk: CompressedPubKey,
268) -> Account {
269 let id = AccountId::new(pk, TokenId::default());
270 let mut account = Account::create_with(id, Balance::from_u64(1_000_000_000_000_000));
271 account.permissions = permissions.unwrap_or_else(Permissions::user_default);
272 account.zkapp = Some(
273 ZkAppAccount {
274 verification_key: Some(VerificationKeyWire::new(vk)),
275 ..Default::default()
276 }
277 .into(),
278 );
279 account
280}
281
282pub fn create_trivial_zkapp_account(
283 permissions: Option<Permissions<AuthRequired>>,
284 vk: VerificationKey,
285 ledger: &mut Mask,
286 pk: CompressedPubKey,
287) {
288 let id = AccountId::new(pk.clone(), TokenId::default());
289 let account = trivial_zkapp_account(permissions, vk, pk);
290 assert!(BaseLedger::location_of_account(ledger, &id).is_none());
291 ledger.get_or_create_account(id, account).unwrap();
292}