Skip to content

Commit 67bb538

Browse files
committed
Ledger/tx-logic: move for_tests into a submodule
1 parent 53afdd1 commit 67bb538

File tree

2 files changed

+287
-288
lines changed

2 files changed

+287
-288
lines changed
Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
use mina_signer::Keypair;
2+
use rand::Rng;
3+
4+
use crate::{
5+
gen_keypair, scan_state::parallel_scan::ceil_log2, AuthRequired, Mask, Permissions,
6+
VerificationKey, ZkAppAccount, TXN_VERSION_CURRENT,
7+
};
8+
9+
use super::*;
10+
11+
const MIN_INIT_BALANCE: u64 = 8000000000;
12+
const MAX_INIT_BALANCE: u64 = 8000000000000;
13+
const NUM_ACCOUNTS: u64 = 10;
14+
const NUM_TRANSACTIONS: u64 = 10;
15+
const DEPTH: u64 = ceil_log2(NUM_ACCOUNTS + NUM_TRANSACTIONS);
16+
17+
/// Use this for tests only
18+
/// Hashmaps are not deterministic
19+
#[derive(Debug, PartialEq, Eq)]
20+
pub struct HashableKeypair(pub Keypair);
21+
22+
impl std::hash::Hash for HashableKeypair {
23+
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
24+
let compressed = self.0.public.into_compressed();
25+
HashableCompressedPubKey(compressed).hash(state);
26+
}
27+
}
28+
29+
/// Use this for tests only
30+
/// Hashmaps are not deterministic
31+
#[derive(Clone, Debug, Eq, derive_more::From)]
32+
pub struct HashableCompressedPubKey(pub CompressedPubKey);
33+
34+
impl PartialEq for HashableCompressedPubKey {
35+
fn eq(&self, other: &Self) -> bool {
36+
self.0 == other.0
37+
}
38+
}
39+
40+
impl std::hash::Hash for HashableCompressedPubKey {
41+
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
42+
self.0.x.hash(state);
43+
self.0.is_odd.hash(state);
44+
}
45+
}
46+
47+
impl PartialOrd for HashableCompressedPubKey {
48+
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
49+
match self.0.x.partial_cmp(&other.0.x) {
50+
Some(core::cmp::Ordering::Equal) => {}
51+
ord => return ord,
52+
};
53+
self.0.is_odd.partial_cmp(&other.0.is_odd)
54+
}
55+
}
56+
57+
/// OCaml reference: src/lib/transaction_logic/mina_transaction_logic.ml L:2285-2285
58+
/// Commit: 5da42ccd72e791f164d4d200cf1ce300262873b3
59+
/// Last verified: 2025-10-10
60+
#[derive(Debug)]
61+
pub struct InitLedger(pub Vec<(Keypair, u64)>);
62+
63+
/// OCaml reference: src/lib/transaction_logic/mina_transaction_logic.ml L:2351-2356
64+
/// Commit: 5da42ccd72e791f164d4d200cf1ce300262873b3
65+
/// Last verified: 2025-10-10
66+
#[derive(Debug)]
67+
pub struct TransactionSpec {
68+
pub fee: Fee,
69+
pub sender: (Keypair, Nonce),
70+
pub receiver: CompressedPubKey,
71+
pub amount: Amount,
72+
}
73+
74+
/// OCaml reference: src/lib/transaction_logic/mina_transaction_logic.ml L:2407
75+
/// Commit: 5da42ccd72e791f164d4d200cf1ce300262873b3
76+
/// Last verified: 2025-10-10
77+
#[derive(Debug)]
78+
pub struct TestSpec {
79+
pub init_ledger: InitLedger,
80+
pub specs: Vec<TransactionSpec>,
81+
}
82+
83+
impl 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+
161+
impl 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+
215+
impl 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)]
240+
pub 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+
// Authorization for the update being performed
251+
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+
258+
pub 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+
276+
pub 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+
}

0 commit comments

Comments
 (0)