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)]
65pub struct InitLedger(pub Vec<(Keypair, u64)>);
66
67#[derive(Debug)]
69pub struct TransactionSpec {
70 pub fee: Fee,
71 pub sender: (Keypair, Nonce),
72 pub receiver: CompressedPubKey,
73 pub amount: Amount,
74}
75
76#[derive(Debug)]
78pub struct TestSpec {
79 pub init_ledger: InitLedger,
80 pub specs: Vec<TransactionSpec>,
81}
82
83impl InitLedger {
84 pub fn init(&self, zkapp: Option<bool>, ledger: &mut impl LedgerIntf) {
85 let zkapp = zkapp.unwrap_or(true);
86
87 self.0.iter().for_each(|(kp, amount)| {
88 let (_tag, mut account, loc) = ledger
89 .get_or_create(&AccountId::new(
90 kp.public.into_compressed(),
91 TokenId::default(),
92 ))
93 .unwrap();
94
95 use AuthRequired::Either;
96 let permissions = Permissions {
97 edit_state: Either,
98 access: AuthRequired::None,
99 send: Either,
100 receive: AuthRequired::None,
101 set_delegate: Either,
102 set_permissions: Either,
103 set_verification_key: crate::SetVerificationKey {
104 auth: Either,
105 txn_version: TXN_VERSION_CURRENT,
106 },
107 set_zkapp_uri: Either,
108 edit_action_state: Either,
109 set_token_symbol: Either,
110 increment_nonce: Either,
111 set_voting_for: Either,
112 set_timing: Either,
113 };
114
115 let zkapp = if zkapp {
116 let zkapp = ZkAppAccount {
117 verification_key: Some(VerificationKeyWire::new(
118 crate::dummy::trivial_verification_key(),
119 )),
120 ..Default::default()
121 };
122
123 Some(zkapp.into())
124 } else {
125 None
126 };
127
128 account.balance = Balance::from_u64(*amount);
129 account.permissions = permissions;
130 account.zkapp = zkapp;
131
132 ledger.set(&loc, account);
133 });
134 }
135
136 pub fn gen() -> Self {
137 let mut rng = rand::thread_rng();
138
139 let mut tbl = HashSet::with_capacity(256);
140
141 let init = (0..NUM_ACCOUNTS)
142 .map(|_| {
143 let kp = loop {
144 let keypair = gen_keypair();
145 let compressed = keypair.public.into_compressed();
146 if !tbl.contains(&HashableCompressedPubKey(compressed)) {
147 break keypair;
148 }
149 };
150
151 let amount = rng.gen_range(MIN_INIT_BALANCE..MAX_INIT_BALANCE);
152 tbl.insert(HashableCompressedPubKey(kp.public.into_compressed()));
153 (kp, amount)
154 })
155 .collect();
156
157 Self(init)
158 }
159}
160
161impl TransactionSpec {
162 pub fn gen(init_ledger: &InitLedger, nonces: &mut HashMap<HashableKeypair, Nonce>) -> Self {
163 let mut rng = rand::thread_rng();
164
165 let pk = |(kp, _): (Keypair, u64)| kp.public.into_compressed();
166
167 let receiver_is_new: bool = rng.gen();
168
169 let mut gen_index = || rng.gen_range(0..init_ledger.0.len().checked_sub(1).unwrap());
170
171 let receiver_index = if receiver_is_new {
172 None
173 } else {
174 Some(gen_index())
175 };
176
177 let receiver = match receiver_index {
178 None => gen_keypair().public.into_compressed(),
179 Some(i) => pk(init_ledger.0[i].clone()),
180 };
181
182 let sender = {
183 let i = match receiver_index {
184 None => gen_index(),
185 Some(j) => loop {
186 let i = gen_index();
187 if i != j {
188 break i;
189 }
190 },
191 };
192 init_ledger.0[i].0.clone()
193 };
194
195 let nonce = nonces
196 .get(&HashableKeypair(sender.clone()))
197 .cloned()
198 .unwrap();
199
200 let amount = Amount::from_u64(rng.gen_range(1_000_000..100_000_000));
201 let fee = Fee::from_u64(rng.gen_range(1_000_000..100_000_000));
202
203 let old = nonces.get_mut(&HashableKeypair(sender.clone())).unwrap();
204 *old = old.incr();
205
206 Self {
207 fee,
208 sender: (sender, nonce),
209 receiver,
210 amount,
211 }
212 }
213}
214
215impl TestSpec {
216 fn mk_gen(num_transactions: Option<u64>) -> TestSpec {
217 let num_transactions = num_transactions.unwrap_or(NUM_TRANSACTIONS);
218
219 let init_ledger = InitLedger::gen();
220
221 let mut map = init_ledger
222 .0
223 .iter()
224 .map(|(kp, _)| (HashableKeypair(kp.clone()), Nonce::zero()))
225 .collect();
226
227 let specs = (0..num_transactions)
228 .map(|_| TransactionSpec::gen(&init_ledger, &mut map))
229 .collect();
230
231 Self { init_ledger, specs }
232 }
233
234 pub fn gen() -> Self {
235 Self::mk_gen(Some(NUM_TRANSACTIONS))
236 }
237}
238
239#[derive(Debug)]
240pub struct UpdateStatesSpec {
241 pub fee: Fee,
242 pub sender: (Keypair, Nonce),
243 pub fee_payer: Option<(Keypair, Nonce)>,
244 pub receivers: Vec<(CompressedPubKey, Amount)>,
245 pub amount: Amount,
246 pub zkapp_account_keypairs: Vec<Keypair>,
247 pub memo: Memo,
248 pub new_zkapp_account: bool,
249 pub snapp_update: zkapp_command::Update,
250 pub current_auth: AuthRequired,
252 pub actions: Vec<Vec<Fp>>,
253 pub events: Vec<Vec<Fp>>,
254 pub call_data: Fp,
255 pub preconditions: Option<zkapp_command::Preconditions>,
256}
257
258pub fn trivial_zkapp_account(
259 permissions: Option<Permissions<AuthRequired>>,
260 vk: VerificationKey,
261 pk: CompressedPubKey,
262) -> Account {
263 let id = AccountId::new(pk, TokenId::default());
264 let mut account = Account::create_with(id, Balance::from_u64(1_000_000_000_000_000));
265 account.permissions = permissions.unwrap_or_else(Permissions::user_default);
266 account.zkapp = Some(
267 ZkAppAccount {
268 verification_key: Some(VerificationKeyWire::new(vk)),
269 ..Default::default()
270 }
271 .into(),
272 );
273 account
274}
275
276pub fn create_trivial_zkapp_account(
277 permissions: Option<Permissions<AuthRequired>>,
278 vk: VerificationKey,
279 ledger: &mut Mask,
280 pk: CompressedPubKey,
281) {
282 let id = AccountId::new(pk.clone(), TokenId::default());
283 let account = trivial_zkapp_account(permissions, vk, pk);
284 assert!(BaseLedger::location_of_account(ledger, &id).is_none());
285 ledger.get_or_create_account(id, account).unwrap();
286}